SQL सर्वर में स्ट्रिंग को सम्‍मिलित करने के लिए GROUP BY का उपयोग कैसे करें?


373

मुझे कैसे मिलेगा:

id       Name       Value
1          A          4
1          B          8
2          C          9

सेवा

id          Column
1          A:4, B:8
2          C:9

18
इस प्रकार की समस्या MySQL पर इसके GROUP_CONCAT()एग्रीगेट फ़ंक्शन के साथ आसानी से हल हो जाती है , लेकिन Microsoft SQL सर्वर पर इसे हल करना अधिक अजीब है। सहायता के लिए निम्नलिखित SO प्रश्न देखें: " संबंध के आधार पर एक रिकॉर्ड के खिलाफ कई रिकॉर्ड कैसे प्राप्त करें? "
बिल करविन

1
प्रत्येक Microsoft खाते के साथ कनेक्ट होने पर एक सरल समाधान के लिए मतदान करना चाहिए: connect.microsoft.com/SQLServer/feedback/details/427987/…
Jens Mühlenhoff

1
जब तक T-SQL नहीं बढ़ाया जाता है तब तक आप SQLCLR एग्रीगेट्स को एक विकल्प के रूप में उपयोग कर सकते हैं: groupconcat.codeplex.com
Orlando Colamatteo

1
का डुप्लीकेट stackoverflow.com/questions/194852/...
सलमान एक

जवाबों:


550

कोई कोरियर, लूप लूप, या उपयोगकर्ता-परिभाषित फ़ंक्शन की आवश्यकता नहीं है

बस XML और PATH के लिए रचनात्मक होना चाहिए।

[नोट: यह समाधान केवल SQL 2005 और बाद में काम करता है। मूल प्रश्न उपयोग में संस्करण को निर्दिष्ट नहीं करता है।]

CREATE TABLE #YourTable ([ID] INT, [Name] CHAR(1), [Value] INT)

INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'A',4)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'B',8)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (2,'C',9)

SELECT 
  [ID],
  STUFF((
    SELECT ', ' + [Name] + ':' + CAST([Value] AS VARCHAR(MAX)) 
    FROM #YourTable 
    WHERE (ID = Results.ID) 
    FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)')
  ,1,2,'') AS NameValues
FROM #YourTable Results
GROUP BY ID

DROP TABLE #YourTable

6
कोई टेम्प टेबल क्यों करेगा?
एमी बी

3
यह मेरे जीवन में देखी गई सबसे अच्छी SQL चीज़ है। किसी भी विचार अगर यह बड़े डेटा सेट के लिए "तेज" है? यह एक कर्सर की तरह क्रॉल करना शुरू नहीं करेगा या कुछ भी करेगा, करता है? काश और अधिक लोग इस पागलपन को वोट देते।
user12861

6
एह। मुझे बस इसके उप-क्वेरी शैली से नफरत है। JOINS बहुत अच्छे हैं। बस मुझे नहीं लगता कि मैं इस समाधान में उपयोग कर सकता हूं। किसी भी तरह, मुझे यह देखकर खुशी हुई कि मेरे अलावा अन्य एसक्यूएल डॉर्क हैं जो इस तरह से सामान सीखना पसंद करते हैं। आप सभी को कुदोस :)
केविन फेयरचाइल्ड

6
स्ट्रिंग हेरफेर करने का थोड़ा क्लीनर तरीका: STUFF ((Select ',' + [Name] + ':' + CAST ([Value] AS VARCHAR (MAX)) XML से #YourTable WHERE (ID / Results.ID) के लिए PATH ('')), 1,2, '') AS NameValues
जोनाथन सायस

3
बस मैंने कुछ पाया है। यहां तक ​​कि एक मामले में असंवेदनशील वातावरण में, क्वेरी के निचले हिस्से को कम करने के लिए। मैं यह अनुमान लगा रहा हूं क्योंकि यह एक्सएमएल है, जो संवेदनशील है
जलोपा

136

यदि यह SQL सर्वर 2017 या SQL सर्वर Vnext, SQL Azure है तो आप नीचे के रूप में string_agg का उपयोग कर सकते हैं:

select id, string_agg(concat(name, ':', [value]), ', ')
    from #YourTable 
    group by id

निर्दोष काम करता है!
10

1
यह महान काम करता है, बेहतर तो स्वीकृत जवाब।
Jannick Breunis

51

XML पथ का उपयोग पूरी तरह से नहीं किया जाएगा जैसा कि आप उम्मीद कर सकते हैं ... यह "&" को "& amp;" से बदल देगा और भी गड़बड़ करेगा <" and "> ... शायद कुछ और चीजें, निश्चित नहीं ... लेकिन आप यह कोशिश कर सकते हैं

मुझे इसके लिए वर्कअराउंड आया ... आपको बदलने की आवश्यकता है:

FOR XML PATH('')
)

साथ में:

FOR XML PATH(''),TYPE
).value('(./text())[1]','VARCHAR(MAX)')

... या NVARCHAR(MAX)यदि आप का उपयोग कर रहे हैं कि क्या thats।

क्यों नरक SQLएक समवर्ती कुल कार्य नहीं है? यह एक PITA है।


2
मैं शुद्ध करने के लिए सबसे अच्छा तरीका है सांकेतिक शब्दों में बदलना करने के लिए उत्पादन सांकेतिक शब्दों में बदलना नहीं है। आपको बहुत - बहुत धन्यवाद! यह निश्चित उत्तर है - जब तक एमएस इसके लिए उचित समर्थन नहीं जोड़ता है, जैसे एक CONCAT () कुल कार्य। मैं जो भी करता हूं उसे एक बाहरी-लागू में फेंक देता हूं जो मेरे संक्षिप्त क्षेत्र को वापस करता है। मैं अपने चयन-कथनों में नेस्टेड-चयन जोड़ने का प्रशंसक नहीं हूं।
माइकेटीवी

मैं सहमत था, मूल्य का उपयोग किए बिना, हम उन समस्याओं में भाग सकते हैं जहां पाठ एक XML एन्कोडेड वर्ण है। कृपया SQL सर्वर में समूहीकृत संघटन के लिए मेरे ब्लॉग को कवर करने वाले परिदृश्य खोजें। blog.vcillusion.co.in/…
vCillusion

40

जब मैं तार रिक्त स्थान और विशेष एक्सएमएल वर्ण (युक्त साथ काम करने के लिए केविन फेयरचाइल्ड के सुझाव को परिवर्तित करने की कोशिश की मैं समस्याओं के एक जोड़े में भाग &, <, >) जो इनकोडिंग किया गया।

मेरे कोड का अंतिम संस्करण (जो मूल प्रश्न का उत्तर नहीं देता है लेकिन किसी के लिए उपयोगी हो सकता है) इस तरह दिखता है:

CREATE TABLE #YourTable ([ID] INT, [Name] VARCHAR(MAX), [Value] INT)

INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'Oranges & Lemons',4)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'1 < 2',8)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (2,'C',9)

SELECT  [ID],
  STUFF((
    SELECT ', ' + CAST([Name] AS VARCHAR(MAX))
    FROM #YourTable WHERE (ID = Results.ID) 
    FOR XML PATH(''),TYPE 
     /* Use .value to uncomment XML entities e.g. &gt; &lt; etc*/
    ).value('.','VARCHAR(MAX)') 
  ,1,2,'') as NameValues
FROM    #YourTable Results
GROUP BY ID

DROP TABLE #YourTable

एक स्थान को एक सीमांकक के रूप में उपयोग करने के बजाय और सभी रिक्त स्थान को कॉमा के साथ बदलने के बजाय, यह सिर्फ प्रत्येक मान के लिए एक अल्पविराम और स्थान को पूर्व-लंबित करता STUFFहै, फिर पहले दो वर्णों को निकालने के लिए उपयोग करता है।

TYPE निर्देश का उपयोग करके XML एन्कोडिंग का स्वचालित रूप से ध्यान रखा जाता है ।


21

Sql Server 2005 और इसके बाद के संस्करण का उपयोग कर एक अन्य विकल्प

---- test data
declare @t table (OUTPUTID int, SCHME varchar(10), DESCR varchar(10))
insert @t select 1125439       ,'CKT','Approved'
insert @t select 1125439       ,'RENO','Approved'
insert @t select 1134691       ,'CKT','Approved'
insert @t select 1134691       ,'RENO','Approved'
insert @t select 1134691       ,'pn','Approved'

---- actual query
;with cte(outputid,combined,rn)
as
(
  select outputid, SCHME + ' ('+DESCR+')', rn=ROW_NUMBER() over (PARTITION by outputid order by schme, descr)
  from @t
)
,cte2(outputid,finalstatus,rn)
as
(
select OUTPUTID, convert(varchar(max),combined), 1 from cte where rn=1
union all
select cte2.outputid, convert(varchar(max),cte2.finalstatus+', '+cte.combined), cte2.rn+1
from cte2
inner join cte on cte.OUTPUTID = cte2.outputid and cte.rn=cte2.rn+1
)
select outputid, MAX(finalstatus) from cte2 group by outputid

इनपुट के लिए धन्यवाद, मैं हमेशा SQL सर्वर में समस्याओं को हल करने के लिए CTE और पुनरावर्ती CTE का उपयोग करना पसंद करता हूं। यह मेरे लिए महान काम करता है!
gbdavid

क्या बाहरी उपयोग के साथ क्वेरी में इसका उपयोग करना संभव है?
को छेद में आग

14

SQLCLR एग्रीगेट्स को http://groupconcat.codeplex.com से इंस्टॉल करें

फिर आप इस तरह से कोड लिख सकते हैं ताकि आपके द्वारा मांगे गए परिणाम प्राप्त हो सकें:

CREATE TABLE foo
(
 id INT,
 name CHAR(1),
 Value CHAR(1)
);

INSERT  INTO dbo.foo
    (id, name, Value)
VALUES  (1, 'A', '4'),
        (1, 'B', '8'),
        (2, 'C', '9');

SELECT  id,
    dbo.GROUP_CONCAT(name + ':' + Value) AS [Column]
FROM    dbo.foo
GROUP BY id;

मैंने कुछ साल पहले इसका इस्तेमाल किया था, सिंटैक्स सभी "एक्सएमएल पथ" ट्रिक की तुलना में बहुत साफ है और यह बहुत अच्छी तरह से काम करता है। जब SQL CLR फ़ंक्शन एक विकल्प होता है, तो मैं दृढ़ता से इसकी सिफारिश करता हूं।
सेप

12

एसक्यूएल सर्वर 2005 और बाद में आपको अपने स्वयं के कस्टम कुल कार्यों को बनाने की अनुमति देता है , जिसमें समंजन जैसी चीजें शामिल हैं- लिंक किए गए लेख के तल पर नमूना देखें।


4
दुर्भाग्य से यह (?) सीएलआर असेंबली का उपयोग करने की आवश्यकता है .. जो कि निपटने के लिए एक और मुद्दा है: - /

1
बस उदाहरण वास्तविक वास्तविक कार्यान्वयन के लिए सीएलआर का उपयोग करता है लेकिन इसकी आवश्यकता नहीं है। आप XML के लिए समाकलन समुच्चय फ़ंक्शन का उपयोग कर सकते हैं ताकि कम से कम यह भविष्य में इसे कॉल करने के लिए नट हो!
शिव

12

आठ साल बाद ... Microsoft SQL सर्वर vNext डेटाबेस इंजन ने अंत में समूहीकृत स्ट्रिंग संघनन का सीधे समर्थन करने के लिए Transact-SQL बढ़ाया है। सामुदायिक तकनीकी पूर्वावलोकन संस्करण 1.0 ने STRING_AGG फ़ंक्शन को जोड़ा और CTP 1.1 ने STRING_AGG फ़ंक्शन के लिए withIN GROUP क्लॉज़ जोड़ा।

संदर्भ: https://msdn.microsoft.com/en-us/library/mt775028.aspx


9

यह केविन फेयरचाइल्ड की पोस्ट के लिए एक अतिरिक्त है (वैसे बहुत चालाक)। मैंने इसे एक टिप्पणी के रूप में जोड़ा होगा, लेकिन मेरे पास अभी पर्याप्त बिंदु नहीं हैं :)

मैं इस विचार का उपयोग उस दृश्य के लिए कर रहा था जिस पर मैं काम कर रहा था, हालाँकि जिन वस्तुओं में मैं समाहित था, वे रिक्त स्थान समेट रहे थे। इसलिए मैंने सीमांकक के रूप में रिक्त स्थान का उपयोग न करने के लिए कोड को थोड़ा संशोधित किया।

फिर से शांत केविन धन्यवाद केविन के लिए!

CREATE TABLE #YourTable ( [ID] INT, [Name] CHAR(1), [Value] INT ) 

INSERT INTO #YourTable ([ID], [Name], [Value]) VALUES (1, 'A', 4) 
INSERT INTO #YourTable ([ID], [Name], [Value]) VALUES (1, 'B', 8) 
INSERT INTO #YourTable ([ID], [Name], [Value]) VALUES (2, 'C', 9) 

SELECT [ID], 
       REPLACE(REPLACE(REPLACE(
                          (SELECT [Name] + ':' + CAST([Value] AS VARCHAR(MAX)) as A 
                           FROM   #YourTable 
                           WHERE  ( ID = Results.ID ) 
                           FOR XML PATH (''))
                        , '</A><A>', ', ')
                ,'<A>','')
        ,'</A>','') AS NameValues 
FROM   #YourTable Results 
GROUP  BY ID 

DROP TABLE #YourTable 

9

एक उदाहरण होगा

Oracle में आप LISTAGG कुल फ़ंक्शन का उपयोग कर सकते हैं।

मूल अभिलेख

name   type
------------
name1  type1
name2  type2
name2  type3

Sql

SELECT name, LISTAGG(type, '; ') WITHIN GROUP(ORDER BY name)
FROM table
GROUP BY name

परिणाम होना

name   type
------------
name1  type1
name2  type2; type3

6
अच्छा लग रहा है, लेकिन सवाल विशेष रूप से ओरेकल के बारे में नहीं हैं।
user12861

13
मै समझता हुँ। लेकिन मैं ओरेकल के लिए एक ही चीज़ की तलाश कर रहा था, इसलिए मैंने सोचा कि मैं इसे यहां मेरे जैसे अन्य लोगों के लिए डालूंगा :)
मिशाल बी।

@MichalB। क्या आप वाक्य रचना के भीतर याद नहीं कर रहे हैं? जैसे: समूह के भीतर सूची (प्रकार, ',') (नाम से क्रम)?
ग्रेगरी

@ वृत्तिका: मैंने अपना उत्तर संपादित किया। मुझे लगता है कि मेरे पुराने समाधान दिनों में वापस काम करते थे। आपके द्वारा सुझाया गया वर्तमान फ़ॉर्म निश्चित रूप से काम करेगा, धन्यवाद।
मिशाल बी।

1
भविष्य के लोगों के लिए - आप अलग-अलग प्लेटफ़ॉर्म जैसे महत्वपूर्ण अंतर के लिए अपने स्वयं के उत्तर के साथ एक नया प्रश्न लिख सकते हैं
माइक एम

7

इस तरह का प्रश्न यहाँ बहुत बार पूछा जाता है, और समाधान अंतर्निहित आवश्यकताओं पर बहुत कुछ निर्भर करता है:

https://stackoverflow.com/search?q=sql+pivot

तथा

https://stackoverflow.com/search?q=sql+concatenate

आमतौर पर, गतिशील SQL, उपयोगकर्ता द्वारा परिभाषित फ़ंक्शन, या कर्सर के बिना ऐसा करने का कोई SQL-एकमात्र तरीका नहीं है।


2
सच नहीं। cte: s का उपयोग करके साइबरबाइवी का घोल बिना किसी विक्रेता-विशिष्ट हैकरी के शुद्ध एसक्यूएल है।
ब्योर्न लिंडक्विस्ट

1
प्रश्न और उत्तर के समय, मैंने पुनरावर्ती CTEs को बहुत पोर्टेबल के रूप में नहीं गिना होगा, लेकिन वे अब Oracle द्वारा समर्थित हैं। सबसे अच्छा समाधान मंच पर निर्भर करने वाला है। SQL सर्वर के लिए यह XML तकनीक या ग्राहक CLR कुल के लिए सबसे अधिक संभावना है।
जेड रूक्स

1
सभी प्रश्नों के लिए अंतिम उत्तर? stackoverflow.com/search?q= [ जो भी सवाल हो]
जुन्चेन लियू

7

बस कैड ने जो कहा, उसे जोड़ने के लिए, यह आमतौर पर एक फ्रंट-एंड डिस्प्ले चीज़ है और इसलिए इसे वहाँ संभाला जाना चाहिए। मुझे पता है कि कभी-कभी फ़ाइल निर्यात या अन्य "एसक्यूएल केवल" समाधान जैसी चीजों के लिए एसक्यूएल में कुछ 100% लिखना आसान होता है, लेकिन अधिकांश बार इस कॉनसेप्टेशन को आपके डिस्प्ले लेयर में संभाला जाना चाहिए।


11
ग्रुपिंग अब फ्रंट-एंड डिस्प्ले चीज़ है? समूहित परिणाम सेट में एक कॉलम को बदलने के लिए बहुत सारे वैध परिदृश्य हैं।
MGOwen

5

एक कर्सर की जरूरत नहीं है ... कुछ समय के लिए पर्याप्त है।

------------------------------
-- Setup
------------------------------

DECLARE @Source TABLE
(
  id int,
  Name varchar(30),
  Value int
)

DECLARE @Target TABLE
(
  id int,
  Result varchar(max) 
)


INSERT INTO @Source(id, Name, Value) SELECT 1, 'A', 4
INSERT INTO @Source(id, Name, Value) SELECT 1, 'B', 8
INSERT INTO @Source(id, Name, Value) SELECT 2, 'C', 9


------------------------------
-- Technique
------------------------------

INSERT INTO @Target (id)
SELECT id
FROM @Source
GROUP BY id

DECLARE @id int, @Result varchar(max)
SET @id = (SELECT MIN(id) FROM @Target)

WHILE @id is not null
BEGIN
  SET @Result = null

  SELECT @Result =
    CASE
      WHEN @Result is null
      THEN ''
      ELSE @Result + ', '
    END + s.Name + ':' + convert(varchar(30),s.Value)
  FROM @Source s
  WHERE id = @id

  UPDATE @Target
  SET Result = @Result
  WHERE id = @id

  SET @id = (SELECT MIN(id) FROM @Target WHERE @id < id)
END

SELECT *
FROM @Target


@marc_s शायद एक बेहतर आलोचना यह है कि तालिका के चर पर प्राथमिक कुंजी घोषित की जानी चाहिए।
एमी बी

@marc_s आगे के निरीक्षण पर, यह लेख एक दिखावा है - जैसा कि IO माप के बिना प्रदर्शन के लगभग सभी चर्चाएं हैं। मैंने LAG के बारे में सीखा - तो उसके लिए धन्यवाद।
एमी बी

4

चलो बहुत सरल हो जाओ:

SELECT stuff(
    (
    select ', ' + x from (SELECT 'xxx' x union select 'yyyy') tb 
    FOR XML PATH('')
    )
, 1, 2, '')

इस पंक्ति को बदलें:

select ', ' + x from (SELECT 'xxx' x union select 'yyyy') tb

आपकी क्वेरी के साथ


3

किसी भी क्रॉस अप्लाई के उत्तर को नहीं देखा, एक्सएमएल निष्कर्षण की भी कोई आवश्यकता नहीं है। यहाँ केविन फेयरचाइल्ड ने जो लिखा है उसका थोड़ा अलग संस्करण है। अधिक जटिल प्रश्नों में इसका उपयोग तेज और आसान है:

   select T.ID
,MAX(X.cl) NameValues
 from #YourTable T
 CROSS APPLY 
 (select STUFF((
    SELECT ', ' + [Name] + ':' + CAST([Value] AS VARCHAR(MAX))
    FROM #YourTable 
    WHERE (ID = T.ID) 
    FOR XML PATH(''))
  ,1,2,'')  [cl]) X
  GROUP BY T.ID

1
मान का उपयोग किए बिना, हम उन समस्याओं में भाग सकते हैं जहां पाठ एक XML एन्कोडेड वर्ण है
vCillusion

2

यदि समूह द्वारा ज्यादातर एक आइटम शामिल है, तो आप प्रदर्शन को निम्न तरीके से बेहतर कर सकते हैं:

SELECT 
  [ID],

CASE WHEN MAX( [Name]) = MIN( [Name]) THEN 
MAX( [Name]) NameValues
ELSE

  STUFF((
    SELECT ', ' + [Name] + ':' + CAST([Value] AS VARCHAR(MAX)) 
    FROM #YourTable 
    WHERE (ID = Results.ID) 
    FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)')
  ,1,2,'') AS NameValues

END

FROM #YourTable Results
GROUP BY ID

यह मानते हुए कि आप सूची में डुप्लिकेट नाम नहीं चाहते हैं, जो आप कर सकते हैं या नहीं।
jnm2

1

रिप्लेस फंक्शन और JSON PATH के लिए उपयोग करना

SELECT T3.DEPT, REPLACE(REPLACE(T3.ENAME,'{"ENAME":"',''),'"}','') AS ENAME_LIST
FROM (
 SELECT DEPT, (SELECT ENAME AS [ENAME]
        FROM EMPLOYEE T2
        WHERE T2.DEPT=T1.DEPT
        FOR JSON PATH,WITHOUT_ARRAY_WRAPPER) ENAME
    FROM EMPLOYEE T1
    GROUP BY DEPT) T3

नमूना डेटा और अधिक तरीकों के लिए यहां क्लिक करें


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