एसक्यूएल सर्वर में स्टफ और 'एक्सएमएल पथ के लिए' कैसे काम करते हैं


367

तालिका है:

+----+------+
| Id | Name |
+----+------+    
| 1  | aaa  |
| 1  | bbb  |
| 1  | ccc  |
| 1  | ddd  |
| 1  | eee  |
+----+------+

आवश्यक उत्पादन:

+----+---------------------+
| Id |        abc          |
+----+---------------------+ 
|  1 | aaa,bbb,ccc,ddd,eee |
+----+---------------------+

प्रश्न:

SELECT ID, 
    abc = STUFF(
                 (SELECT ',' + name FROM temp1 FOR XML PATH ('')), 1, 1, ''
               ) 
FROM temp1 GROUP BY id

यह क्वेरी ठीक से काम कर रही है। लेकिन मुझे केवल इस स्पष्टीकरण की आवश्यकता है कि यह कैसे काम करता है या ऐसा करने के लिए कोई अन्य या छोटा तरीका है।

मुझे यह समझने में बहुत उलझन हो रही है।


1
यह भी देखें stackoverflow.com/questions/21623593/...
ChrisF

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

1
^ शायद IDअलग-अलग संस्थाओं की एक अलग तालिका में अद्वितीय है, और यह तालिका उन चीजों को संग्रहीत कर रही है जो उनके हैं।
निक रोलैंडो

यह क्वेरी काम नहीं करती है यदि कुछ पंक्तियों में एक अलग आईडी है। उदाहरण के लिए अगर 'ddd' और 'eee' में Id 2 है
केविनविक्टर

10
इस पृष्ठ पर मेरी मासिक यात्रा का समय यह देखने के लिए कि मैं कहाँ गलत हुआ।
टेलर एकले

जवाबों:


683

यहाँ दिया गया है कि यह कैसे काम करता है:

1. XML के लिए XML तत्व स्ट्रिंग प्राप्त करें

एक क्वेरी के अंत में XML पथ के लिए जोड़ना आपको पथ के तर्क में निहित तत्व नाम के साथ क्वेरी के परिणामों को एक्सएमएल तत्वों के रूप में आउटपुट करने की अनुमति देता है। उदाहरण के लिए, यदि हम निम्नलिखित कथन को चलाने के लिए थे:

SELECT ',' + name 
              FROM temp1
              FOR XML PATH ('')

रिक्त स्ट्रिंग (XML पथ ('') के लिए) पास करके, हम इसके बजाय निम्नलिखित प्राप्त करते हैं:

,aaa,bbb,ccc,ddd,eee

2. STUFF के साथ अग्रणी अल्पविराम निकालें

STUFF कथन का शाब्दिक अर्थ है "सामान" एक स्ट्रिंग को दूसरे में, पहले स्ट्रिंग के भीतर वर्णों को बदलना। हम, हालांकि, इसका उपयोग केवल मूल्यों की परिणामी सूची के पहले चरित्र को हटाने के लिए कर रहे हैं।

SELECT abc = STUFF((
            SELECT ',' + NAME
            FROM temp1
            FOR XML PATH('')
            ), 1, 1, '')
FROM temp1

के पैरामीटर STUFFहैं:

  • स्ट्रिंग "भरवां" (हमारे मामले में एक अग्रणी अल्पविराम के साथ नाम की पूरी सूची)
  • वर्णों को हटाने और सम्मिलित करने के लिए स्थान (1, हम एक रिक्त स्ट्रिंग में भर रहे हैं)
  • हटाने के लिए वर्णों की संख्या (1, अग्रणी अल्पविराम)

तो हम साथ समाप्त करते हैं:

aaa,bbb,ccc,ddd,eee

3. पूरी सूची प्राप्त करने के लिए आईडी पर शामिल हों

इसके बाद, हम सिर्फ अस्थायी तालिका में आईडी की सूची में शामिल होते हैं, नाम के साथ आईडी की सूची प्राप्त करने के लिए:

SELECT ID,  abc = STUFF(
             (SELECT ',' + name 
              FROM temp1 t1
              WHERE t1.id = t2.id
              FOR XML PATH (''))
             , 1, 1, '') from temp1 t2
group by id;

और हमारे पास हमारा परिणाम है:

-----------------------------------
| Id        | Name                |
|---------------------------------|
| 1         | aaa,bbb,ccc,ddd,eee |
-----------------------------------

उम्मीद है की यह मदद करेगा!


57
आपको Microsoft की प्रलेखन टीम (यदि कोई हो) के लिए काम करना चाहिए
Fandango68

55
@ Fandango68, @ FutbolFan - वह Microsoft की प्रलेखन टीम के लिए काम नहीं कर सकता है। उनकी व्याख्याएँ बहुत स्पष्ट हैं और बहुत सीधी भी। ;-)
क्रिस

1
@ChrisProsser मैं सहमत हूँ। Oracle LISTAGG11gR2 में फ़ंक्शन शुरू करके Oracle इस पर Microsoft से आगे रहा है । मुझे उस दिन की कार्यक्षमता याद आती है जहां मुझे इसके बजाय इसका उपयोग करना है। techonthenet.com/oracle/functions/listagg.php
FutbolFan

2
हैलो। चरण 1 में, यदि आप ऐसा करते हैं: XML PATH ('') के लिए अस्थायी नाम से चयन करें ... आपको <name> aaa </ name> <name> bbb </ name> ... आदि मिलेंगे ... I didn ' t पहली बार में इसका एहसास ... इसे SELECT '' + नाम ... आदि में बदलना ... टैग हटा देता है।
केविनविक्टर

1
@ क्रिसपॉसर - साइबेस एएसए का listदशकों से एक समारोह है। दुर्भाग्य से Microsoft इसके बजाय Sybase के ASE पर SQLServer आधारित है, और पिछले साल तक सूची समारोह के साथ कभी परेशान नहीं हुआ। मैं सहमत हूँ - यह मन-मुटाव है। और फिर वे करते हैं, वे इसे कहते हैं string_agg। मैंने सोचा listथा कि बहुत स्पष्ट था।
youcantryreachingme

75

यह लेख SQL में समवर्ती तारों के विभिन्न तरीकों को शामिल करता है, जिसमें आपके कोड का एक बेहतर संस्करण भी शामिल है जो कि XML को संक्षिप्त मानों को एनकोड नहीं करता है।

SELECT ID, abc = STUFF
(
    (
        SELECT ',' + name
        FROM temp1 As T2
        -- You only want to combine rows for a single ID here:
        WHERE T2.ID = T1.ID
        ORDER BY name
        FOR XML PATH (''), TYPE
    ).value('.', 'varchar(max)')
, 1, 1, '')
FROM temp1 As T1
GROUP BY id

यह समझने के लिए कि क्या हो रहा है, आंतरिक क्वेरी से शुरू करें:

SELECT ',' + name
FROM temp1 As T2
WHERE T2.ID = 42 -- Pick a random ID from the table
ORDER BY name
FOR XML PATH (''), TYPE

क्योंकि आप निर्दिष्ट कर रहे हैं FOR XML, आपको एक ही पंक्ति मिलेगी जिसमें एक XML टुकड़ा होगा जो सभी पंक्तियों का प्रतिनिधित्व करता है।

क्योंकि आपने पहले कॉलम के लिए एक स्तंभ उपनाम निर्दिष्ट नहीं किया है, प्रत्येक पंक्ति को XML तत्व में लिपटे हुए नाम के साथ कोष्ठक में निर्दिष्ट किया जाएगा FOR XML PATH। उदाहरण के लिए, यदि आपके पास था, तो आपको FOR XML PATH ('X')एक XML दस्तावेज़ मिलेगा जो जैसा दिखता था:

<X>,aaa</X>
<X>,bbb</X>
...

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

,aaa,bbb,...

.value('.', 'varchar(max)')बस जिसके परिणामस्वरूप एक्सएमएल टुकड़ा से मान प्राप्त करता है, किसी भी "विशेष" वर्ण एक्सएमएल-एन्कोडिंग के बिना। अब आपके पास एक स्ट्रिंग है जो दिखता है:

',aaa,bbb,...'

यह STUFFफ़ंक्शन तब आपको आगे आने वाले अंतिम परिणाम प्रदान करता है, जो निम्न दिखता है:

'aaa,bbb,...'

यह पहली नज़र में काफी भ्रामक लगता है, लेकिन यह कुछ अन्य विकल्पों की तुलना में काफी अच्छा प्रदर्शन करता है।


2
आपकी क्वेरी में टाइप का उपयोग क्या है। मुझे लगता है कि इसे परिभाषित करने के लिए, XML पथ का परिणाम मूल्य में संग्रहीत किया जाएगा (सुनिश्चित करें कि यह गलत है तो स्पष्ट नहीं करें)।
पुनीत चावला

8
@PuneetChawla: निर्देश बताता एसक्यूएल का उपयोग कर डेटा वापस करने के लिए लिखें। इसके बिना, डेटा को एक के रूप में वापस किया जाता है । स्तंभ में विशेष वर्ण होने पर XML- एन्कोडिंग समस्याओं से बचने के लिए इसका उपयोग यहाँ किया जाता है । TYPExmlnvarchar(max)name
रिचर्ड डीमिंग

2
@barlop: जैसा कि SimpleTalk लेख बताते हैं, यदि आप ड्रॉप TYPEऔर .value('.', 'varchar(max)'), तो आप परिणाम में XML -एनकोडित संस्थाओं के साथ समाप्त कर सकते हैं।
रिचर्ड डीमिंग

1
@RichardDeeming का मतलब है कि क्या डेटा में कोण कोष्ठक शामिल हैं या हो सकते हैं?
बार्लोप

1
लेकिन, चूंकि आपने कोई तत्व नाम निर्दिष्ट नहीं किया है, इसलिए आपको केवल मानों की एक सूची मिलती है , यह वह अंतर्दृष्टि है जो मुझे याद आ रही थी। धन्यवाद।
एडम

44

PATH मोड का उपयोग XML को SELECT क्वेरी से जेनरेट करने में किया जाता है

1. SELECT   
       ID,  
       Name  
FROM temp1
FOR XML PATH;  

Ouput:
<row>
<ID>1</ID>
<Name>aaa</Name>
</row>

<row>
<ID>1</ID>
<Name>bbb</Name>
</row>

<row>
<ID>1</ID>
<Name>ccc</Name>
</row>

<row>
<ID>1</ID>
<Name>ddd</Name>
</row>

<row>
<ID>1</ID>
<Name>eee</Name>
</row>

आउटपुट एलीमेंट-सेंट्रिक एक्सएमएल है, जहाँ परिणामस्वरूप पंक्तियों में प्रत्येक कॉलम वैल्यू को एक पंक्ति तत्व में लपेटा जाता है। चूंकि सेलेक्ट क्लॉज कॉलम नामों के लिए किसी भी एलियास को निर्दिष्ट नहीं करता है, इसलिए उत्पन्न चाइल्ड एलिमेंट नेम सेलेक्ट क्लॉज में संबंधित कॉलम नामों के समान हैं।

पंक्तियों में प्रत्येक पंक्ति के लिए एक टैग जोड़ा जाता है।

2.
SELECT   
       ID,  
       Name  
FROM temp1
FOR XML PATH('');

Ouput:
<ID>1</ID>
<Name>aaa</Name>
<ID>1</ID>
<Name>bbb</Name>
<ID>1</ID>
<Name>ccc</Name>
<ID>1</ID>
<Name>ddd</Name>
<ID>1</ID>
<Name>eee</Name>

चरण 2 के लिए: यदि आप एक शून्य-लंबाई स्ट्रिंग निर्दिष्ट करते हैं, तो रैपिंग तत्व उत्पन्न नहीं होता है।

3. 

    SELECT   

           Name  
    FROM temp1
    FOR XML PATH('');

    Ouput:
    <Name>aaa</Name>
    <Name>bbb</Name>
    <Name>ccc</Name>
    <Name>ddd</Name>
    <Name>eee</Name>

4. SELECT   
        ',' +Name  
FROM temp1
FOR XML PATH('')

Ouput:
,aaa,bbb,ccc,ddd,eee

चरण 4 में हम मानों को समेट रहे हैं।

5. SELECT ID,
    abc = (SELECT   
            ',' +Name  
    FROM temp1
    FOR XML PATH('') )
FROM temp1

Ouput:
1   ,aaa,bbb,ccc,ddd,eee
1   ,aaa,bbb,ccc,ddd,eee
1   ,aaa,bbb,ccc,ddd,eee
1   ,aaa,bbb,ccc,ddd,eee
1   ,aaa,bbb,ccc,ddd,eee


6. SELECT ID,
    abc = (SELECT   
            ',' +Name  
    FROM temp1
    FOR XML PATH('') )
FROM temp1 GROUP by iD

Ouput:
ID  abc
1   ,aaa,bbb,ccc,ddd,eee

चरण 6 में हम आईडी द्वारा तारीख को समूहीकृत कर रहे हैं।

STUFF (source_string, start, length, add_string) पैरामीटर या तर्क source_string संशोधित करने के लिए स्रोत स्ट्रिंग। position लंबाई वर्णों को हटाने के लिए source_string में स्थिति जोड़ें और फिर add_string डालें। लंबाई source_string से हटाने के लिए वर्णों की संख्या। add_string वर्णों का अनुक्रम source_string को प्रारंभ स्थिति में सम्मिलित करने के लिए।

SELECT ID,
    abc = 
    STUFF (
        (SELECT   
                ',' +Name  
        FROM temp1
        FOR XML PATH('')), 1, 1, ''
    )
FROM temp1 GROUP by iD

Output:
-----------------------------------
| Id        | Name                |
|---------------------------------|
| 1         | aaa,bbb,ccc,ddd,eee |
-----------------------------------

1
आप लिखते हैं "चरण 4 में हम मानों को समेट रहे हैं।" लेकिन यह स्पष्ट नहीं है कि क्यों / कैसे ','स्तंभ के रूप में निर्दिष्ट किया गया है, ('')xml पथ के साथ संयुक्त ,
संघनन

चरण 4 में, किसी भी स्ट्रिंग ऑपरेशन को करने से निर्दिष्ट रैपिंग तत्व का उपयोग होगा जो इस मामले के लिए रिक्त ('') है।
vCillusion

1
बिंदु 4 के बारे में सोच रहे किसी के लिए और क्यों <नाम> का विघटन। यह इसलिए है क्योंकि अल्पविराम के साथ नाम के बाद अब स्तंभ नहीं है, लेकिन सिर्फ मूल्य है, इसलिए SQL सर्वर को नहीं पता है कि xml टैग के लिए किस नाम का उपयोग किया जाना चाहिए। उदाहरण के लिए इस क्वेरी SELECT 'a' FROM some_table FOR XML PATH('')का उत्पादन होगा 'aaaaaaa':। लेकिन अगर कॉलम नाम निर्दिष्ट किया जाएगा: SELECT 'a' AS Col FROM some_table FOR XML PATH('')आपको परिणाम मिलेगा:<Col>a</Col><Col>a</Col><Col>a</Col>
एंथन नोव

23

इस सटीक परिदृश्य को संभालने के लिए Azure SQL डेटाबेस और SQL Server (2017 से शुरू) में बहुत नई कार्यक्षमता है। मेरा मानना ​​है कि यह XML / STUFF विधि के साथ जो आप पूरा करने की कोशिश कर रहे हैं, उसके लिए एक देशी आधिकारिक विधि के रूप में काम करेगा। उदाहरण:

select id, STRING_AGG(name, ',') as abc
from temp1
group by id

STRING_AGG - https://msdn.microsoft.com/en-us/library/mt790580.aspx

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


3
STRING_AGG SQL Server 2016 में नहीं है। इसे "vNext" कहा जा रहा है।
N8allan

उफ़, मेरा मतलब @lostmylogin से एडिट को ओवरराइट करने का नहीं था, इस बारे में खेद ... जो वास्तव में करेक्शन एडिट के जरिए धकेला गया।
ब्रायन जोर्डन

5

में for xml path, हम जैसे किसी भी मूल्य को परिभाषित करता है, तो [ for xml path('ENVLOPE') ]उसके बाद इन टैग प्रत्येक पंक्ति के साथ जोड़ दिया जाएगा:

<ENVLOPE>
</ENVLOPE>

2
SELECT ID, 
    abc = STUFF(
                 (SELECT ',' + name FROM temp1 FOR XML PATH ('')), 1, 1, ''
               ) 
FROM temp1 GROUP BY id

यहाँ उपरोक्त क्वेरी में STUFF फ़ंक्शन का उपयोग (,)उत्पन्न किए गए xml स्ट्रिंग से पहले अल्पविराम को हटाने के लिए किया जाता है, (,aaa,bbb,ccc,ddd,eee)फिर यह बन जाएगा (aaa,bbb,ccc,ddd,eee)

और FOR XML PATH('')बस कॉलम डेटा को (,aaa,bbb,ccc,ddd,eee)स्ट्रिंग में कनवर्ट करता है लेकिन PATH में हम '' पास कर रहे हैं, इसलिए यह XML टैग नहीं बनाएगा।

और अंत में हमने ID कॉलम का उपयोग करते हुए रिकॉर्ड बनाए हैं ।


2

मैंने डिबगिंग की और अंत में अपनी 'भरवां' क्वेरी को वापस कर दिया, यह सामान्य तरीका है।

केवल

select * from myTable for xml path('myTable')

मुझे एक ट्रिगर I डिबग से लॉग टेबल पर लिखने के लिए तालिका की सामग्री देता है।


1
Declare @Temp As Table (Id Int,Name Varchar(100))
Insert Into @Temp values(1,'A'),(1,'B'),(1,'C'),(2,'D'),(2,'E'),(3,'F'),(3,'G'),(3,'H'),(4,'I'),(5,'J'),(5,'K')
Select X.ID,
stuff((Select ','+ Z.Name from @Temp Z Where X.Id =Z.Id For XML Path('')),1,1,'')
from @Temp X
Group by X.ID

-1

STUFF ((अलग-अलग चुनें), '+ CAST (T.ID) तालिका T से जहां XML PATH (' ') के लिए T.ID = 1, 1,1,' ') AS नाम


-3

जहां क्लॉज के साथ अक्सर उपयोग कर रहा हूं

SELECT 
TapuAda=STUFF(( 
SELECT ','+TBL.TapuAda FROM (
SELECT TapuAda FROM T_GayrimenkulDetay AS GD 
INNER JOIN dbo.T_AktiviteGayrimenkul AS AG ON  D.GayrimenkulID=AG.GayrimenkulID WHERE 
AG.AktiviteID=262
) AS TBL FOR XML PATH ('')
),1,1,'')

2
मैं नहीं देखता कि यह एक उत्तर कैसे है, क्या आप कुछ स्पष्टीकरणों में टॉस कर सकते हैं?
गर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.