2015-07-03 83 views
150

表是:怎樣的東西, '對於XML路徑' 在SQL服務器工作

+----+------+ 
| 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 

此查詢工作正常。但我只需要解釋它是如何工作的,或者是否有任何其他或簡短的方法來做到這一點。

我越來越糊塗瞭解這一點。

+0

參見https://stackoverflow.com/questions/21623593/what-is-the-meaning-of-select-for-xml-path-1-1 – ChrisF

+1

令人難以置信的是,我開始了一個錯誤的問題的賞金,哈哈。 – user798719

+0

我爲此做了一個[SqlFiddle頁面](http://sqlfiddle.com/#!18/442cc/4),看它在現實生活中的工作。希望它能幫助別人。 – Sabuncu

回答

296

下面是它的工作原理:用FOR XML

添加FOR XML PATH到查詢的末尾

1.使用XML元素字符串可以讓你輸出的查詢作爲XML的結果元素,包含在PATH參數中的元素名稱。例如,如果我們運行下面的語句:

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

通過在一個空字符串傳遞(FOR XML PATH( ''))來代替,我們得到如下:

,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. ID註冊以獲得完整列表

接下來,我們剛剛加入這個ID的臨時表就行了,拿到名單與名字的ID:

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

希望這有助於!

+8

嗨尼泊爾,謝謝你的回答。我得到了完整的解釋。 –

+8

真的很好的解釋! –

+6

對我很有幫助+1 –

45

This article涵蓋了在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)')只是從結果XML片段中檢索值,而不對任何「特殊」字符進行XML編碼。您現在有一個字符串,它看起來像:

​​

STUFF功能,然後刪除前導逗號,給你一個最後的結果是這樣的:

'aaa,bbb,...' 

它乍看上去相當混亂,但與其他一些選擇相比,它的表現往往非常好。

+0

嗨理查德,謝謝你的回答。這有點混亂,但我幾乎得到完整的解釋和邏輯。 –

+2

類型在你的查詢中有什麼用處。我認爲它的定義,XML路徑的結果將存儲在值(不知道如果錯誤解釋它)。 –

+7

@PuneetChawla:['TYPE'指令](https://msdn.microsoft.com/en-us/library/ms190025.aspx)告訴SQL使用'xml'類型返回數據。沒有它,數據將以'nvarchar(max)'的形式返回。如果'name'列中有特殊字符,它在這裏用來避免XML編碼問題。 –

1

在用於XML路徑 如果我們定義等[用於XML路徑( 'ENVLOPE')]然後將與各行被添加

上述標籤的任何值。

+0

非常感謝。我正在使用FOR XML PATH解決方案。 – FrenkyB

6

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 Server 2016作爲提及我以爲我看到了一個潛在的功能,它是被包括在內。要麼我記得那個不正確的東西或改變了什麼,感謝修改版本的建議編輯。此外,相當深刻的印象,並沒有完全意識到多步審查過程,剛剛拉我最後的選擇。

+2

STRING_AGG不在SQL Server 2016中。據說它將進入「vNext」。 – N8allan

+0

糟糕,我不是故意重寫@lostmylogin的編輯,抱歉...這是誰通過修正編輯實際推送的。 –

0
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 
0
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列對分組記錄進行分組。

16

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> 

的輸出元件爲中心的XML,其中在所得到的行集中每個列的值被包裹在一個行元素。由於SELECT子句未爲列名指定任何別名,因此生成的子元素名稱與SELECT子句中的相應列名稱相同。

對於行集中的每一行添加一個標籤。

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中,我們將ID按日期分組。

STUFF(source_string,啓動,長度,add_string) 參數或參數 source_string 源字符串進行修改。 開始 在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 | 
----------------------------------- 
+0

非常好的解釋 – user798719

0

我做了調試,終於回到我的「釀」查詢到它,它的正常方式。

只需

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

給我的表的內容從觸發我調試寫入日誌表。