it-swarm.com.de

Group_concat MySQL-Funktion in Microsoft SQL Server 2005 simulieren?

Ich versuche, eine MySQL-basierte App auf Microsoft SQL Server 2005 zu migrieren (nicht nach Wahl, aber so ist das Leben).

In der ursprünglichen App haben wir fast vollständig ANSI-SQL-konforme Anweisungen verwendet, mit einer bedeutenden Ausnahme - wir haben ziemlich häufig die Funktion group_concat Von MySQL verwendet.

group_concat Tut dies übrigens: Zum Beispiel eine Tabelle mit Mitarbeiternamen und Projekten ...

SELECT empName, projID FROM project_members;

kehrt zurück:

ANDY   |  A100
ANDY   |  B391
ANDY   |  X010
TOM    |  A100
TOM    |  A510

... und das bekommen Sie mit group_concat:

SELECT 
    empName, group_concat(projID SEPARATOR ' / ') 
FROM 
    project_members 
GROUP BY 
    empName;

kehrt zurück:

ANDY   |  A100 / B391 / X010
TOM    |  A100 / A510

Ich würde also gerne wissen, ob es möglich ist, beispielsweise eine benutzerdefinierte Funktion in SQL Server zu schreiben, die die Funktionalität von group_concat Emuliert.

Ich habe fast keine Erfahrung mit UDFs, gespeicherten Prozeduren oder Ähnlichem, nur mit direktem SQL-Zugriff.

338
DanM

Keine WIRKLICH einfache Möglichkeit, dies zu tun. Es gibt jedoch viele Ideen.

Beste, die ich gefunden habe :

SELECT table_name, LEFT(column_names , LEN(column_names )-1) AS column_names
FROM information_schema.columns AS extern
CROSS APPLY
(
    SELECT column_name + ','
    FROM information_schema.columns AS intern
    WHERE extern.table_name = intern.table_name
    FOR XML PATH('')
) pre_trimmed (column_names)
GROUP BY table_name, column_names;

Oder eine Version, die korrekt funktioniert, wenn die Daten Zeichen wie < Enthalten.

WITH extern
     AS (SELECT DISTINCT table_name
         FROM   INFORMATION_SCHEMA.COLUMNS)
SELECT table_name,
       LEFT(y.column_names, LEN(y.column_names) - 1) AS column_names
FROM   extern
       CROSS APPLY (SELECT column_name + ','
                    FROM   INFORMATION_SCHEMA.COLUMNS AS intern
                    WHERE  extern.table_name = intern.table_name
                    FOR XML PATH(''), TYPE) x (column_names)
       CROSS APPLY (SELECT x.column_names.value('.', 'NVARCHAR(MAX)')) y(column_names) 
167
BradC

Ich komme vielleicht etwas spät zur Party, aber diese Methode funktioniert bei mir und ist einfacher als die COALESCE-Methode.

SELECT STUFF(
             (SELECT ',' + Column_Name 
              FROM Table_Name
              FOR XML PATH (''))
             , 1, 1, '')
166
Scott

Möglicherweise zu spät, um jetzt von Nutzen zu sein, aber ist dies nicht der einfachste Weg, Dinge zu tun?

SELECT     empName, projIDs = replace
                          ((SELECT Surname AS [data()]
                              FROM project_members
                              WHERE  empName = a.empName
                              ORDER BY empName FOR xml path('')), ' ', REQUIRED SEPERATOR)
FROM         project_members a
WHERE     empName IS NOT NULL
GROUP BY empName
48
J Hardiman

SQL Server 2017 führt eine neue Aggregatfunktion ein

STRING_AGG ( expression, separator) .

Verkettet die Werte von Zeichenfolgenausdrücken und fügt Trennzeichen dazwischen ein. Das Trennzeichen wird nicht am Ende der Zeichenfolge hinzugefügt.

Die verketteten Elemente können durch Anhängen von WITHIN GROUP (ORDER BY some_expression) sortiert werden.

Für die Versionen 2005-2016 verwende ich normalerweise die XML-Methode in der akzeptierten Antwort.

Dies kann jedoch unter Umständen fehlschlagen. z.B. Wenn die zu verkettenden Daten CHAR(29) enthalten, wird angezeigt

FOR XML konnte die Daten nicht serialisieren ... da sie ein Zeichen (0x001D) enthalten, das in XML nicht zulässig ist.

Eine robustere Methode, die mit allen Zeichen umgehen kann, ist die Verwendung eines CLR-Aggregats. Bei diesem Ansatz ist es jedoch schwieriger, eine Reihenfolge auf die verketteten Elemente anzuwenden.

Die Zuweisungsmethode zu einer Variablen lautet nicht garantiert und sollte im Seriencode vermieden werden.

41
Martin Smith

Schauen Sie sich das GROUP_CONCAT Projekt auf Github an, ich glaube, ich mache genau das, wonach Sie suchen:

Dieses Projekt enthält eine Reihe von SQLCLR-benutzerdefinierten Aggregatfunktionen (SQLCLR-UDAs), die zusammen ähnliche Funktionen wie die MySQL GROUP_CONCAT-Funktion bieten. Es gibt mehrere Funktionen, um die bestmögliche Leistung basierend auf den erforderlichen Funktionen zu gewährleisten ...

34
MaxiWheat

So verknüpfen Sie alle Projektmanagernamen aus Projekten, in die mehrere Projektmanager geschrieben haben:

SELECT a.project_id,a.project_name,Stuff((SELECT N'/ ' + first_name + ', '+last_name FROM projects_v 
where a.project_id=project_id
 FOR
 XML PATH(''),TYPE).value('text()[1]','nvarchar(max)'),1,2,N''
) mgr_names
from projects_v a
group by a.project_id,a.project_name
10
Cmaly

Mit dem folgenden Code müssen Sie PermissionLevel = External in Ihren Projekteigenschaften vor der Bereitstellung festlegen und die Datenbank so ändern, dass sie externem Code vertraut (lesen Sie die Informationen zu Sicherheitsrisiken und Alternativen [wie Zertifikaten] an anderer Stelle), indem Sie "ALTER DATABASE database_name SET" ausführen VERTRAUENSWERT AUF ".

using System;
using System.Collections.Generic;
using System.Data.SqlTypes;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using Microsoft.SqlServer.Server;

[Serializable]
[SqlUserDefinedAggregate(Format.UserDefined,
MaxByteSize=8000,
IsInvariantToDuplicates=true,
IsInvariantToNulls=true,
IsInvariantToOrder=true,
IsNullIfEmpty=true)]
    public struct CommaDelimit : IBinarySerialize
{


[Serializable]
 private class StringList : List<string>
 { }

 private StringList List;

 public void Init()
 {
  this.List = new StringList();
 }

 public void Accumulate(SqlString value)
 {
  if (!value.IsNull)
   this.Add(value.Value);
 }

 private void Add(string value)
 {
  if (!this.List.Contains(value))
   this.List.Add(value);
 }

 public void Merge(CommaDelimit group)
 {
  foreach (string s in group.List)
  {
   this.Add(s);
  }
 }

 void IBinarySerialize.Read(BinaryReader reader)
 {
    IFormatter formatter = new BinaryFormatter();
    this.List = (StringList)formatter.Deserialize(reader.BaseStream);
 }

 public SqlString Terminate()
 {
  if (this.List.Count == 0)
   return SqlString.Null;

  const string Separator = ", ";

  this.List.Sort();

  return new SqlString(String.Join(Separator, this.List.ToArray()));
 }

 void IBinarySerialize.Write(BinaryWriter writer)
 {
  IFormatter formatter = new BinaryFormatter();
  formatter.Serialize(writer.BaseStream, this.List);
 }
    }

Ich habe dies mit einer Abfrage getestet, die wie folgt aussieht:

SELECT 
 dbo.CommaDelimit(X.value) [delimited] 
FROM 
 (
  SELECT 'D' [value] 
  UNION ALL SELECT 'B' [value] 
  UNION ALL SELECT 'B' [value] -- intentional duplicate
  UNION ALL SELECT 'A' [value] 
  UNION ALL SELECT 'C' [value] 
 ) X 

Und ergibt: A, B, C, D

9
GregTSmith

Versuchte diese aber für meine Zwecke in MS SQL Server 2005 war die folgende am nützlichsten, die ich bei xaprb fand

declare @result varchar(8000);

set @result = '';

select @result = @result + name + ' '

from master.dbo.systypes;

select rtrim(@result);

@ Mark, wie Sie sagten, war es das Leerzeichen, das mir Probleme bereitete.

9
isoughtajam

Über J Hardimans Antwort, wie wäre es mit:

SELECT empName, projIDs=
  REPLACE(
    REPLACE(
      (SELECT REPLACE(projID, ' ', '-somebody-puts-Microsoft-out-of-his-misery-please-') AS [data()] FROM project_members WHERE empName=a.empName FOR XML PATH('')), 
      ' ', 
      ' / '), 
    '-somebody-puts-Microsoft-out-of-his-misery-please-',
    ' ') 
  FROM project_members a WHERE empName IS NOT NULL GROUP BY empName

Ist die Verwendung von "Nachname" übrigens ein Tippfehler oder verstehe ich hier kein Konzept?

Wie auch immer, vielen Dank Jungs, es hat mir ziemlich viel Zeit gespart :)

6
user422190

Für meine Googler-Kollegen gibt es hier eine sehr einfache Plug-and-Play-Lösung, die für mich funktioniert hat, nachdem ich einige Zeit mit den komplexeren Lösungen zu kämpfen hatte:

SELECT
distinct empName,
NewColumnName=STUFF((SELECT ','+ CONVERT(VARCHAR(10), projID ) 
                     FROM returns 
                     WHERE empName=t.empName FOR XML PATH('')) , 1 , 1 , '' )
FROM 
returns t

Beachten Sie, dass ich die ID in ein VARCHAR konvertieren musste, um sie als Zeichenfolge zu verketten. Wenn Sie das nicht tun müssen, finden Sie hier eine noch einfachere Version:

SELECT
distinct empName,
NewColumnName=STUFF((SELECT ','+ projID
                     FROM returns 
                     WHERE empName=t.empName FOR XML PATH('')) , 1 , 1 , '' )
FROM 
returns t

Das gesamte Guthaben hierfür finden Sie hier: https://social.msdn.Microsoft.com/Forums/sqlserver/en-US/9508abc2-46e7-4186-b57f-7f368374e084/replicating-groupconcat -Funktion-von-MySQL-in-SQL-Server? forum = transactsql

0
krock