2011-04-01 104 views
4

在我的SQL 2005數據庫中,我有一個表格,其值存儲爲與其他表格有關係的ID。因此,在我的MyDBO.warranty表中,我正在存儲product_id而不是product_name以節省空間。 product_name存儲在MyDBO.productsSQL:嵌套SELECT在單個字段中具有多個值

當營銷部門拉動的人口統計資料,查詢相關表中的每個ID(下調爲簡潔起見),選擇相應的名稱:

SELECT w1.warranty_id AS "No.", 
     w1.created AS "Register Date" 
     w1.full_name AS "Name", 
     w1.purchase_date AS "Purchased", 
     (
      SELECT p1.product_name 
      FROM WarrDBO.products p1 WITH(NOLOCK) 
      WHERE p1.product_id = i1.product_id 
     ) AS "Product Purchased", 
     i1.accessories 
FROM WarrDBO.warranty w1 
LEFT OUTER JOIN WarrDBO.warranty_info i1 
    ON i1.warranty_id = w1.warranty_id 
ORDER BY w1.warranty_id ASC 

現在,我的問題是,「附件」列在warranty_info表存儲幾個值:

No.  Register Date Name    Purchased  Accessories 
--------------------------------------------------------------------- 
1500 1/1/2008   Smith, John  Some Product 5,7,9 
1501 1/1/2008   Hancock, John Another   2,3 
1502 1/1/2008   Brown, James  And Another  2,9 

我需要做同樣的事情與我與「產品」做「附件」,並從拉accessory_name MyDBO.accessories表使用accessory_id。我不知道從哪裏開始,因爲首先我需要提取ID,然後以某種方式將多個值連接成一個字符串。所以每行都會有「accessoryname1,accessoryname2,accessoryname3」:

No.  Register Date Name    Purchased  Accessories 
--------------------------------------------------------------------- 
1500 1/1/2008   Smith, John  Some Product Case,Bag,Padding 
1501 1/1/2008   Hancock, John Another   Wrap,Label 
1502 1/1/2008   Brown, James  And Another  Wrap,Padding 

我該怎麼做?

編輯>>發佈我的最終代碼:

我創造了這個功能:

CREATE FUNCTION SQL_GTOInc.Split 
(
    @delimited varchar(50), 
    @delimiter varchar(1) 
) RETURNS @t TABLE 
(
-- Id column can be commented out, not required for sql splitting string 
    id INT identity(1,1), -- I use this column for numbering splitted parts 
    val INT 
) 
AS 
BEGIN 
    declare @xml xml 
    set @xml = N'<root><r>' + replace(@delimited,@delimiter,'</r><r>') + '</r></root>' 

    insert into @t(val) 
    select 
    r.value('.','varchar(5)') as item 
    from @xml.nodes('//root/r') as records(r) 

    RETURN 
END 

並據此更新了我的代碼:

SELECT w1.warranty_id, 
     i1.accessories, 
     (
      CASE 
       WHEN i1.accessories <> '' AND i1.accessories <> 'NULL' AND LEN(i1.accessories) > 0 THEN 
        STUFF(
         (
          SELECT ', ' + a1.accessory 
          FROM MyDBO.accessories a1 
          INNER JOIN MyDBO.Split(i1.accessories, ',') a2 
          ON a1.accessory_id = a2.val 
          FOR XML PATH('') 
         ), 1, 1, '' 
        ) 
       ELSE '' 
      END 
     ) AS "Accessories" 
FROM MyDBO.warranty w1 
    LEFT OUTER JOIN MyDBO.warranty_info i1 
    ON i1.warranty_id = w1.warranty_id 
+0

很好地使用,就(應該)不用說,但你不應該在這樣的warranty_info的acccessories列存儲。您應該有一個包含兩列的連接表Warranty_info_accessories:warranty_id和accessory_id。這將大大簡化您的問題 – Gerrat 2011-04-01 20:50:14

+0

我同意,如果我建造了這個結構,那就是我會做的。不幸的是,數據庫作者正在盡其所能節省空間,這是他使用的方法之一。 – Dexter 2011-04-01 20:59:45

+0

我欣賞所有幫助的人,如果我可以選擇多個答案,我會的。顯然,我只是沒有仔細閱讀,並在我的陳述中遺漏了FOR XML PATH('')',導致了錯誤。我已經應用了所有的建議,但它並不完美。 – Dexter 2011-04-04 17:21:58

回答

5

您可以編寫一個表值函數,它將逗號分隔的字符串拆分爲XML,並將XML節點轉換爲行。

參見: http://www.kodyaz.com/articles//t-sql-convert-split-delimeted-string-as-rows-using-xml.aspx

通過函數調用的結果加入到配飾,以及東西,結果回逗號分隔的名稱列表。

未經測試的代碼:

SELECT w1.warranty_id AS "No.", 
     w1.created AS "Register Date" 
     w1.full_name AS "Name", 
     w1.purchase_date AS "Purchased", 
     (
      SELECT p1.product_name 
      FROM WarrDBO.products p1 WITH(NOLOCK) 
      WHERE p1.product_id = i1.product_id 
     ) AS "Product Purchased", 
     STUFF(
     (
      SELECT 
      ', ' + a.name 
      FROM [table-valued-function](i1.accessories) acc_list 
      INNER JOIN accessories a ON acc_list.id = a.id 
      FOR XML PATH('') 
     ), 1, 1, '' 
     ) AS [accessories] 
FROM WarrDBO.warranty w1 
    LEFT OUTER JOIN WarrDBO.warranty_info i1 
    ON i1.warranty_id = w1.warranty_id 
ORDER BY w1.warranty_id ASC 
+0

試過這個,我得到一個錯誤:'子查詢返回了超過1個值。當子查詢遵循=,!=,<, <= , >,> =或者當子查詢用作表達式時,這是不允許的。如果我放置一個where子句並且只從列表中選擇一個附件,它可以正常工作。我不認爲它會讓我在子查詢中使用函數作爲STUFF()的一部分() – Dexter 2011-04-01 23:56:30

1

你只需要使用的功能FOR XML SQL Server輕鬆地掃描字符串:

來自鏈接的博客帖子:

SELECT 
    STUFF(
    (
    SELECT 
     ' ' + Description 
    FROM dbo.Brands 
    FOR XML PATH('') 
    ), 1, 1, '' 
) As concatenated_string 

要分析已經被存儲爲逗號分隔的你將不得不寫分析領域,並返回,然後可以用IN謂詞在WHERE中使用的表UDF場條款。看看here for startershere

+0

好的,所以這會從配件表中選擇所有配件,並將它們放在一個字符串中。但是,如何從逗號分隔的字符串「2,3,9」中獲取配件ID?一旦我有了這些,如何將這些用作STUFF中SELECT的條件? – Dexter 2011-04-01 21:04:46

+0

謝謝,我會仔細看看。 – Dexter 2011-04-01 21:27:06

3

無關,與你的問題。只是,你原來的查詢,也可以寫一個說明,subqery移動到一起,因爲:

SELECT w1.warranty_id AS "No.", 
     w1.created AS "Register Date" 
     w1.full_name AS "Name", 
     w1.purchase_date AS "Purchased", 
     p1.product_name AS "Product Purchased", 
     i1.accessories 
FROM WarrDBO.warranty w1 
INNER JOIN WarrDBO.products p1 
    ON p1.product_id = i1.product_id 
LEFT OUTER JOIN WarrDBO.warranty_info i1 
    ON i1.warranty_id = w1.warranty_id 
ORDER BY w1.warranty_id ASC 
+0

謝謝你讓我知道。我一直試圖將所有這些選擇移到內部聯接,但是我一直難以形成這個部分。我大概有5個類似的表,我添加了兩個以上的INNER JOIN,導致我的查詢不返回任何內容。不知道爲什麼。 – Dexter 2011-04-04 19:08:17

+0

「SELECT」列表中的子查詢通常可以轉換爲「LEFT」連接。不是'INNER'加入。 – 2012-08-09 07:08:25