SQL 查询以获取给定日期的有效数据
SQL Query to Get Valid Data on Given Date
我有一个数据库可以随着时间的推移跟踪个人工资,如下 table 所示:
我想查询每个月的人(基于 id)薪水,以给出如下 table 的输出
我不知道使用什么查询,因为它需要在薪水数据库中迭代以检查特定日期的有效薪水是多少。
有什么想法吗?
谢谢!
这是使用横向连接的方便位置。以下是一个月的第一天而不是最后一天——因为这样更容易生成:
select i.id, gs.mon, s.salary
from generate_series('2019-01-01'::date, '2020-12-01'::date, interval '1 month') gs(mon) cross join
(select distinct id from salaries) i left join lateral
(select s.salary
from salaries s
where s.id = i.id and s.datevaliduntil >= gs.mon
order by s.datevaliduntil asc
limit 1
) s;
当然,如果你想要最后一天,你可以从每个日期中减去 1 天。
我会使用横向连接,但反过来:从 table 本身开始,将前一个日期与 lag()
一起使用,然后使用生成系列生成两者之间的日期。需要一些额外的逻辑来调整月末:
select x.date - interval '1 day' date, t.id, t.salary
from (
select id, salary,
datevaliduntil + interval '1 day' datevaliduntil,
lag(datevaliduntil, 1, datevaliduntil)
over(partition by id order by datevaliduntil) + interval '1 day' lag_datevaliduntil
from mytable t
) t
cross join lateral generate_series(
t.lag_datevaliduntil,
least(t.datevaliduntil, '2021-03-01'),
'1 month'
) x(date)
您可以使用 generate_series
的第二个参数中的文字日期来控制总体上限(此处,您想停止 2021 年 3 月结束)。
date | id | salary
:------------------ | ---: | -----:
2020-04-30 00:00:00 | 1001 | 3000
2020-04-30 00:00:00 | 1001 | 4000
2020-05-31 00:00:00 | 1001 | 4000
2020-06-30 00:00:00 | 1001 | 4000
2020-07-31 00:00:00 | 1001 | 4000
2020-08-31 00:00:00 | 1001 | 4000
2020-08-31 00:00:00 | 1001 | 5000
2020-09-30 00:00:00 | 1001 | 5000
2020-10-31 00:00:00 | 1001 | 5000
2020-11-30 00:00:00 | 1001 | 5000
2020-12-31 00:00:00 | 1001 | 5000
2021-01-31 00:00:00 | 1001 | 5000
2021-02-28 00:00:00 | 1001 | 5000
这里有所有示例数据。如前所述,您需要 valid-from-date ...
WITH
-- your input ...
indata(id,datevaliduntil,salary) AS (
SELECT 1001,DATE '9999-12-31', 5000
UNION ALL SELECT 1001,DATE '2020-08-31', 4000
UNION ALL SELECT 1001,DATE '2020-04-30', 3000
)
,
-- make it almost like a slowly changing dimension
-- table - ad a valid-from-date ...
scd AS (
SELECT
id
, LAG(datevaliduntil,1,DATE '1900-01-01') OVER (
PARTITION BY id ORDER BY datevaliduntil
) AS datevalidfrom
, datevaliduntil
, salary
FROM indata
)
,
-- the months from the example ...
months(monthend) AS (
SELECT
mon::DATE - 1 AS monthend
FROM
GENERATE_SERIES(
'2020-04-01'::DATE
, '2021-03-01'::DATE
, INTERVAL '1 MONTH'
) gs(mon)
)
SELECT
monthend
, id
, salary
FROM scd
JOIN months ON monthend > datevalidfrom
AND monthend <= datevaliduntil
ORDER BY 1
;
-- out monthend | id | salary
-- out ------------+------+--------
-- out 2020-03-31 | 1001 | 3000
-- out 2020-04-30 | 1001 | 3000
-- out 2020-05-31 | 1001 | 4000
-- out 2020-06-30 | 1001 | 4000
-- out 2020-07-31 | 1001 | 4000
-- out 2020-08-31 | 1001 | 4000
-- out 2020-09-30 | 1001 | 5000
-- out 2020-10-31 | 1001 | 5000
-- out 2020-11-30 | 1001 | 5000
-- out 2020-12-31 | 1001 | 5000
-- out 2021-01-31 | 1001 | 5000
-- out 2021-02-28 | 1001 | 5000
我有一个数据库可以随着时间的推移跟踪个人工资,如下 table 所示:
我想查询每个月的人(基于 id)薪水,以给出如下 table 的输出
我不知道使用什么查询,因为它需要在薪水数据库中迭代以检查特定日期的有效薪水是多少。 有什么想法吗?
谢谢!
这是使用横向连接的方便位置。以下是一个月的第一天而不是最后一天——因为这样更容易生成:
select i.id, gs.mon, s.salary
from generate_series('2019-01-01'::date, '2020-12-01'::date, interval '1 month') gs(mon) cross join
(select distinct id from salaries) i left join lateral
(select s.salary
from salaries s
where s.id = i.id and s.datevaliduntil >= gs.mon
order by s.datevaliduntil asc
limit 1
) s;
当然,如果你想要最后一天,你可以从每个日期中减去 1 天。
我会使用横向连接,但反过来:从 table 本身开始,将前一个日期与 lag()
一起使用,然后使用生成系列生成两者之间的日期。需要一些额外的逻辑来调整月末:
select x.date - interval '1 day' date, t.id, t.salary
from (
select id, salary,
datevaliduntil + interval '1 day' datevaliduntil,
lag(datevaliduntil, 1, datevaliduntil)
over(partition by id order by datevaliduntil) + interval '1 day' lag_datevaliduntil
from mytable t
) t
cross join lateral generate_series(
t.lag_datevaliduntil,
least(t.datevaliduntil, '2021-03-01'),
'1 month'
) x(date)
您可以使用 generate_series
的第二个参数中的文字日期来控制总体上限(此处,您想停止 2021 年 3 月结束)。
date | id | salary :------------------ | ---: | -----: 2020-04-30 00:00:00 | 1001 | 3000 2020-04-30 00:00:00 | 1001 | 4000 2020-05-31 00:00:00 | 1001 | 4000 2020-06-30 00:00:00 | 1001 | 4000 2020-07-31 00:00:00 | 1001 | 4000 2020-08-31 00:00:00 | 1001 | 4000 2020-08-31 00:00:00 | 1001 | 5000 2020-09-30 00:00:00 | 1001 | 5000 2020-10-31 00:00:00 | 1001 | 5000 2020-11-30 00:00:00 | 1001 | 5000 2020-12-31 00:00:00 | 1001 | 5000 2021-01-31 00:00:00 | 1001 | 5000 2021-02-28 00:00:00 | 1001 | 5000
这里有所有示例数据。如前所述,您需要 valid-from-date ...
WITH
-- your input ...
indata(id,datevaliduntil,salary) AS (
SELECT 1001,DATE '9999-12-31', 5000
UNION ALL SELECT 1001,DATE '2020-08-31', 4000
UNION ALL SELECT 1001,DATE '2020-04-30', 3000
)
,
-- make it almost like a slowly changing dimension
-- table - ad a valid-from-date ...
scd AS (
SELECT
id
, LAG(datevaliduntil,1,DATE '1900-01-01') OVER (
PARTITION BY id ORDER BY datevaliduntil
) AS datevalidfrom
, datevaliduntil
, salary
FROM indata
)
,
-- the months from the example ...
months(monthend) AS (
SELECT
mon::DATE - 1 AS monthend
FROM
GENERATE_SERIES(
'2020-04-01'::DATE
, '2021-03-01'::DATE
, INTERVAL '1 MONTH'
) gs(mon)
)
SELECT
monthend
, id
, salary
FROM scd
JOIN months ON monthend > datevalidfrom
AND monthend <= datevaliduntil
ORDER BY 1
;
-- out monthend | id | salary
-- out ------------+------+--------
-- out 2020-03-31 | 1001 | 3000
-- out 2020-04-30 | 1001 | 3000
-- out 2020-05-31 | 1001 | 4000
-- out 2020-06-30 | 1001 | 4000
-- out 2020-07-31 | 1001 | 4000
-- out 2020-08-31 | 1001 | 4000
-- out 2020-09-30 | 1001 | 5000
-- out 2020-10-31 | 1001 | 5000
-- out 2020-11-30 | 1001 | 5000
-- out 2020-12-31 | 1001 | 5000
-- out 2021-01-31 | 1001 | 5000
-- out 2021-02-28 | 1001 | 5000