如何计算明细表中的唯一组合?

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 byorder 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')

你会看到新的列是动态生成的:

【讨论】:

以上是关于如何计算明细表中的唯一组合?的主要内容,如果未能解决你的问题,请参考以下文章

iOS核心数据连接2x主明细表

delphi如何将明细表中的数据保存到数据库中

BPM配置故事之案例7-公式计算

SQL中啥叫主键,啥是外键,有啥关系

SQL如何查询两个表连接明细表结果用分隔符分开?

Solid edge ST5 自己设置工程图模版时,无法设置零件明细表的默认格式