SQL:嵌套SELECT在单个字段中具有多个值

问题描述:

在我的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

您可以编写一个表值函数,它将逗号分隔的字符串拆分为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

你只需要使用的功能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

无关,与你的问题。只是,你原来的查询,也可以写一个说明,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

这似乎是一个串连聚合函数工作。 在SQL它可以使用CLR

http://www.mssqltips.com/tip.asp?tip=2022