Cmd*_*ozy 5 sql postgresql date-range generate-series
我正在尝试使用PostgreSQL 9.2.4编写一个复杂的查询,但我无法正常工作.我有一个包含时间范围的表,以及其他几个列.当我在此表中存储数据时,如果所有列都相同且时间范围重叠或相邻,我将它们组合成一行.
但是当我检索它们时,我想在天边界处分割范围 - 例如:
2013-01-01 00:00:00 to 2013-01-02 23:59:59
Run Code Online (Sandbox Code Playgroud)
将被选为两行:
2013-01-01 00:00:00 to 2013-01-01 23:59:59
2013-01-02 00:00:00 to 2013-01-02 23:59:59
Run Code Online (Sandbox Code Playgroud)
对于两个检索到的条目,其他列中的值相同.
我已经看到这个问题似乎或多或少地解决了我想要的问题,但它适用于PostgreSQL的"非常旧"版本,所以我不确定它是否仍然适用.
我也看到了这个问题,它完全符合我的要求,但据我所知,该CONNECT BY
语句是SQL标准的Oracle扩展,所以我不能使用它.
我相信我可以使用PostgreSQL来实现这一点generate_series
,但我希望有一个简单的例子可以证明它是如何用来做到这一点的.
这是我目前正在处理的查询,目前无效(因为我无法FROM
在连接的子查询中引用该表),但我相信这或多或少是正确的轨道.
更新:我刚刚发现一个有趣的事实,感谢这个问题,如果你SELECT
在查询的一部分中使用set-returning函数,PostgreSQL将"自动"在集合和行上进行交叉连接.我想我已接近完成这项工作.
首先,你的上边界概念被打破了.时间戳23:59:59
并不好.数据类型timestamp
具有小数位数.怎么样2013-10-18 23:59:59.123::timestamp
?
包括下边框并在逻辑中的任何位置排除上边框.相比:
在此前提的基础上:
SELECT id
, stime
, etime
FROM timesheet_entries t
WHERE etime <= stime::date + 1 -- this includes upper border 00:00
UNION ALL
SELECT id
, CASE WHEN stime::date = d THEN stime ELSE d END -- AS stime
, CASE WHEN etime::date = d THEN etime ELSE d + 1 END -- AS etime
FROM (
SELECT id
, stime
, etime
, generate_series(stime::date, etime::date, interval '1d')::date AS d
FROM timesheet_entries t
WHERE etime > stime::date + 1
) sub
ORDER BY id, stime;
Run Code Online (Sandbox Code Playgroud)
或者干脆:
SELECT id
, CASE WHEN stime::date = d THEN stime ELSE d END -- AS stime
, CASE WHEN etime::date = d THEN etime ELSE d + 1 END -- AS etime
FROM (
SELECT id
, stime
, etime
, generate_series(stime::date, etime::date, interval '1d')::date AS d
FROM timesheet_entries t
) sub
ORDER BY id, stime;
Run Code Online (Sandbox Code Playgroud)
更简单的可能更快.
注意角落情况的差异何时stime
和etime
两者00:00
完全相同.然后在末尾添加具有零时间范围的行.有各种方法可以解决这个问题.我提议:
SELECT *
FROM (
SELECT id
, CASE WHEN stime::date = d THEN stime ELSE d END AS stime
, CASE WHEN etime::date = d THEN etime ELSE d + 1 END AS etime
FROM (
SELECT id
, stime
, etime
, generate_series(stime::date, etime::date, interval '1d')::date AS d
FROM timesheet_entries t
) sub1
ORDER BY id, stime
) sub2
WHERE etime <> stime;
Run Code Online (Sandbox Code Playgroud)
在Postgres 9.3+中你最好用LATERAL
它
SELECT id
, CASE WHEN stime::date = d THEN stime ELSE d END AS stime
, CASE WHEN etime::date = d THEN etime ELSE d + 1 END AS etime
FROM timesheet_entries t
, LATERAL (SELECT d::date
FROM generate_series(t.stime::date, t.etime::date, interval '1d') d
) d
ORDER BY id, stime;
Run Code Online (Sandbox Code Playgroud)
手册中的详细信息.
与上述相同的角落情况.
SQL Fiddle演示了所有.
归档时间: |
|
查看次数: |
5673 次 |
最近记录: |