SQL:在单个字段中包含多个值的嵌套 SELECT

发布于 2024-10-28 08:34:53 字数 3208 浏览 0 评论 0原文

在我的 SQL 2005 DB 中,我有一个表,其中的值存储为 ID,并与其他表有关系。因此,在我的 MyDBO.warranty 表中,我存储的是product_id 而不是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

现在,我的问题是保修信息表上的“配件”列存储了几个值:

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

我需要对“Accessories”执行与“Product”类似的操作,并使用 accessory_id< 从 MyDBO.accessories 表中提取 accessory_name /强>。我不知道从哪里开始,因为首先我需要提取 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

In my SQL 2005 DB, I have a table with values stored as IDs with relationships to other tables. So in my MyDBO.warranty table, I'm storing product_id instead of product_name in order to save space. The product_name is stored in MyDBO.products.

When the marketing department pulls the demographic information, the query selects the corresponding name for each ID from related tables (trimmed down for brevity):

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

Now, my problem is that the "accessories" column on the warranty_info table stores several values:

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

I need to do something similar with "Accessories" that I did with "Product" and pull accessory_name from the MyDBO.accessories table using accessory_id. I'm not sure where to start, because first I'd need to extract the IDs and then somehow concatenate multiple values into a string. So each line would have "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

How do I do this?

EDIT>> Posting my final code:

I created this function:

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

And updated my code accordingly:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

っ左 2024-11-04 08:34:53

您可以编写一个表值函数,该函数只需将逗号分隔的字符串拆分为 XML 并将 XML 节点转换为行。

看:
http://www.kodyaz.com /articles//t-sql-convert-split-delimed-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  

You could write a table valued function that simply splits comma separated string into XML and turns XML nodes to rows.

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

Join to accessories through the result of function call, and stuff the result back to comma separated list of names.

Untested code:

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  
戒ㄋ 2024-11-04 08:34:53

与你的问题无关。请注意,您也可以编写原始查询,将子查询移动到联接,如下所示:

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

Nothing to do with your question. Just a note that your original query can also be written, moving the subqery to a join, as:

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
无名指的心愿 2024-11-04 08:34:53

您只需要使用 SQL Server 的 FOR XML 功能即可easy cat strings:

链接博客文章中的示例:

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

要解析已存储为逗号分隔的字段,您必须编写一个 UDF 来解析该字段并返回一个表,然后可以将其与 WHERE 中的 IN 谓词一起使用条款。请参阅这里了解初学者,以及此处

You just need to use the FOR XML feature of SQL Server to easily cat strings:

Example from the linked blog post:

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

To parse a field that has already been stored as comma delimited you will have to write a UDF that parses the field and returns a table which can then be used with an IN predicate in your WHERE clause. Look here for starters, and here.

咿呀咿呀哟 2024-11-04 08:34:53

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

http://www.mssqltips.com/tip.asp 进行部署?tip=2022

It seem to be a work for a concatenate aggregate function.
In SQL it can be deployed using CLR

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文