在 SQL 中叠加行

Overlaying rows in SQL

因此,我想将键值对集合存储在数据库中,然后根据一系列集合检索组合的键值对集合。每组可能不完整。

假设我们有一个 table 如下:

create table MYTABLE (ROLE VARCHAR(62), KEY VARCHAR(62), VALUE VARCHAR(62));
insert into MYTABLE values('R1', 'K1', 'R1K1');
insert into MYTABLE values('R1', 'K2', 'R1K2');
insert into MYTABLE values('R1', 'K3', 'R1K3');
insert into MYTABLE values('R2', 'K1', 'R2K1');
insert into MYTABLE values('R2', 'K2', 'R2K2');
insert into MYTABLE values('R2', 'K4', 'R2K4');
insert into MYTABLE values('R3', 'K1', 'R3K1');
insert into MYTABLE values('R3', 'K4', 'R3K4');
insert into MYTABLE values('R3', 'K5', 'R3K5');
insert into MYTABLE values('R4', 'K1', 'R4K1');
insert into MYTABLE values('R5', 'K6', 'R5K6');

当给定序列 R1、R2、R3(或 R3、R2、R1,具体取决于您的观点)时,我想要以下结果:

+-----+-------+
| KEY | VALUE |
+-----+-------+
| K1  | R3K1  |
| K2  | R2K2  |
| K3  | R1K3  |
| K4  | R3K4  |
| K5  | R3K5  |
+-----+-------+ 

最初,我打算在 SQL 之外执行此操作,方法是检索每个集合并将结果转储到散列中,然后让后续项目覆盖之前的项目:

select KEY,VALUE from MYTABLE where ROLE = 'R1';
select KEY,VALUE from MYTABLE where ROLE = 'R2';
select KEY,VALUE from MYTABLE where ROLE = 'R3';

但如果可以混入很多角色,我不想为每个角色返回数据库。

最终我发现这些语句是可行的:

Oracle/DB2:
select distinct KEY, first_value(VALUE) over (partition by KEY order by decode(ROLE, 'R1', 1, 'R2', 2, 'R3', 3) desc) value from MYTABLE where ROLE in ('R1', 'R2', 'R3') order by KEY;
MySQL (guess):
select distinct KEY, first_value(VALUE) over (partition by KEY order by field(ROLE, 'R1, 'R2', 'R3') desc) value from MYTABLE where ROLE in ('R1', 'R2', 'R3') order by KEY;

(最后的“按 KEY 排序”对于我的需要来说并不是绝对必要的。)

有更好的方法吗?

您可以用 row_number() 表达该逻辑:

select key, value
from (
    select t.*,
        row_number() over(partition by key order by role) as rn
    from mytable t
    where role in ('R1', 'R2', 'R3')
) t
where rn = 1

这里并不真正需要条件排序,因为 'R1' < 'R2' < 'R3'。但是如果你想对其他一些序列这样做,你通常会使用标准的 case 表达式:

select key, value
from (
    select t.*,
        row_number() over(
            partition by key 
            order by case role when 'R1' then 1 when 'R2' then 2 when 'R3' then 3 end
        ) as rn
    from mytable t
    where role in ('R1', 'R2', 'R3')
) t
where rn = 1

这是一种相当可移植的语法,适用于许多数据库(只要它们支持 window 函数)。