我有下表存储属性及其值。
DECLARE @temp table
(
attribute varchar(10),
value varchar(10)
)
INSERT INTO @temp
VALUES
('height', '1.6m'),
('height', '1.8m'),
('weight', '50kg'),
('weight', '80kg'),
('gender', 'male'),
('gender', 'female')
无论表中有多少个属性,我都希望生成这些属性的所有组合。
|Combination |Attribute |Value |
|------------|----------|-------|
|1 |height |1.6m |
|1 |weight |50kg |
|1 |gender |male |
|2 |height |1.6m |
|2 |weight |50kg |
|2 |gender |female |
|3 |height |1.6m |
|3 |weight |80kg |
|3 |gender |male |
|4 |height |1.6m |
|4 |weight |80kg |
|4 |gender |female |
|5 |height |1.8m |
|5 |weight |50kg |
|5 |gender |male |
|6 |height |1.8m |
|6 |weight |50kg |
|6 |gender |female |
|7 |height |1.8m |
|7 |weight |80kg |
|7 |gender |male |
|8 |height |1.8m |
|8 |weight |80kg |
|8 |gender |female |
我认为CTE是必需的,但没有想到实现的方法。请帮忙。
答案 0 :(得分:2)
以下查询似乎有效,是的,它使用CTE:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) rn
FROM (SELECT attribute AS attr1, value AS val1 FROM temp WHERE attribute = 'height') t1
CROSS JOIN (SELECT attribute AS attr2, value AS val2 FROM temp WHERE attribute = 'weight') t2
CROSS JOIN (SELECT attribute AS attr3, value AS val3 FROM temp WHERE attribute = 'gender') t3
)
SELECT rn AS Combination, attr1 AS Attribute, val1 AS Value FROM cte WHERE attr1 = 'height' UNION ALL
SELECT rn, attr2, val2 FROM cte WHERE attr2 = 'weight' UNION ALL
SELECT rn, attr3, val3 FROM cte WHERE attr3 = 'gender'
ORDER BY Combination, Attribute, Value;
此方法使用一系列交叉联接来生成中间表(CTE),该中间表包含每个组合的一条记录。有8种组合,给定3个属性,每个属性2个值。然后,我们使用一系列并集将其解开为所需的长格式结果。
答案 1 :(得分:2)
with cte as
(
select
-- combination number
row_number() over (order by (select null)) as rn
,t1.attribute as a1
,t1.value as v1
,t2.attribute as a2
,t2.value as v2
,t3.attribute as a3
,t3.value as v3
-- create all combinations, each attribute needs an extra join
from @temp as t1
join @temp as t2 on t1.attribute < t2.attribute
join @temp as t3 on t2.attribute < t3.attribute
)
select c.*
from cte
-- unpivot using CROSS APPLY
cross apply
(
values
(rn, a1, v1),
(rn, a2, v2),
(rn, a3, v3)
) c (combination, attribute, value)
order by 1,2,3;
每个其他属性都需要一个额外的联接,“选择”列表中的两列和“交叉应用”中的“值”。
请参见fiddle
当然可以使用基于select distinct attribute
的Dynamic SQL创建此查询
答案 2 :(得分:2)
如果我理解正确,则您不想对表中的属性数量进行预编程。您只需要所有组合,不管有多少。
这表示递归CTE。我认为以字符串形式获取组合是最简单的-将所有行合并为一行。为此,如果id
中有一个temp
列,这将很有帮助。所以,我假设一个存在。
因此,要获取ID的分组:
with t as (
select t.*,
dense_rank() over (order by attribute) as attribute_seqnum
from temp t
),
cte as (
select t.attribute, t.value, attribute_seqnum, 1 as lev, convert(varchar(max), t.id) as ids
from t
where attribute_seqnum = 1
union all
select t.attribute, t.value, t.attribute_seqnum, lev + 1, concat(ids, ',', t.id)
from cte join
t
on t.attribute_seqnum = cte.attribute_seqnum + 1
)
select *
from (select cte.*, max(lev) over () as max_lev
from cte
) x
where lev = max_lev;
这是通过枚举temp
中的属性,然后一次添加一个来实现的。
您可以取消拆分ID,然后重新加入表格以获取单独的行:
with t as (
select t.*,
dense_rank() over (order by attribute) as attribute_seqnum
from temp t
),
cte as (
select t.attribute, t.value, attribute_seqnum, 1 as lev, convert(varchar(max), t.id) as ids
from t
where attribute_seqnum = 1
union all
select t.attribute, t.value, t.attribute_seqnum, lev + 1, concat(ids, ',', t.id)
from cte join
t
on t.attribute_seqnum = cte.attribute_seqnum + 1
)
select x.seqnum, t.attribute, t.value
from (select row_number() over (order by (select null)) as seqnum, x.*
from (select cte.*, max(lev) over () as max_lev
from cte
) x
where lev = max_lev
) x cross apply
string_split(ids, ',') s join
temp t
on t.id = convert(int, s.value);
Here是db <>小提琴。
注意:您不必在id
中有temp
才能起作用。您可以在递归的每个步骤中存储属性/值对。但是,我认为这只会增加一些其他的复杂性。
答案 3 :(得分:1)
您可以结合使用cross join
和unpivot
来实现这一目标。
交叉加入
select row_number() over(order by t_h.attribute) as 'combo',
t_h.*, t_w.*, t_g.*
from @temp t_h
cross join @temp t_w
cross join @temp t_g
where t_h.attribute = 'height'
and t_w.attribute = 'weight'
and t_g.attribute = 'gender';
combo attribute value attribute value attribute value
-------------------- ---------- ---------- ---------- ---------- ---------- ----------
1 height 1.6m weight 50kg gender male
2 height 1.6m weight 50kg gender female
3 height 1.8m weight 50kg gender male
4 height 1.8m weight 50kg gender female
5 height 1.6m weight 80kg gender male
6 height 1.6m weight 80kg gender female
7 height 1.8m weight 80kg gender male
8 height 1.8m weight 80kg gender female
取消枢纽
我将上一个查询移至CTE,但也可以进行子查询。
with cte_source as (
select row_number() over(order by t_h.attribute) as 'combo',
t_h.attribute as 'att_h',
t_h.value as 'val_h',
t_w.attribute as 'att_w',
t_w.value as 'val_w',
t_g.attribute as 'att_g',
t_g.value as 'val_g'
from @temp t_h
cross join @temp t_w
cross join @temp t_g
where t_h.attribute = 'height'
and t_w.attribute = 'weight'
and t_g.attribute = 'gender'
)
select up.combo,
case up.x
when 'val_h' then 'height'
when 'val_w' then 'weight'
when 'val_g' then 'gender'
end as 'attribute',
up.val as 'value'
from cte_source s
unpivot (val for x in (val_h, val_w, val_g)) up;
combo attribute value
-------------------- --------- ----------
1 height 1.6m
1 weight 50kg
1 gender male
2 height 1.6m
2 weight 50kg
2 gender female
3 height 1.8m
3 weight 50kg
3 gender male
4 height 1.8m
4 weight 50kg
4 gender female
5 height 1.6m
5 weight 80kg
5 gender male
6 height 1.6m
6 weight 80kg
6 gender female
7 height 1.8m
7 weight 80kg
7 gender male
8 height 1.8m
8 weight 80kg
8 gender female
答案 4 :(得分:0)
这是我尝试的一种简单解决方案
;with
rows_cte([name], attribute, rn) as (
select v.*, row_number() over (order by (select null)) from
(select [value] h, attribute a from @temp where attribute='height') h
cross join
(select [value] w, attribute a from @temp where attribute='weight') w
cross join
(select [value] g, attribute a from @temp where attribute='gender') g
cross apply
(values (h, h.a), (w, w.a), (g, g.a)) v(n, a))
select ((rn-1)/3)+1 combination, rc.attribute, rc.[name]
from rows_cte rc
order by combination;
答案 5 :(得分:-1)
也许是:
select t1.attribute, t2.value from tab t1, tab t2
或者如果您需要唯一的内容:
select t1.attribute, t2.value
from
(select distinct attribute from tab) t1,
(select distinct value from tab ) t2