【问题标题】:Unique combination of multiple columns, order doesn't matter多列的唯一组合,顺序无所谓
【发布时间】:2021-07-15 06:03:40
【问题描述】:

假设一个有 3 列的表。每行代表每个值的唯一组合:

a a a
a a b
a b a
b b a
b b c
c c a
...

然而,我想要的是,

aab = baa = aba 
cca = cac = acc
...

最后,我想以 CSV 格式获取这些值,作为每个值的组合,就像我附加的图像一样。

感谢您的帮助!

下面是生成我的问题的查询,请看一下!

--=======================================
--populate test data
--=======================================
drop table if exists #t0
;
with 
cte_tally as
(
select row_number() over (order by (select 1)) as n 
from sys.all_columns
)
select 
  char(n) as alpha
into #t0
from 
  cte_tally
where
  (n > 64 and n < 91) or
  (n > 96 and n < 123);

drop table if exists #t1
select distinct upper(alpha) alpha into #t1 from #t0

drop table if exists #t2
select
    a.alpha c1
,   b.alpha c2
,   c.alpha c3
,   row_number()over(order by (select 1)) row_num
into #t2
from #t1 a
join #t1 b on 1=1
join #t1 c on 1=1


drop table if exists #t3
select *
into #t3
from (
    select *
    from #t2
) p
unpivot
    (cvalue for c in (c1,c2,c3)
) unpvt


select
    row_num
,   c
,   cvalue
from #t3
order by 1,2

--=======================================
--these three rows should be treated equally
--=======================================
select *
from #t2
where concat(c1,c2,c3) in  ('ABA','AAB', 'BAA')

--=======================================
--what i've tried...
--row count is actually correct, but the problem is that it ommits where there're any duplicate alphabet.
--=======================================
select 
    distinct
    stuff((
        select
            distinct
        '.' + cvalue
        from #t3 a
        where a.row_num = h.row_num
    for xml path('')
    ),1,1,'') as comb
from #t3 h

【问题讨论】:

  • 反透视、排序、透视、连接。向我们展示您不成功的查询尝试!
  • 在我看来,按原样提取行并用编程语言进行组合会容易得多。
  • 根据问题指南,请展示您的尝试并告诉我们您发现了什么(在本网站或其他地方)以及为什么它不能满足您的需求。
  • 我找不到任何关于我的案例的帖子,似乎一切都是关于交叉连接以从多个列中找到不同的值,这并不是我想要的。
  • 您关心输出 csv 中的元素顺序吗?例如,您可能会得到 37、30、30 或者您可能会得到 30、37、30 等,但是对于您的输出,您是否需要元素的标准排序,例如 37、30、30,或者任何顺序都可以接受?

标签: sql sql-server tsql unique combinations


【解决方案1】:

正如in the comments 所指出的,您可以取消透视这些值,按正确的顺序对它们进行排序,然后将它们重新聚合到一行中。然后您可以按这些新值对原始行进行分组。

SELECT *
FROM #t2
CROSS APPLY (
    SELECT a = MIN(val), b = MIN(CASE WHEN rn = 2 THEN val), c = MAX(val)
    FROM (
        SELECT *, rn = ROW_NUMBER() OVER (ORDER BY val)
        FROM (VALUES (c1),(c2),(c3) ) v3(val)
    ) v2
) v
GROUP BY v.a, v.b, v.c;

真的,您或许应该做的是首先确保值的顺序正确:

ALTER TABLE #t2
   ADD CONSTRAINT t2_ValuesOrder
       CHECK (c1 <= c2 AND c2 <= c3);

【讨论】:

  • 这解决了我的问题。谢谢!使用交叉应用取消透视是我不知道的。
  • @HoonChoi CROSS APPLY 是自切片面包以来最好的东西。另见stackoverflow.com/questions/65818438/…
【解决方案2】:

会很好奇为什么,当然你有一个理由。可能建议有一个查找表,将所有关联的键保存到“映射表”。您可以在实施时优化其中的一些。首先创建一个表来保存“下一个/新键”(这是 1、2、3...)的来源。在您批量插入“映射表”的每批记录之后,您都会获得一个新的“新键”。 “映射表”包含键值的组合,每个组合一行以及您的“新键”应该得到一个看起来像这样的表:

A, B, C, 1
A, C, B, 1
B, A, C, 1
...
X, Y, Z, 2
X, Z, Y, 2

如果您可以更新源表以保存“映射键”(1、2、3)列,那么您只需从映射表中查找(c1=a,c2=a,c3=b ) 此查找的顺序无关紧要。一个建议是在您的映射表上使用 c1,c2,c3 创建一个复合唯一键。然后要获取您的记录,只需从映射表中查找“映射键值”,然后查询匹配映射键值的记录。或者,如果您不进行预查找以获取映射键,您应该能够使用映射键值进行自联接...

【讨论】:

  • 您的建议是为每个组合创建一个映射表到某个(一个)值。但是问题是如何创建映射表。
  • 好吧,我的建议会奏效,而且大多数时候我都看到过“ABC”==“CAB”或任何组合的地方,你很快就会需要“ABC”==“XYZ”,解决方案建议也会处理吗?无论如何,我认为我提到了创建映射表的过程?
【解决方案3】:

如果您希望它们采用 CSV 格式:

select distinct v.cs
from #t2 t2 cross apply
     (select string_agg(c order by c desc, ',') as cs
      from (values (t2.c1), (t2.c2), (t2.c3)
           ) v(c)
     ) v;

【讨论】:

    【解决方案4】:

    在我看来,您需要的是某种形式的掩蔽*。拿着这个小提琴:

    http://sqlfiddle.com/#!18/fc67f/8

    我创建了一个映射表,其中包含所有可能的值,并将其与 10 的递增顺序配对。在该映射表上进行交叉连接,连接值,添加掩码并对总数进行分组将产生所有独特的组合。

    这是小提琴的代码:

    CREATE TABLE maps (
      val varchar(1),
      num int
    );
    
    INSERT INTO maps (val, num) VALUES ('a', 1), ('b', 10), ('c', 100);
    
    SELECT mask, max(vals) as val 
    FROM (
      SELECT concat(m1.val, m2.val, m3.val) as vals,
          m1.num + m2.num + m3.num as mask
      FROM maps m1
      CROSS JOIN maps m2
      CROSS JOIN maps m3
    ) q GROUP BY mask
    

    使用这些值 10 将确保掩码包含每个值的计数,结果数字中的每个位置列一个,然后您可以对其进行分组以获得唯一(ish)字符串。

    我不知道您的数据是什么样的,如果您有超过 10 个可能的值,那么您将不得不使用 10 以外的其他基数,但该理论仍应适用。我没有编写代码来将值表中的列提取到映射表中,但我相信你可以做到。

    *实际上,我认为我正在寻找的术语是 flag。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-02-25
      • 2015-09-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多