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

时间:2011-04-01 20:41:35

标签: sql sql-server sql-server-2005 select

在我的SQL 2005 DB中,我有一个表,其值存储为ID,并与其他表有关系。所以在我的 MyDBO.warranty 表中,我存储的是product_id而不是product_name,以节省空间。 product_name存储在 MyDBO.products 中。

当市场营销部门提取人口统计信息时,查询会从相关表中为每个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

我需要使用“附件”执行与“产品”类似的操作,并从 MyDBO.accessories 表中提取 accessory_name 使用 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

4 个答案:

答案 0 :(得分: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  

答案 1 :(得分:3)

与您的问题无关。只需注意您也可以编写原始查询,将子查询移动到连接,如下:

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

答案 2 :(得分:1)

您只需使用SQL Server的FOR XML功能轻松捕捉字符串:

链接博客帖子中的示例:

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

要解析已经存储为逗号分隔的字段,您必须编写一个解析该字段的UDF并返回一个表,然后该表可以与WHERE子句中的IN谓词一起使用。查看here for startershere

答案 3 :(得分:0)

它似乎是连接聚合函数的工作。 在SQL中,它可以使用CLR

进行部署

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