多列的唯一组合,顺序无关紧要
Unique combination of multiple columns, order doesn't matter
假设 table 有 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
很好奇为什么,肯定有原因。可能建议查找 table,将所有关联的键保存到“映射 Table”。您可以在实施时优化其中的一些。首先创建一个 table 用于保存“Next/New 键”(这是 1、2、3... 的来源)。在将每批记录批量插入“映射 Table”后,您会获得一个新的“新密钥”。 “映射 Table”包含键值的组合,每个组合一行与您的“新键”一起应该得到一个 table 看起来像:
A, B, C, 1
A, C, B, 1
B, A, C, 1
...
X, Y, Z, 2
X, Z, Y, 2
如果您可以更新源 table 以保留“映射键”(1、2、3)的列,那么您只需从映射 table 中查找,其中 (c1 =a, c2=a, c3=b) 此查找的顺序无关紧要。一个建议是在您的映射 table 上使用 c1、c2、c3 创建一个复合唯一键。然后要获取您的记录,只需从映射 table 中查找“映射键值”,然后查询与映射键值匹配的记录。或者,如果您不进行预查找以获取映射键,您应该能够使用映射键值进行自连接...
正如 所指出的,您可以对值进行逆透视,以正确的顺序对它们进行排序,然后将它们重新聚合到一行中。然后您可以按这些新值对原始行进行分组。
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);
如果您希望它们采用 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;
在我看来,您需要的是某种形式的掩蔽*。拿这个 fiddle:
http://sqlfiddle.com/#!18/fc67f/8
我在其中创建了一个映射 table,其中包含所有可能的值并将其与 10 的递增顺序配对。在该映射 table 上进行交叉连接,连接值,添加掩码和总计分组将为您提供所有独特的组合。
这是来自 fiddle 的代码:
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 以外的其他基数,但理论应该仍然适用。我没有编写代码来将值 table 中的列提取到映射 table 中,但我相信你可以做到这一点。
*实际上,我认为我正在寻找的术语是标志。
假设 table 有 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
很好奇为什么,肯定有原因。可能建议查找 table,将所有关联的键保存到“映射 Table”。您可以在实施时优化其中的一些。首先创建一个 table 用于保存“Next/New 键”(这是 1、2、3... 的来源)。在将每批记录批量插入“映射 Table”后,您会获得一个新的“新密钥”。 “映射 Table”包含键值的组合,每个组合一行与您的“新键”一起应该得到一个 table 看起来像:
A, B, C, 1
A, C, B, 1
B, A, C, 1
...
X, Y, Z, 2
X, Z, Y, 2
如果您可以更新源 table 以保留“映射键”(1、2、3)的列,那么您只需从映射 table 中查找,其中 (c1 =a, c2=a, c3=b) 此查找的顺序无关紧要。一个建议是在您的映射 table 上使用 c1、c2、c3 创建一个复合唯一键。然后要获取您的记录,只需从映射 table 中查找“映射键值”,然后查询与映射键值匹配的记录。或者,如果您不进行预查找以获取映射键,您应该能够使用映射键值进行自连接...
正如
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);
如果您希望它们采用 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;
在我看来,您需要的是某种形式的掩蔽*。拿这个 fiddle:
http://sqlfiddle.com/#!18/fc67f/8
我在其中创建了一个映射 table,其中包含所有可能的值并将其与 10 的递增顺序配对。在该映射 table 上进行交叉连接,连接值,添加掩码和总计分组将为您提供所有独特的组合。
这是来自 fiddle 的代码:
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 以外的其他基数,但理论应该仍然适用。我没有编写代码来将值 table 中的列提取到映射 table 中,但我相信你可以做到这一点。
*实际上,我认为我正在寻找的术语是标志。