从表中获取逗号分隔的一组值,其中另一个表上的另一个参考值出现两次(或更多)
Posted
技术标签:
【中文标题】从表中获取逗号分隔的一组值,其中另一个表上的另一个参考值出现两次(或更多)【英文标题】:Get comma-separated set of values from table where another reference value on another table appears twice (or more) 【发布时间】:2021-12-06 22:01:01 【问题描述】:假设 SQL Server 2014 中的数据库设置如下:
DECLARE @MATERIAL TABLE (ID int, CODE varchar(30));
INSERT @MATERIAL (ID, CODE) VALUES
(1, 'D3033MBBY'),
(2, 'D3033MBTY'),
(3, '011130-01'),
(4, '011130-04C'),
(5, '021002'),
(6, '021017-B'),
(7, '021134-01'),
(8, '021135-01'),
(9, '021955-01'),
(10, '3LS91101-550'),
(11, 'D3049MBRB'),
(12, 'EF0118'),
(13, 'FV8130'),
(14, 'FY7009'),
(15, 'H05802'),
(16, 'D3033MRTE');
DECLARE @SUBSTITUTE TABLE (ID int, ITEID int, SUBSTITUTECODE varchar(100));
INSERT @SUBSTITUTE (ID, ITEID, SUBSTITUTECODE) VALUES
(5232, 1, '191045762418'),
(5442, 2, '191045762418'),
(6435, 3, '5206432380030'),
(6573, 4, '5206432380030'),
(6582, 5, '5206432357131'),
(6683, 6, '5206432369486'),
(7332, 7, '5206432380610'),
(7482, 8, '5206432380818'),
(7721, 9, '5206432346029'),
(7831, 10, '5205172116350'),
(8034, 11, '191045480992'),
(8184, 12, '4061622759543'),
(8284, 13, '4062058577497'),
(8573, 14, '4064039588089'),
(9438, 15, '4064048672519'),
(9746, 16, '191045762418');
SELECT sub.SUBSTITUTECODE
FROM @SUBSTITUTE AS sub
INNER JOIN @MATERIAL AS prod ON prod.ID = sub.ITEID
GROUP BY sub.SUBSTITUTECODE
HAVING COUNT(sub.SUBSTITUTECODE) > 1;
我想创建一个会产生以下结果集的查询:
CODES | SUBSTITUTECODE |
---|---|
D3033MBBY,D3033MBTY,D3033MRTE | 191045762418 |
011130-01,011130-04C | 5206432380030 |
换句话说,我想在@MATERIAL
中获得一组以逗号分隔的CODE
s,其中表@SUBSTITUTE
中的这些记录有重复的SUBSTITUTECODE
引用
间接地,我可以通过以下查询找到与那些重复的SUBSTITUTECODE
s 对应的CODE
s:
SELECT prod.CODE, sub.SUBSTITUTECODE
FROM @SUBSTITUTE AS sub
INNER JOIN @MATERIAL AS prod ON prod.ID = sub.ITEID
WHERE sub.SUBSTITUTECODE IN (SELECT sub.SUBSTITUTECODE
FROM @SUBSTITUTE AS sub
INNER JOIN @MATERIAL AS prod ON prod.ID = sub.ITEID
GROUP BY sub.SUBSTITUTECODE
HAVING COUNT(sub.SUBSTITUTECODE) > 1)
上述案例的工作小提琴可以找到here。
请注意,此方案的完整案例在 SQL Server 2014 上运行。
TIA
【问题讨论】:
我已经设法做到了这一点,正如工作小提琴中所示。我的意思是,我能够获得带有重复条目的SUBSTITUTECODE
s 列表!我无法添加与每个产品对应的产品的逗号分隔值的列。
@DaleK 我在我的问题中添加了更多信息。我能够在单独的行中获得CODES
以及与它们对应的SUBSTITUTECODE
s,并使用间接查询,这意味着我使用IN
子句将两个查询组合在一起的查询...但我敢打赌,还有另一种解决方案,只需使用某种类型的 JOIN
子句。
【参考方案1】:
不错的开始小提琴,谢谢!如果我们只是把你已经拥有的东西放在 CTE 中,我们可以围绕它编写一个标准的字符串聚合:
;WITH subs AS
(
SELECT prod.CODE, sub.SUBSTITUTECODE
FROM @SUBSTITUTE AS sub
INNER JOIN @MATERIAL AS prod ON prod.ID = sub.ITEID
WHERE sub.SUBSTITUTECODE IN (SELECT sub.SUBSTITUTECODE
FROM @SUBSTITUTE AS sub
INNER JOIN @MATERIAL AS prod ON prod.ID = sub.ITEID
GROUP BY sub.SUBSTITUTECODE
HAVING COUNT(sub.SUBSTITUTECODE) > 1)
)
SELECT CODES = STUFF((SELECT ',' + CODE
FROM subs AS s2 WHERE s2.SUBSTITUTECODE = subs.SUBSTITUTECODE
FOR XML PATH(''), TYPE).value(N'./text()[1]', N'nvarchar(max)'),1,1,''),
SUBSTITUTECODE FROM subs
GROUP BY SUBSTITUTECODE;
示例db<>fiddle
但是我们可以稍微简化一下这段代码,最重要的是避免引用两个表两次,像这样:
;WITH subs AS
(
SELECT s.ITEID, s.SUBSTITUTECODE, m.CODE,
c = COUNT(*) OVER (PARTITION BY s.SUBSTITUTECODE)
FROM @SUBSTITUTE AS s
INNER JOIN @MATERIAL AS m
ON m.ID = s.ITEID
)
SELECT CODES = STUFF((SELECT ',' + CODE
FROM subs AS s2 WHERE s2.SUBSTITUTECODE = subs.SUBSTITUTECODE
FOR XML PATH(''), TYPE).value(N'./text()[1]', N'nvarchar(max)'),1,1,''),
SUBSTITUTECODE
FROM subs
WHERE c > 1
GROUP BY SUBSTITUTECODE;
示例db<>fiddle
请注意,在更现代的 SQL Server 版本(2017+)上,STRING_AGG()
使这更容易:
SELECT CODES = STRING_AGG(m.CODE, ','), s.SUBSTITUTECODE
FROM @SUBSTITUTE AS s
INNER JOIN @MATERIAL AS m
ON m.ID = s.ITEID
GROUP BY s.SUBSTITUTECODE
HAVING COUNT(*) > 1;
示例db<>fiddle
【讨论】:
我想我会采用您的解决方案(简化的解决方案),因为对于不像您这样专业水平的人来说,它更清楚地了解发生了什么,而不是盲目地复制您的解决方案并将其粘贴到他们的真实情况中。非常感谢您! 是的,很遗憾我无法控制 SQL Server 的运行版本。我只是为了让他们的桌面商品应用程序和新网站之间运行同步服务而建立这个桥梁! @Faye 没问题,我认为从概念上讲,将STUFF()
中的所有内容都放在脑海中会容易得多,因为“这是一个将字符串连接在一起的表达式”。我已经记住了语法,但我并没有真正考虑过底层机制。 CTE 获取连接的行并提供计数基于具有重复项的列,因此我们知道如何过滤。然后如果你删除STUFF()
里面发生的事情(连接所有匹配这个子代码的材料代码),它只是从计数> 1的CTE中选择。
text()[1]
比 .[1]
快,例如参见 dba.stackexchange.com/a/193323/220697
@Charlieface 确实如此,但其他改进(如升级)会产生更大的影响。 :-) 也祝你好运教这种肌肉记忆使用text()
而不是.
。您不妨让我重新开始使用 Windows。 :-)【参考方案2】:
由于您使用的是 SQL Server 2014,因此您不能使用STRING_AGG()
这是使用FOR XML PATH
的解决方案
WITH CTE AS
(
SELECT prod.CODE, sub.SUBSTITUTECODE,
c = COUNT(*) OVER (PARTITION BY sub.SUBSTITUTECODE)
FROM @SUBSTITUTE AS sub
INNER JOIN @MATERIAL AS prod ON prod.ID = sub.ITEID
),
CTE2 AS
(
SELECT *
FROM CTE
WHERE c > 1
)
SELECT STUFF((SELECT ',' + CODE
FROM CTE2 x
WHERE x.SUBSTITUTECODE = c.SUBSTITUTECODE
FOR XML PATH('')), 1, 1, ''),
SUBSTITUTECODE
FROM CTE2 c
GROUP BY SUBSTITUTECODE
【讨论】:
这很好用!我只是想知道是否有任何更简单的方法来编写查询而不使用两个临时表... @FayeD。什么临时表?您存储数据的两个表变量?两个 CTE(比您现在的查询要简单得多)?还有什么? 是的,临时表是指使用WITH
表达式定义的两个 CTE。
@Faye 如果没有像子查询或 CTE 这样的表表达式,您将很难做到这一点。我认为查询中的 CTE 数量不是您应该用来评估它的清晰或有效程度的东西。 :-)以上是关于从表中获取逗号分隔的一组值,其中另一个表上的另一个参考值出现两次(或更多)的主要内容,如果未能解决你的问题,请参考以下文章
从表中选择行,其中具有相同 id 的另一个表中的行在另一列中具有特定值