【问题标题】:All possible combinations for two column data两列数据的所有可能组合
【发布时间】:2011-11-23 17:47:15
【问题描述】:

我有一个两列视图

Product Id   Tag
----------------------
1            Leather
1            Watch
2            Red
2            Necklace
2            Pearl

我正在尝试获取产品的所有可能的标签组合:

1          Leather
1          Leather,Watch
2          Pearl
2          Pearl,Necklace
2          Pearl Necklace,Red
2          Necklace
2          Necklace, Red
2          Red

我发现并窃取了一些 SQL,它们为我提供了所有但不是小版本的完整列表,如下所示。

任何想法,都开始让我头疼。虚拟品脱以获得最佳答案。

SELECT ProductId, 
       (SELECT CAST(Tag + ', ' AS VARCHAR(MAX)) 
          FROM ProductByTagView 
         WHERE Product.ProductId = ProductByTagView.ProductId
      order by tag
       FOR XML PATH ('')) AS Tags
FROM Product

【问题讨论】:

  • 在 SQL 中生成所有可能的组合是困难的;你最好用客户端语言来做,比如 PHP、perl 或 C#
  • 主要问题是组合的数量呈指数增长。至少一个标签有 (2^n -1) 种组合。 (您的列表中缺少 Red,Pearl,其中 n =3)。 n 能达到多大?
  • @Andomar 我想你可能已经找到答案了。如下所述,我有 40 多万个产品和 50 个 ish 标签,理论上每个产品都可以有 50 个标签,但实际上不会超过 10 个。
  • 如果每个产品都可以有 50 个标签的任意组合,那么您正在查看每个产品大约 50 个阶乘(表示为 50 个!)组合 - 或者换句话说,超过 10^64。即使将输出限制为所有 50 个标签中的任何 15 个,也应该会给您大约 10^24 个组合。有没有办法进一步缩小组合的数量?

标签: sql sql-server sql-server-2008 tsql permutation


【解决方案1】:

这是一种方法。

理论上,每个产品最多可以处理 20 个标签(受数字表大小的限制),但我并没有费心去尝试。在我的桌面上,大约需要 30 秒才能为具有 16 个标签的单个产品生成 65,535 个结果。希望每个产品的实际标签数量会比这少得多!

IF OBJECT_ID('tempdb..#Nums') IS NULL
BEGIN
CREATE TABLE #Nums
(
i int primary key
)

;WITH 
L0 AS (SELECT 1 AS c UNION ALL SELECT 1),       
L1 AS (SELECT 1 AS c FROM L0 A CROSS JOIN L0 B), 
L2 AS (SELECT 1 AS c FROM L1 A CROSS JOIN L1 B), 
L3 AS (SELECT 1 AS c FROM L2 A CROSS JOIN L2 B),
L4 AS (SELECT 1 AS c FROM L3 A CROSS JOIN L3 B), 
L5 AS (SELECT 1 AS c FROM L4 A CROSS JOIN L4 B), 
Nums AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS i FROM L5)

INSERT INTO #Nums
SELECT TOP 1048576 i FROM Nums;
END


;with ProductTags As
(
SELECT 1 ProductId,'Leather' AS Tag UNION ALL
SELECT 1, 'Watch' UNION ALL
SELECT 2, 'Red' UNION ALL
SELECT 2, 'Necklace' UNION ALL
SELECT 2, 'Pearl'
), NumberedTags AS
(
SELECT 
      ProductId,Tag,
       ROW_NUMBER() OVER (PARTITION BY ProductId ORDER BY Tag) rn,
       COUNT(*) OVER (PARTITION BY ProductId) cn
FROM ProductTags
),
GroupedTags As
(
SELECT ProductId,Tag,i
FROM NumberedTags
JOIN #Nums  on 
               #Nums.i <  POWER ( 2 ,cn)  
           and #Nums.i & POWER ( 2 ,rn-1) > 0
)
SELECT ProductId, 
       STUFF((SELECT CAST(', ' + Tag AS VARCHAR(MAX)) 
          FROM GroupedTags g2
         WHERE g1.ProductId = g2.ProductId and g1.i = g2.i
      ORDER BY Tag
       FOR XML PATH ('')),1,1,'') AS Tags
FROM GroupedTags g1
GROUP BY ProductId, i
ORDER BY ProductId, i

返回

ProductId   Tags
----------- ------------------------------
1            Leather
1            Watch
1            Leather, Watch
2            Necklace
2            Pearl
2            Necklace, Pearl
2            Red
2            Necklace, Red
2            Pearl, Red
2            Necklace, Pearl, Red

【讨论】:

  • Martin,谢谢你,但在我的生产数据库中(40,000 种产品,每个产品 50 个标签中有 15 个标签),这需要一个小时才能通过 415 个产品。
  • 正如@Andomar 上面评论的那样,我认为 C#(我选择的武器)和 SQL 的混合很可能是答案。非常感谢,爱这个社区。​​span>
【解决方案2】:

像这样?:

select a.ProductID, a.tag+','+b.tag from aView a 交叉连接 aView b 其中 a.tag != b.tag 联盟 从视图中选择 ProductID、标签

【讨论】:

    【解决方案3】:

    我的固定答案:

    假设您有一个包含整数的辅助 Numbers 表。

    DECLARE @s VARCHAR(5);
    SET @s = 'ABCDE';
    
    WITH Subsets AS (
    SELECT CAST(SUBSTRING(@s, Number, 1) AS VARCHAR(5)) AS Token,
    CAST('.'+CAST(Number AS CHAR(1))+'.' AS VARCHAR(11)) AS Permutation,
    CAST(1 AS INT) AS Iteration
    FROM dbo.Numbers WHERE Number BETWEEN 1 AND 5
    UNION ALL
    SELECT CAST(Token+SUBSTRING(@s, Number, 1) AS VARCHAR(5)) AS Token,
    CAST(Permutation+CAST(Number AS CHAR(1))+'.' AS VARCHAR(11)) AS
    Permutation,
    s.Iteration + 1 AS Iteration
    FROM Subsets s JOIN dbo.Numbers n ON s.Permutation NOT LIKE
    '%.'+CAST(Number AS CHAR(1))+'.%' AND s.Iteration < 5 AND Number
    BETWEEN 1 AND 5
    --AND s.Iteration = (SELECT MAX(Iteration) FROM Subsets)
    )
    SELECT * FROM Subsets
    WHERE Iteration = 5
    ORDER BY Permutation
    
    Token Permutation Iteration
    ----- ----------- -----------
    ABCDE .1.2.3.4.5. 5
    ABCED .1.2.3.5.4. 5
    ABDCE .1.2.4.3.5. 5
    (snip)
    EDBCA .5.4.2.3.1. 5
    EDCAB .5.4.3.1.2. 5
    EDCBA .5.4.3.2.1. 5
    (120 row(s) affected)
    

    【讨论】:

      【解决方案4】:

      正如@Andomar 所建议的那样,这是一个用客户端语言最好解决的问题,我尝试了各种解决方案(谢谢大家),尽管似乎有一些东西,尤其是在 Martin 的回答中,问题是花费的时间运行查询。

      再次感谢各位。

      亚历克

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-12
        • 1970-01-01
        • 2017-09-06
        相关资源
        最近更新 更多