【问题标题】:How to perform permutation and combination in SQL Server?如何在 SQL Server 中进行排列组合?
【发布时间】:2019-11-13 12:17:40
【问题描述】:

我们有一个主表

Id combination dataid
1   1A,2B      5
2   1B,2A      5
3   1A         5
4   2A         5
5   1B         5
6   2B         5

输入中的每个元素创建组合,如果组合是相对于主表形成的,它应该返回第一个形成的组合。

如果组合没有形成,它应该从输入中返回组合列中可用的第一个元素。

以下是一些所需的输入和输出

输入:

dataid  value
5       1A,2B,2A

output: id-1 combination- 1A,2B

输入:

dataid  value
5       2B,1A

 output: id-1 combination- 1A,2B

输入:

dataid  value
5       1B,2A,2B,1A

output : id -2 Combination-1B,2A

输入:

dataid value

5       1B,1A

output : id-5 combination-1B

这可以在 SQL Server 2012 中实现吗?

有人可以帮忙吗?

谢谢!

【问题讨论】:

  • 为什么要在表中存储分隔数据?你真的应该把它正常化吗?到目前为止,您还尝试过什么?
  • 您好,组合是必需的,因为它具有含义因此存储它。
  • 所以不是定界值?
  • 不,是一对。
  • 我不太明白这个问题。

标签: sql sql-server tsql sql-server-2012


【解决方案1】:

首先,您需要一个包含 ItemNumber拆分器,为此您可以使用 delimitedSplit8k。那,APPLY 运营商 window aggregate function 和一些 bitwise 逻辑,你可以这样做:

-- Sample Data
DECLARE @t1 TABLE (stringID INT IDENTITY, String VARCHAR(100));
INSERT @t1 VALUES('1A,2B,2A'),('2B,1A'),('1B,2A,2B,1A'),('1B,1A');

-- Solution
WITH f AS
(
  SELECT
    t.stringID,
    Item = part.One+Part.Two,
    Chk  = COUNT(part.One) OVER (PARTITION BY t.StringID, part.One ORDER BY s.ItemNumber) &
           COUNT(part.Two) OVER (PARTITION BY t.StringID, part.Two ORDER BY s.ItemNumber)
  FROM        @t1 AS t
  CROSS APPLY dbo.delimitedSplit8k(t.String,',') AS s
  CROSS APPLY (VALUES(SUBSTRING(s.Item,1,1),SUBSTRING(s.Item,2,1))) part(One,Two)
)
SELECT ID = f.stringID, item.Combo
FROM   f
CROSS APPLY
(
  SELECT STUFF((
           SELECT ','+f2.Item
           FROM   f AS f2
           WHERE  f2.Chk = 1 AND f2.stringID = f.stringID
           ORDER BY f2.Item
           FOR XML PATH('')),1,1,'')
) AS item(Combo)
WHERE CHARINDEX(f.item, item.Combo) & f.Chk = 1;

返回:

ID          Combo
----------- ------------
1           1A,2B
2           1A,2B
3           1B,2A
4           1B

更新 2019114 基于 OP cmets:

如果您无法创建函数或在 PDW 中不允许使用有关 DelimitedSplit8K 的​​内容,您可以使用 XML 创建一个 内联拆分器(不是我的第一选择,但它并不可怕,并且可以满足您的需求'正在做。)

-- Sample Data
DECLARE @t1 TABLE (stringID INT IDENTITY, String VARCHAR(100));
INSERT @t1 VALUES('1A,2B,2A'),('2B,1A'),('1B,2A,2B,1A'),('1B,1A');

WITH f AS
(
  SELECT
    t.stringID,
    Item = part.One+Part.Two,
    Chk  = COUNT(part.One) OVER (PARTITION BY t.StringID, part.One ORDER BY s.ItemNumber) &
           COUNT(part.Two) OVER (PARTITION BY t.StringID, part.Two ORDER BY s.ItemNumber)
  FROM        @t1 AS t
  --CROSS APPLY dbo.delimitedSplit8k(t.String,',') AS s
  CROSS APPLY 
  (
    SELECT      ROW_NUMBER() OVER (ORDER BY (SELECT NULL)), x.xxx.value('(text())[1]','varchar(8000)')
    FROM        (VALUES(CAST(CONCAT('<Z><x>',REPLACE(t.string, ',','</x><x>'),'</x></Z>') AS XML))) AS f(NS)
    CROSS APPLY f.NS.nodes('Z/x') AS x(xxx)
  ) AS s(ItemNumber,Item)
  CROSS APPLY (VALUES(SUBSTRING(s.Item,1,1),SUBSTRING(s.Item,2,1))) part(One,Two)
)
SELECT ID = f.stringID, item.Combo
FROM   f
CROSS APPLY
(
  SELECT STUFF((
           SELECT ','+f2.Item
           FROM   f AS f2
           WHERE  f2.Chk = 1 AND f2.stringID = f.stringID
           ORDER BY f2.Item
           FOR XML PATH('')),1,1,'')
) AS item(Combo)
WHERE CHARINDEX(f.item, item.Combo) & f.Chk = 1;

另外,我记得曾经在 PDW 中遇到过类似的问题,我们可以使用递归 CTE 解决它。如果您可以创建函数,则可以将其用作拆分器:

ALTER FUNCTION dbo.rCteSplitter(@string VARCHAR(8000), @delim CHAR(1))
RETURNS TABLE AS RETURN
WITH a(N,X,XX) AS 
(
  SELECT  1, f.CI, SUBSTRING(@string,1,f.CI-1)
  FROM (VALUES(ISNULL(NULLIF(CHARINDEX(@delim,@string),0),LEN(@string)+1))) AS f(CI)
  UNION ALL
  SELECT  N+1, f.CI, SUBSTRING(@string,X+1,f.CI-X-1)
  FROM   a
  CROSS APPLY (VALUES(ISNULL(NULLIF(CHARINDEX(@delim,@string,X+1),0),LEN(@string)+1))) AS f(CI)
  WHERE N <= LEN(@string)-LEN(REPLACE(@string,@delim,''))
)
SELECT 
  ItemNumber = a.N,
  ItemIndex  = a.X,
  Item       = a.XX
FROM   a;
GO

那么解决方案将如下所示:

-- Sample Data
DECLARE @t1 TABLE (stringID INT IDENTITY, String VARCHAR(100));
INSERT @t1 VALUES('1A,2B,2A'),('2B,1A'),('1B,2A,2B,1A'),('1B,1A');

WITH f AS
(
  SELECT
    t.stringID,
    Item = part.One+Part.Two,
    Chk  = COUNT(part.One) OVER (PARTITION BY t.StringID, part.One ORDER BY s.ItemNumber) &
           COUNT(part.Two) OVER (PARTITION BY t.StringID, part.Two ORDER BY s.ItemNumber)
  FROM        @t1 AS t
  CROSS APPLY dbo.rCteSplitter(t.String,',') AS s
  CROSS APPLY (VALUES(SUBSTRING(s.Item,1,1),SUBSTRING(s.Item,2,1))) part(One,Two)
)
SELECT ID = f.stringID, item.Combo
FROM   f
CROSS APPLY
(
  SELECT STUFF((
           SELECT ','+f2.Item
           FROM   f AS f2
           WHERE  f2.Chk = 1 AND f2.stringID = f.stringID
           ORDER BY f2.Item
           FOR XML PATH('')),1,1,'')
) AS item(Combo)
WHERE CHARINDEX(f.item, item.Combo) & f.Chk = 1;

【讨论】:

  • 谢谢 Alan,我们不能在没有 delimitedSplit8k 的情况下这样做吗?在运行此脚本时,我发现并行数据仓库功能未启用
  • 最后一件事 - 我做了一点研究。您不能在 PDW 中使用函数,因此唯一可行的解​​决方案是使用我上面的示例,该示例不包含该函数(其中包含 XML 代码的那个)
  • 我在将 ORDER BY s.ItemNumber 分配给 Chk 时遇到 PDW 错误。如果我删除` ORDER BY s.ItemNumber `,它会给出soem结果而没有任何错误。我也只是通过它,我们没有将结果与主表匹配?或者我错了。谢谢!
猜你喜欢
  • 1970-01-01
  • 2013-01-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-26
  • 2019-05-22
相关资源
最近更新 更多