2013-01-17 110 views
4

我有以下表格:選擇回來的ID分組用逗號分隔的列表

EntryTag 
--------- 
EntryID 
TagID 

例putput(的EntryID,標籤識別):

1 2 
1 4 
1 5 
2 3 
2 4 
2 5 
etc... 

Tags 
---- 
TagID 
Name 

示例輸出:

1 peas 
2 corn 
3 carrots 
...etc. 

我想爲每個條目帶回標籤列表,但將標籤以逗號分隔。

例如,我想看到這一點:

EntryID  TagsCommaDelimited 
-------  ------------------ 
1   corn, peas, carrots 
2   barley, oats 
...and so on 

所以我需要列出每個的EntryID和它對應的逗號分隔的標籤列表。

而且我選擇形式的內容表看起來像這樣:

Content 
-------- 
ID -(which is in essence the EntryID, they didn't make it consistent) 
Description 
..etc. 

下面是我嘗試過,但我的語法沒有運氣:

declare @tagsCommaDelimited varchar (200) 
set @tagsCommaDelimited = ''; 


With AllEntryTags_CTE(Name, EntryID) 
as 
(
    select Tags .Name, 
      entryTags.EntryID 
    from EntryTag entryTags 
    join Tags on tags.Id = entryTags.TagID 
    group by entryTags.EntryID, tags.Name, entryTags.TagID 
), 

TagsByEntryCommaDelimited_CTE(EntryID, CommaDelimitedTags) 
as 
(
    select distinct allTags.EntryID, 
      (select @tagsCommaDelimited from (select @tagsCommaDelimited = coalesce (case when @tagsCommaDelimited = '' then allTags.Name 
                         else @tagsCommaDelimited + ',' + allTags.Name end ,'') as CommaDelimitedTags 
    from AllEntryTags_CTE allTags 
) 

select EntryID, CommaDelimitedTags from TagsByEntryCommaDelimited_CTE 

---- ----------------------- UPDATE -------------------------- --------

現在我跟gogn進行測試

我現在的問題是,我想用這最後CTE在我最後的select語句搶逗號標籤分隔的列表names..but它在說我的語法是不正確的:

;WITH CommaDelimitedTagIDs AS 
(
    SELECT DISTINCT EntryID, 
    (SELECT SUBSTRING((SELECT ',' + CAST(TagID AS NVARCHAR(10)) 
         FROM EntryTag AS T1 WHERE T1.EntryID=T2.EntryID 
         ORDER BY TagID 
         FOR XML PATH('')),2,200)) AS commaDelimitedTagIDs 
    FROM EntryTag T2 
), 

CommaDelimittedTagNames_CTE (EntryID, CommaDelimitedTagNames) as 
( 
    SELECT EntryID, (SELECT SUBSTRING((SELECT ',' + Name 
        FROM Tags 
        WHERE commaDelimitedTagIDs LIKE '%'+CAST(ID AS NVARCHAR(5))+'%' 
        ORDER BY ID FOR XML PATH('')),2,200) AS CSV) 
    FROM CommaDelimitedTagIDs 
) 

--select EntryID, CommaDelimitedTagNames from CommaDelimittedTagNames_CTE 


SELECT Title, 
     [Description], 
     DateSyndicated, 
     DateUpdated, 
     1, 
     CAST([Text] AS NVARCHAR(MAX)), 
     Author, 
     (select CommaDelimitedTagNames from CommaDelimittedTagNames_CTE) as tagNamesCommaDelimited 
FROM Content 
Join CommaDelimittedTagNames_CTE tags on tags.EntryID = Content.ID 
group by ID, Title, [Description], 
      DateSyndicated, DateUpdated, 
      CAST(subtextContent.[Text] AS NVARCHAR(MAX)), Author 

嘗試它也是這樣,沒有運氣

Select 
    ....other fields 
    (select CommaDelimitedTagNames from CommaDelimittedTagNames_CTE tagNames 
    join subContent on subContent.ID = tagNames.EntryID) as tags 
    FROM Content as subContent 

好吧我猜你不能有一個連接,我不得不改變它在哪裏。不知道爲什麼,但這個工程現在:

Select 
    ....other fields 
    (select CommaDelimitedTagNames from CommaDelimittedTagNames_CTE tagNames 
    where Content.ID = tagNames.EntryID) as tags 
    FROM Content 

回答

13
select ET1.EntryID, 
     (
     select ', '+T.Name 
     from Tags as T 
     inner join EntryTag as ET2 
      on T.TagID = ET2.TagID 
     where ET1.EntryID = ET2.EntryID 
     for xml path(''), type 
     ).value('substring(text()[1], 3)', 'varchar(max)') as TagsCommaDelimited 
from EntryTag as ET1 
group by ET1.EntryID 

剖析查詢

主查詢做了group by所以你只能得到一個行每個EntryID

TagsCommaDelimited與相關子查詢創建的。

在SQL Server for xml path用於創建一個查詢結果的XML表示。通過使用列別名和參數pathroot,您可以很好地控制XML的創建方式。

核心子查詢中連接的值', '+T.Name將不會有列名,並且for xml path('')的空參數根本不會創建任何標籤。將只有一個文本值返回。

當您添加typefor xml查詢的數據類型將是XML

爲了得到一個值了XML的,你應該使用value()方法。你可以轉換爲字符串,但如果你這樣做,你會例如獲得&字符串中,無論你已經使用&

value()函數的第一個參數是用來獲取你想要的值XQuery表達式。使用text()指定您只需要當前元素的值。 [1]告訴你想找到的第一個文本節點的SQL Server(你只有一個在這裏),但它仍然是必要的。

for xml查詢創建的字符串在字符串的開頭處有一個額外的逗號和空格,需要將其刪除。在這裏,我使用XQuery函數substring來獲取除前兩個字符以外的所有內容。

的第二個參數value()指定應返回的數據類型。

+0

你能解釋一下你在用XML做什麼以及它是如何工作的。 – PositiveGuy

+0

@CoffeeAddict我添加了對正在發生的事情的解釋。你爲什麼更喜歡CTE? gotqn使用CTE [在他的回答(http://stackoverflow.com/a/14374759/569436),但他仍然使用'爲xml'來串聯值。我想這可以通過遞歸CTE來完成,而不是使用'for xml',但我認爲這不會帶來什麼實際好處。 –

+0

這個解決方案的一個優點是它變成了1個sql命令,這使得它更容易與jdbcTemplate或其他數據庫API一起使用。 –

1
DECLARE @TableOne TABLE 
(
    EntryID INT, 
    TagID INT 
) 

DECLARE @TableTwo TABLE 
(
    TagID INT, 
    Name NVARCHAR(100) 
) 

INSERT INTO @TableOne (EntryID,TagID) 
VALUES (1,2) 
     ,(1,4) 
     ,(1,5) 
     ,(2,3) 
     ,(2,4) 
     ,(2,1) 

INSERT INTO @TableTwo (TagID,Name) 
VALUES (1,'Daniel') 
     ,(2,'Samuel') 
     ,(3,'Petkov') 
     ,(4,'Ivan') 
     ,(5,'Jack') 

/* 
    In this CTE we are going to format the values int the folowing way: 

    1 2,4,5 
    2 1,3,4 

    Or for eaech EntryIDs, we will have all its TagIDs 

*/ 
;WITH CTE AS 
(
    SELECT DISTINCT EntryID 
      ,(SELECT SUBSTRING((SELECT ',' + CAST(TagID AS NVARCHAR(10)) FROM @TableOne AS T1 WHERE T1.EntryID=T2.EntryID ORDER BY TagID FOR XML PATH('')),2,200)) AS CSVTags 
    FROM @TableOne T2 

) 
/* 
    Here we are replacing the EntryIDs with their names from the @TableTwo: 
*/ 
SELECT EntryID 
     ,(SELECT SUBSTRING((SELECT ',' + Name FROM @TableTwo WHERE CSVTags LIKE '%'+CAST(TagID AS NVARCHAR(5))+'%' ORDER BY TagID FOR XML PATH('')),2,200) AS CSV) 
FROM CTE