【问题标题】:How to transform this data in PostgreSQL如何在 PostgreSQL 中转换这些数据
【发布时间】:2021-04-12 19:56:40
【问题描述】:
如何转换这些数据
| Date1 |
Date2 |
ID |
| 2021-01-01 |
2021-01-04 |
1 |
| 2021-01-05 |
2021-01-25 |
2 |
进入这个
| Date |
ID |
| 2021-01-01 |
1 |
| 2021-01-02 |
1 |
| 2021-01-03 |
1 |
| 2021-01-04 |
1 |
| 2021-01-05 |
2 |
| 2021-01-06 |
2 |
| ... |
... |
| 2021-01-24 |
2 |
| 2021-01-25 |
2 |
我正在使用 Postgres,直觉告诉我在 Postgres 中使用 generate_series 函数,但我不确定实现。任何帮助表示赞赏。
【问题讨论】:
标签:
sql
postgresql
date
common-table-expression
【解决方案1】:
你的直觉是正确的,日历表方法应该在这里工作:
WITH dates AS (
SELECT date_trunc('day', dt)::date AS dt
FROM generate_series (
'2021-01-01'::timestamp,
'2021-01-31'::timestamp,
'1 day'::interval) dt
)
SELECT d.dt AS Date, t.ID
FROM dates d
LEFT JOIN yourTable t
ON d.dt BETWEEN t.Date1 AND t.Date2;
Demo
【解决方案2】:
因为我们可能无法提前知道开始日期和结束日期的界限。
这是一种方法
WITH yourTable AS (
SELECT '2021-01-01'::date AS Date1, '2021-01-04'::date AS Date2, 1 AS ID
UNION ALL
SELECT '2021-01-05'::date, '2021-01-25'::date, 2
)
select x.date1 + (lvl-1)*INTERVAL '1 day'
,x.id
from yourTable x
join generate_series(1,date2-date1+1) as lvl
on 1=1
db fiddle
https://dbfiddle.uk/?rdbms=postgres_9.6&fiddle=3ef42eb613422c148b76561522d33a55
+---------------------+----+
| date1 | id |
+---------------------+----+
| 2021-01-01 00:00:00 | 1 |
| 2021-01-02 00:00:00 | 1 |
| 2021-01-03 00:00:00 | 1 |
| 2021-01-04 00:00:00 | 1 |
| 2021-01-05 00:00:00 | 2 |
| 2021-01-06 00:00:00 | 2 |
| 2021-01-07 00:00:00 | 2 |
| 2021-01-08 00:00:00 | 2 |
| 2021-01-09 00:00:00 | 2 |
| 2021-01-10 00:00:00 | 2 |
| 2021-01-11 00:00:00 | 2 |
| 2021-01-12 00:00:00 | 2 |
| 2021-01-13 00:00:00 | 2 |
| 2021-01-14 00:00:00 | 2 |
| 2021-01-15 00:00:00 | 2 |
| 2021-01-16 00:00:00 | 2 |
| 2021-01-17 00:00:00 | 2 |
| 2021-01-18 00:00:00 | 2 |
| 2021-01-19 00:00:00 | 2 |
| 2021-01-20 00:00:00 | 2 |
| 2021-01-21 00:00:00 | 2 |
| 2021-01-22 00:00:00 | 2 |
| 2021-01-23 00:00:00 | 2 |
| 2021-01-24 00:00:00 | 2 |
| 2021-01-25 00:00:00 | 2 |
+---------------------+----+
【解决方案3】:
我强烈建议使用横向连接和generate_series():
select gs.date, t.id
from t cross join lateral
generate_series(t.date1, t.date2, interval '1 day') gs(date);
Here 是一个 dbfiddle。
为什么我推荐这个而不是left join?基本上,generate_series() 一次在一行上运行。所以,执行计划基本上应该是“读取一行,生成对应的行”。
使用left join,Postgres 必须实际匹配所有行中的数据,而且该操作的成本要高得多。