Microsoft SQL Server 2005 में group_concat MySQL फ़ंक्शन का अनुकरण करना?


347

मैं Microsoft SQL Server 2005 पर एक MySQL आधारित ऐप को माइग्रेट करने की कोशिश कर रहा हूं (पसंद से नहीं, लेकिन यही जीवन है)।

मूल ऐप में, हमने लगभग पूरी तरह से एएनएसआई-एसक्यूएल आज्ञाकारी बयानों का उपयोग किया, एक महत्वपूर्ण अपवाद के साथ - हमने MySQL के group_concatफ़ंक्शन का अक्सर उपयोग किया ।

group_concat, वैसे, यह करता है: कर्मचारी का नाम और परियोजनाओं, की एक तालिका दी गई है ...

SELECT empName, projID FROM project_members;

रिटर्न:

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

... और यहाँ आपको group_concat के साथ क्या मिलता है:

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

रिटर्न:

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

तो मैं जो जानना चाहता हूं वह है: क्या SQL सर्वर में उपयोगकर्ता-परिभाषित फ़ंक्शन लिखना संभव है, जो कार्यक्षमता का अनुकरण करता है group_concat?

मुझे UDFs, संग्रहीत कार्यविधियाँ या ऐसा कुछ भी उपयोग करने का लगभग कोई अनुभव नहीं है, बस सीधे-सीधे SQL है, इसलिए कृपया बहुत अधिक स्पष्टीकरण के पक्ष में :)



यह एक पुराना प्रश्न है, लेकिन मुझे यहां दिए गए सीएलआर समाधान पसंद हैं
डिएगो

के संभावित डुप्लिकेट कैसे मैं एक अल्पविराम से अलग किसी SQL क्वेरी का उपयोग कर सूची कैसे बनाऊँ? - यह पोस्ट व्यापक है इसलिए मैं इसे एक के रूप में चुनूँगा
TMS


आप कैसे जानते हैं कि किस सूची का निर्माण किया जाना चाहिए, उदाहरण के लिए आप A100 / B391 / X010 दिखाते हैं, लेकिन यह देखते हुए कि किसी रिलेशनल डेटाबेस में कोई अंतर्निहित आदेश नहीं है यह आसानी से X010 / A100 / B391 या किसी अन्य संयोजन के रूप में हो सकता है।
स्टीव फोर्ड

जवाबों:


174

ऐसा करने का कोई आसान तरीका नहीं है। विचारों के बहुत सारे वहाँ, यद्यपि।

मुझे जो सबसे अच्छा मिला है :

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;

या एक संस्करण जो सही ढंग से काम करता है अगर डेटा में वर्ण हो सकते हैं जैसे कि <

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) 

1
इस उदाहरण ने मेरे लिए काम किया, लेकिन मैंने एक और एकत्रीकरण करने की कोशिश की और यह काम नहीं किया, मुझे एक त्रुटि दी: "सहसंबंध नाम 'pre_trimmed' एक FROM खंड में कई बार निर्दिष्ट किया गया है।"
फिलचुआंग

7
'pre_trimmed' उपश्रेणी के लिए केवल एक उपनाम है। उपश्रेणियों के लिए उपनामों की आवश्यकता होती है और उन्हें अद्वितीय होना पड़ता है, इसलिए एक अन्य उपश्रेणी के लिए इसे कुछ अनोखे तरीके से बदलना होगा ...
कोएन

2
क्या आप एक तालिका नाम के बिना एक उदाहरण दिखा सकते हैं जो एक स्तंभ नाम है जो भ्रामक है।
एस.मनस

169

मुझे पार्टी में थोड़ी देर हो सकती है लेकिन यह विधि मेरे लिए काम करती है और COALESCE विधि की तुलना में आसान है।

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

1
यह केवल यह दिखाता है कि मूल्यों को कैसे सम्‍मिलित किया जाए - group_concat समूह द्वारा उन्हें सम्‍मिलित करता है, जो अधिक चुनौतीपूर्ण है (और ओपी को आवश्‍यक प्रतीत होता है)। ऐसा करने के लिए SO 15154644 को स्वीकार किए गए उत्तर को देखें - WHERE क्लॉज महत्वपूर्ण जोड़ है
DJDave

@DJDave इस जवाब का जिक्र कर रहा था । एक समान प्रश्न का स्वीकृत उत्तर भी देखें ।
जॉन कमिंग्स

51

संभवतः बहुत देर हो चुकी है अब लाभ होगा, लेकिन क्या यह चीजों को करने का सबसे आसान तरीका नहीं है?

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

दिलचस्प। मैंने पहले ही परियोजना को हाथ में ले लिया है, लेकिन मैं इस विधि को आजमाऊंगा। धन्यवाद!
DanM

7
अच्छी चाल - केवल समस्या ऐसे स्थान वाले उपनामों की होती है जो अंतरिक्ष को विभाजक से बदल देगा।
मार्क इलियट

मुझे खुद इस तरह की समस्या का सामना करना पड़ा है, मार्क। दुर्भाग्य से, जब तक MSSQL समय के साथ हो जाता है और GROUP_CONCAT का परिचय देता है, यह ओवरहेड-सघन विधियों में से सबसे कम है जो कि मैं यहाँ आने के लिए सक्षम हूं।
जे हार्डमैन

इसके लिए धन्यवाद! : यहाँ एक एसक्यूएल फिडल यह काम कर रहा दिखा रहा है sqlfiddle.com/#!6/c5d56/3
fleed

42

SQL Server 2017 एक नया एग्रीगेट फ़ंक्शन पेश करता है

STRING_AGG ( expression, separator)

स्ट्रिंग के भावों को सम्‍मिलित करता है और उनके बीच विभाजक मान डालता है। स्ट्रिंग के अंत में विभाजक नहीं जोड़ा जाता है।

सम्मोहित तत्वों को जोड़कर आदेश दिया जा सकता है WITHIN GROUP (ORDER BY some_expression)

2005-2016 के संस्करणों के लिए मैं आमतौर पर स्वीकृत उत्तर में XML विधि का उपयोग करता हूं।

यह हालांकि कुछ परिस्थितियों में विफल हो सकता है। उदाहरण के लिए, यदि CHAR(29)आपके द्वारा देखे जाने वाले डेटा में आप देखते हैं

XML के लिए डेटा को अनुक्रमित नहीं किया जा सकता है ... क्योंकि इसमें एक वर्ण (0x001D) होता है जिसे XML में अनुमति नहीं है।

अधिक मजबूत विधि जो सभी वर्णों से निपट सकती है, वह सीएलआर कुल का उपयोग करना होगा। हालांकि इस दृष्टिकोण के साथ समवर्ती तत्वों के लिए एक आदेश लागू करना अधिक कठिन है।

एक चर को असाइन करने की विधि की गारंटी नहीं है और इसे उत्पादन कोड से बचा जाना चाहिए।


यह अब Azure SQL में भी उपलब्ध है: azure.microsoft.com/en-us/roadmap/…
Simon_Weaver

34

Github पर GROUP_CONCAT परियोजना पर एक नज़र डालें , मुझे लगता है कि मैं वही करता हूं जो आप खोज रहे हैं:

इस प्रोजेक्ट में SQLCLR उपयोगकर्ता-परिभाषित एग्रीगेट फ़ंक्शंस (SQLCLR UDAs) का एक सेट है जो सामूहिक रूप से MySQL GROUP_CONCAT फ़ंक्शन को समान कार्यक्षमता प्रदान करता है। आवश्यक कार्यक्षमता के आधार पर सर्वश्रेष्ठ प्रदर्शन सुनिश्चित करने के लिए कई कार्य हैं ...


2
@MaxiWheat: बहुत से लोग वोट पर क्लिक करने से पहले सवाल या जवाब को ध्यान से नहीं पढ़ते। यह उनकी गलती के कारण सीधे मालिक पद को प्रभावित करता है।
स्टीव लैम

बहुत अच्छा काम करता है। एकमात्र विशेषता जो मुझे याद आ रही है वह एक कॉलम पर सॉर्ट करने की क्षमता है जिसे MySQL group_concat () पसंद कर सकते हैं:GROUP_CONCAT(klascode,'(',name,')' ORDER BY klascode ASC SEPARATOR ', ')
Jan

10

कई प्रोजेक्ट मैनेजर लिखने वाले प्रोजेक्ट्स से सभी प्रोजेक्ट मैनेजर नामों को संक्षिप्त करने के लिए:

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

9

नीचे दिए गए कोड के साथ आपको अपने प्रोजेक्ट गुणों पर PermissionLevel = बाहरी सेट करना होगा, परिनियोजन से पहले, और बाहरी कोड पर विश्वास करने के लिए डेटाबेस को बदलना होगा (सुरक्षा प्रमाणपत्रों और विकल्पों के बारे में कहीं और पढ़ना सुनिश्चित करें [जैसे प्रमाण पत्र]) "ALD DATASEASE_name SET" चलाकर। ट्रस्टी पर "।

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);
 }
    }

मैंने ऐसा परीक्षण किया है जो इस प्रकार दिखता है:

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 

और पैदावार: ए, बी, सी, डी


9

इनकी कोशिश की लेकिन एमएस SQL ​​सर्वर 2005 में मेरे उद्देश्यों के लिए निम्नलिखित सबसे उपयोगी था, जो मुझे xaprb पर मिला

declare @result varchar(8000);

set @result = '';

select @result = @result + name + ' '

from master.dbo.systypes;

select rtrim(@result);

@ मार्क जैसा कि आपने बताया कि यह अंतरिक्ष चरित्र था, जो मेरे लिए समस्या का कारण था।


मुझे लगता है कि इंजन वास्तव में इस पद्धति के साथ किसी भी आदेश की गारंटी नहीं देता है, क्योंकि निष्पादन योजना के आधार पर डेटा प्रवाह के रूप में चर की गणना की जाती है। ऐसा लगता है कि यह अभी तक ज्यादातर समय काम करता है।
19_

6

जे हार्डिमन के जवाब के बारे में, कैसे के बारे में:

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

वैसे, "सरनेम" का उपयोग एक टाइपो है या मैं यहां एक अवधारणा को नहीं समझ रहा हूं?

वैसे भी, बहुत से लोग धन्यवाद cuz यह मुझे काफी कुछ समय बचाया :)


1
अगर आप मुझसे पूछते हैं तो बेवजह जवाब दें और जवाब के रूप में मददगार नहीं हैं।
टिम मेर्स

1
केवल यह देखते हुए कि अब ... मैं इसे एक तरह से मतलब नहीं था, उस समय मैं sql सर्वर (अभी भी हूँ) से बहुत निराश था। वास्तव में इस पोस्ट से उत्तर वास्तव में मददगार थे; संपादित करें: क्यों यह सहायक btw नहीं था? इसने मेरे लिए चाल
चली

1

मेरे साथी गोगलर्स के लिए, यहां एक बहुत ही सरल प्लग-एंड-प्ले समाधान है जो कुछ समय के लिए अधिक जटिल समाधानों के साथ संघर्ष करने के बाद मेरे लिए काम करता है:

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

ध्यान दें कि मुझे आईडी को एक स्ट्रिंग के रूप में बदलने के लिए एक VARCHAR में बदलना था। यदि आपको ऐसा करने की आवश्यकता नहीं है, तो यहां और भी सरल संस्करण है:

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

इसका सारा श्रेय यहां दिया जाता है: https://social.msdn.microsoft.com/Forums/sqlserver/en-US/9508abc2-46e7-4186-b57f-7f368v4e084/replicating-groupconcat-function-of-mysql-in एसक्यूएल सर्वर? मंच = transactsql

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.