如何计算明细表中的唯一组合?
Posted
技术标签:
【中文标题】如何计算明细表中的唯一组合?【英文标题】:How do I count unique combinations from a detail table? 【发布时间】:2018-05-10 20:07:19 【问题描述】:我需要在 SQL 中编写一个查询来计算记录的唯一组合的数量。我有一个项目表,每个项目都有一个子表列出选项。每个项目可能有 0 到 x 个选项。我想计算每种组合有多少。我以为我可以使用子表并使用 pivot 和 unpivot 对其进行转置,但我还没有弄清楚。然后我尝试创建一个组合列表,但我不知道如何计算出现次数。有人可以告诉我如何做到这一点或指出正确的方向吗?
这是我要使用的表格:
Item | Option
----------------
1 | A
1 | B
2 | B
3 | B
4 | B
4 | C
5 | A
6 | A
6 | B
6 | C
7 | A
7 | B
7 | C
8 | A
8 | B
9 | A
10 | A
10 | B
我想要的结果是这样的:
Option 1 | Option 2 | Option 3 | Count
--------------------------------------------
A | B | | 3 * 1, 8, 10
B | | | 2 * 2, 3
B | C | | 1 * 4
A | | | 2 * 5, 9
A | B | C | 2 * 6, 7
这就是说 A 和 B 的组合出现了两次,两次 B 是唯一选择的选项,B 和 C 一起选择了 1 次。 (星号后面的数字不是结果的一部分,它们只是用来显示正在计算的项目。)
我最接近的是下面的查询。它给了我独特的组合,但没有告诉我这种组合发生了多少次:
SELECT ItemCombo, Count(*) AS ItemComboCount
FROM
(
SELECT
Item
,STUFF((SELECT ',' + CAST(Option AS varchar(MAX))
FROM itemDetail a
WHERE a.Item = b.Item
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'),1,1,''
) AS ItemCombo
FROM itemDetail b
) AS Combos
GROUP BY ItemCombo
ORDER BY Count(*) DESC
【问题讨论】:
【参考方案1】:您应该在内部查询中使用group by
和order by option
,以便可以正确分组连接的值。
SELECT ItemCombo, Count(*) AS ItemComboCount
FROM
(
SELECT
Item
,STUFF((SELECT ',' + CAST(Option AS varchar(MAX))
FROM itemDetail a
WHERE a.Item = b.Item
ORDER BY Option
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'),1,1,''
) AS ItemCombo
FROM itemDetail b
GROUP BY item
) AS Combos
GROUP BY ItemCombo
ORDER BY Count(*) DESC
【讨论】:
谢谢!这让我明白了。有什么方法可以在不同列中获取选项,如我原始示例中的示例所示?我希望将它们放在单独的列中,而不是带有“A,B”的单列:Option1、Option2、Option3 等。【参考方案2】:为了解决您在 cmets 中提到的额外要求,我将添加一个 CTE、更多 XML 处理和动态 TSQL 到 Vamsi Prabhala's excellent answer(我这边 +1):
--create test table
create table tmp (Item int, [Option] char(1))
--populate test table
insert into tmp values ( 1, 'A') ,( 1, 'B') ,( 2, 'B') ,( 3, 'B') ,( 4, 'B') ,( 4, 'C') ,( 5, 'A') ,( 6, 'A') ,( 6, 'B') ,( 6, 'C') ,( 7, 'A') ,( 7, 'B') ,( 7, 'C') ,( 8, 'A') ,( 8, 'B') ,( 9, 'A') ,(10, 'A') ,(10, 'B')
declare @count int
declare @loop int = 1
declare @dynamicColums nvarchar(max) = ''
declare @sql nvarchar(max) = ''
--count possible values
select @count = max(c.options_count) from (
select count(*) as options_count from tmp group by item
) c
--build dynamic headers for all combinations
while @loop <= @count
begin
set @dynamicColums = @dynamicColums + ' Parts.value(N''/x['+ cast(@loop as nvarchar(max)) +']'', ''char(1)'') AS [Option ' + cast(@loop as nvarchar(max)) + '],'
set @loop = @loop + 1
end
--build dynamic TSQL statement
set @sql = @sql + ';WITH Splitted'
set @sql = @sql + ' AS ('
set @sql = @sql + ' SELECT ItemComboCount'
set @sql = @sql + ' ,ItemCombo'
set @sql = @sql + ' ,CAST(''<x>'' + REPLACE(ItemCombo, '','', ''</x><x>'') + ''</x>'' AS XML) AS Parts'
set @sql = @sql + ' FROM '
set @sql = @sql + ' ('
set @sql = @sql + ' SELECT ItemCombo, Count(*) AS ItemComboCount'
set @sql = @sql + ' FROM'
set @sql = @sql + ' ('
set @sql = @sql + ' SELECT'
set @sql = @sql + ' Item '
set @sql = @sql + ' ,STUFF((SELECT '','' + CAST([Option] AS varchar(MAX))'
set @sql = @sql + ' FROM tmp a '
set @sql = @sql + ' WHERE a.Item = b.Item'
set @sql = @sql + ' ORDER BY [Option]'
set @sql = @sql + ' FOR XML PATH(''''), TYPE).value(''.'', ''VARCHAR(MAX)''),1,1,'''''
set @sql = @sql + ' ) AS ItemCombo'
set @sql = @sql + ' FROM tmp b'
set @sql = @sql + ' GROUP BY item'
set @sql = @sql + ' ) AS Combos'
set @sql = @sql + ' GROUP BY ItemCombo'
set @sql = @sql + ' ) t'
set @sql = @sql + ' )'
set @sql = @sql + ' SELECT '
set @sql = @sql + @dynamicColums
set @sql = @sql + ' ItemComboCount as [Count]'
set @sql = @sql + ' FROM Splitted'
--execute dynamic TSQL statement
exec(@sql)
结果:
现在,如果您使用几个插入语句添加另一个值(例如“D”):
insert into tmp values ( 1, 'D')
insert into tmp values ( 7, 'D')
你会看到新的列是动态生成的:
【讨论】:
以上是关于如何计算明细表中的唯一组合?的主要内容,如果未能解决你的问题,请参考以下文章