Frage

Ich versuche, eine MySQL-basierte Anwendung auf dem Microsoft SQL Server 2005 zu migrieren (nicht durch Wahl, aber das ist das Leben).

In der ursprünglichen App verwenden wir fast vollständig ANSI-SQL-kompatible Aussagen, mit einer bedeutenden Ausnahme -. Wir verwenden MySQL group_concat Funktion ziemlich häufig

group_concat, nebenbei bemerkt, tut dies: Da eine Tabelle von, sagen wir, Mitarbeiternamen und Projekte ...

SELECT empName, projID FROM project_members;

Rückgabe:

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

... und hier ist das, was Sie mit Group_concat bekommen:

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

Rückgabe:

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

Also, was ich möchte wissen, ist: Ist es möglich, zu schreiben, sagen wir, eine benutzerdefinierte Funktion in SQL Server, die die Funktionalität von group_concat emuliert?

Ich habe fast keine Erfahrung UDF, gespeicherte Prozeduren oder ähnliches verwendet wird, nur straight-up SQL, so wenden Sie sich bitte auf der Seite zu viel Erklärung irren:)

War es hilfreich?

Lösung

Nein REAL einfache Möglichkeit, dies zu tun. Viele Ideen da draußen, aber.

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, wenn die Daten enthalten Zeichen wie <

funktioniert
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) 

Andere Tipps

Ich kann ein bisschen spät, um die Partei, aber diese Methode funktioniert für mich und ist einfacher als die COALESCE-Methode.

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

Möglicherweise zu spät von Nutzen zu sein, aber das ist 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

SQL Server 2017 einführt eine neue Aggregatfunktion

STRING_AGG ( expression, separator) .

  

Verkettet die Werte von String-Ausdrücke und Orte Separator   Werte zwischen ihnen. Der Separator ist nicht am Ende der Zeichenfolge hinzugefügt.

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

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

Dies kann jedoch unter bestimmten Umständen fehlschlagen. z.B. wenn die Daten verknüpft werden soll, enthält CHAR(29) Sie sehen

  

FOR XML könnten die Daten nicht serialisiert ... weil es   enthält ein Zeichen (0x001D), die nicht in XML erlaubt ist.

Eine robustere Methode, die mit allen Charakteren umgehen können wäre ein CLR Aggregat zu verwenden. eine Ordnung der verketteten Elemente jedoch Anwendung ist schwieriger, mit diesem Ansatz.

Das Verfahren einer Variablen zuzuweisen ist nicht garantiert und im Produktionscode vermieden werden sollte.

Haben Sie einen Blick auf die GROUP_CONCAT Projekt Github, ich glaube, ich tue genau das, was Sie suchen:

  

Dieses Projekt enthält eine Reihe von SQLCLR Benutzerdefinierte Aggregatfunktionen (SQLCLR UDAs), die zusammen eine ähnliche Funktionalität wie der MySQL GROUP_CONCAT Funktion bieten. Es gibt mehrere Funktionen, um die beste Leistung zu gewährleisten basiert auf der Funktionalität erforderlich ...

Um alle Projektmanager Namen von Projekten verketten, die mehrere Projektmanager haben schreiben:

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

Mit dem folgenden Code, den Sie haben PermissionLevel = External auf Ihrer Projekteigenschaft festlegen, bevor Sie bereitstellen und die Datenbank ändern externen Code zu vertrauen (sicher sein, an anderer Stelle über Sicherheitsrisiken und Alternativen zu lesen [wie Zertifikate]) durch „ALTER laufen DATABASE database_name SET TRUSTWORTHY ON“.

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 Ausbeuten: A, B, C, D

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

declare @result varchar(8000);

set @result = '';

select @result = @result + name + ' '

from master.dbo.systypes;

select rtrim(@result);

@ Mark wie Sie bereits erwähnt, es war der Raum Charakter, der für mich verursacht Probleme.

Über J Hardiman Antwort, wie etwa:

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

By the way, ist die Verwendung von „Name“ einen Tippfehler oder bin ich kein Konzept hier zu verstehen?

Wie auch immer, vielen Dank Jungs Cuz es hat mich gerettet einige Zeit:)

Für meine Kolleginnen und Googler da draußen, hier ist eine sehr einfache Plug-and-Play-Lösung, die für mich gearbeitet, nachdem eine Zeit lang mit den komplexeren Lösungen zu kämpfen:

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 eine VARCHAR, um zu konvertieren hatte es als String zu verketten. Wenn Sie das nicht tun müssen, ist 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

Alle Verdienstes hierfür liegt hier: https://social.msdn.microsoft.com/Forums/sqlserver/en-US/9508abc2-46e7-4186-b57f-7f368374e084/replicating-groupconcat-function-of- mysql-in-sQL-Server? forum = TRANSACTSQL

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top