有人可以帮助我完成查询的最后一步吗。
我有这张桌子小提琴
CREATE TABLE rent(id integer,start_date date, end_date date,objekt_id integer,person_id integer);
INSERT INTO rent VALUES
(1, '2011-10-01','2015-10-31',5156,18268),
(2, '2015-11-01','2018-04-30',5156,18268),
(3, '2018-05-01','2021-03-31',5156,18269),
(4, '2021-04-01','2021-05-15',5156,null),
(5, '2021-05-16','2100-01-01',5156,18270),
(6, '2021-03-14','2021-05-15',5160,18270),
(7, '2021-05-16','2100-01-01',5160,18271);
对于滞后和超前,我想要两列用于最后一个 person_id 和下一个 person_id。
通过这个查询,我几乎解决了我的问题,但仍有一件事我需要帮助改变。
with tbl as (
SELECT rent.*,
row_number() over (PARTITION BY objekt_id) as row_id
FROM rent
ORDER BY id)
SELECT r.id,
r.start_date,
r.end_date,
r.objekt_id,
r.person_id,
lag(person_id) over (PARTITION BY objekt_id, person_id IS NOT NULL AND objekt_id IS NOT NULL ORDER BY id) as last_person,
lead(person_id) over (PARTITION BY objekt_id, person_id IS NOT NULL AND objekt_id IS NOT NULL ORDER BY id) as next_person
FROM tbl r
order by 1;
最后一个或下一个 Person_id 始终必须为 null 或来自另一个 person_id。
目前第 2 行将为我提供 last_person_id = 18268,因为第 1 行具有相同的 person_id。如果 person_id 为空,我还想看到最后一个和下一个人。
现在输出:
id start_date end_date objekt_id person_id last_person next_person
1 2011-10-01 2015-10-31 5156 18268 18268
2 2015-11-01 2018-04-30 5156 18268 18268 18269
3 2018-05-01 2021-03-31 5156 18269 18268 18270
4 2021-04-01 2021-05-15 5156
5 2021-05-16 2100-01-01 5156 18270 18269
6 2021-03-14 2021-05-15 5160 18270 18271
7 2021-05-16 2100-01-01 5160 18271 18270
希望的输出:
id start_date end_date objekt_id person_id last_person next_person
1 2011-10-01 2015-10-31 5156 18268 18269
2 2015-11-01 2018-04-30 5156 18268 18269
3 2018-05-01 2021-03-31 5156 18269 18268 18270
4 2021-04-01 2021-05-15 5156 18269 18270
5 2021-05-16 2100-01-01 5156 18270 18269
6 2021-03-14 2021-05-15 5160 18270 18271
7 2021-05-16 2100-01-01 5160 18271 18270
查询的目标是选择一个特定的日期并判断该对象是否出租,然后还显示谁在出租它,谁是最后一个以及是否有人排队出租
你可以尝试使用相关子查询来根据你的逻辑条件来实现。
with tbl as (
SELECT rent.*,
row_number() over (PARTITION BY objekt_id) as row_id
FROM rent
ORDER BY id)
SELECT r.id,
r.start_date,
r.end_date,
r.objekt_id,
r.person_id,
( SELECT t1.person_id
FROM tbl t1
WHERE t1.objekt_id = r.objekt_id
AND t1.id < r.id
AND (t1.person_id <> r.person_id OR r.person_id IS NULL)
AND t1.person_id IS NOT NULL
ORDER BY t1.id desc
LIMIT 1) last_person,
(SELECT t1.person_id
FROM tbl t1
WHERE t1.objekt_id = r.objekt_id
AND t1.id > r.id
AND (t1.person_id <> r.person_id OR r.person_id IS NULL)
AND t1.person_id IS NOT NULL
ORDER BY t1.id
LIMIT 1) next_person
FROM tbl r
order by 1;
使用窗口函数是可能的,但我正在努力找出一个简洁的答案,因为 PostGreSQL 没有
IGNORE NULLS
。
目前,这是一个笨拙的答案......
with
tbl as
(
-- From your question, but fixed by moving the `ORDER BY` into the window function
SELECT
rent.*,
row_number() over (PARTITION BY objekt_id ORDER BY start_date) as row_id
FROM
rent
),
lag_lead AS
(
-- do a naive lag and lead, not yet trying to account for nulls
-- if the result is the same as the current row, replace with NULL
-- (thus only identifying lag/lead values where the's a change)
SELECT
*,
NULLIF(LAG( person_id) over (PARTITION BY objekt_id ORDER BY start_date), person_id) AS last_person,
NULLIF(LEAD(person_id) over (PARTITION BY objekt_id ORDER BY start_date), person_id) AS next_person
FROM
tbl
),
identify_partitions AS
(
-- create groups of rows where the results should be the same
SELECT
*,
COUNT(new_last_person) OVER (PARTITION BY objekt_id ORDER BY start_date ASC) AS last_person_partition,
COUNT(new_next_person) OVER (PARTITION BY objekt_id ORDER BY start_date DESC) AS next_person_partition
FROM
lag_lead
)
SELECT
*,
MAX(new_last_person) OVER (PARTITION BY objekt_id, last_person_partition) AS real_last_person,
MAX(new_next_person) OVER (PARTITION BY objekt_id, next_person_partition) AS real_next_person
FROM
identify_partitions
ORDER BY
1;
https://dbfiddle.uk/?rdbms=postgres_10&fiddle=b613f88a730cfddcef4efb612b6e236c
在该示例中,我稍微修改了您的数据,以演示 person_id 从 X 转换为 NULL 再返回 X 时的行为。