【发布时间】:2021-02-03 16:39:06
【问题描述】:
考虑下表,它存储了某些对象的某些属性的更新历史记录,按effective 和published 日期组织:
create table update_history(
obj_id integer,
effective date,
published date,
attr1 text,
attr2 integer,
attr3 boolean,
primary key(obj_id, effective, published)
);
insert into update_history values
(1, '2021-01-01', '2021-01-01', 'foo', null, null),
(1, '2021-01-01', '2021-01-02', null, 1, false),
(1, '2021-01-02', '2021-01-01', 'foo', 1, false),
(1, '2021-01-02', '2021-01-02', 'bar', 1, false),
(1, '2021-01-03', '2021-01-01', 'bar', 1, true),
(1, '2021-01-04', '2021-01-01', 'bar', 1, true),
(1, '2021-01-05', '2021-01-01', 'bar', 2, true),
(1, '2021-01-05', '2021-01-02', 'bar', 1, true),
(1, '2021-01-05', '2021-01-03', 'bar', 1, true),
(1, '2021-01-06', '2021-01-04', 'bar', 1, true)
;
我需要编写一个 PostgreSQL 查询来简化给定 obj_id 的历史记录视图,方法是排除那些没有按照@987654327 的顺序更改前一个更新中的任何属性的更新记录@ 和 published 列。本质上,这些将是第 ## 6、9 和 10 行,在下表中以 斜体 标记:
| # | obj_id | effective | published | attr1 | attr2 | attr3 |
|---|---|---|---|---|---|---|
| 1 | 1 | 2021-01-01 | 2021-01-01 | foo | (null) | (null) |
| 2 | 1 | 2021-01-01 | 2021-01-02 | (null) | 1 | false |
| 3 | 1 | 2021-01-02 | 2021-01-01 | foo | 1 | false |
| 4 | 1 | 2021-01-02 | 2021-01-02 | bar | 1 | false |
| 5 | 1 | 2021-01-03 | 2021-01-01 | bar | 1 | true |
| 6 | 1 | 2021-01-04 | 2021-01-01 | bar | 1 | true |
| 7 | 1 | 2021-01-05 | 2021-01-01 | bar | 2 | true |
| 8 | 1 | 2021-01-05 | 2021-01-02 | bar | 1 | true |
| 9 | 1 | 2021-01-05 | 2021-01-03 | bar | 1 | true |
| 10 | 1 | 2021-01-06 | 2021-01-04 | bar | 1 | true |
请记住,在现实生活中,要处理的属性要多得多,我不希望查询变得过于混乱。
我最接近预期结果的是使用rank 窗口函数:
select
obj_id, effective, published,
attr1, attr2, attr3
from (
select *,
rank() over (
partition by attr1, attr2, attr3
order by effective, published
) as rank
from update_history
where obj_id = 1) as d
where rank = 1
order by effective, published;
结果如下:
| obj_id | effective | published | attr1 | attr2 | attr3 |
|---|---|---|---|---|---|
| 1 | 2021-01-01 | 2021-01-01 | foo | (null) | (null) |
| 1 | 2021-01-01 | 2021-01-02 | (null) | 1 | false |
| 1 | 2021-01-02 | 2021-01-01 | foo | 1 | false |
| 1 | 2021-01-02 | 2021-01-02 | bar | 1 | false |
| 1 | 2021-01-03 | 2021-01-01 | bar | 1 | true |
| 1 | 2021-01-05 | 2021-01-01 | bar | 2 | true |
如您所见,原始表中的第 8 行被错误地排除在外,尽管它从前一行第 7 行更改为 attr2。显然,问题在于在窗口定义中排序之前应用了分区。
我想知道是否有另一种方法可以通过单个 PostgresSQL 查询来完成此操作。
【问题讨论】:
标签: postgresql window-functions