我从数据库中选择句点列表。如果当前行是第一行,那么期间从日期开始,我可以找到期间开始之间的间隔,如下所示:
SELECT
...
CASE WHEN row_number() OVER(ORDER BY r.created_at ASC) = 1 THEN r.created_at - r.created_at::date ELSE NULL END AS period
...
FROM mytable r
如何对最后行执行相同的操作?要查找最后一行的r.created_at与其日期的午夜之间的时间。
我知道PostgreSQL中的first
和last
函数(https://wiki.postgresql.org/wiki/First/last_(aggregate)),但它们是聚合函数,在这种情况下没有帮助。
编辑: 这个问题有两个很好的答案。在我的情况下,它们都没有帮助,因为我作为我的问题的一部分提出的这一行是更大查询的一部分,以编程方式组合在一起并使用提供的解决方案将迫使我改变很多代码,我不愿意这样做这点。如果缩放问题受到影响 - 那么我肯定会重新考虑。
答案 0 :(得分:1)
这可能比窗口函数更快:
with r as (
select
min(created_at) as min_created_at,
max(created_at) as max_created_at
from mytable
)
select
case when (select min_created_at from r) = created_at
then created_at - created_at::date else null
end as period_min,
case when (select max_created_at from r) = created_at
then created_at - created_at::date else null
end as period_max
from mytable
答案 1 :(得分:1)
您可以在单 CASE
声明中使用window functions first_value()
and last_value()
:
SELECT *
, CASE WHEN ts IN ( first_value(created_at) OVER w
, last_value(created_at) OVER w)
THEN created_at::time::interval ELSE NULL END AS period
FROM tbl
WINDOW w AS (ORDER BY created_at rows BETWEEN UNBOUNDED PRECEDING
AND UNBOUNDED FOLLOWING);
此处有特殊要求:您需要调整last_value()
来电的frame definition。默认情况下是:
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
但你需要:
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
first_value()
调用适用于默认框架,但也可以使用相同的框架。
我还简化了period
的计算。您的定义与time
的{{1}}组件相吻合,只需投放到timestamp.
以“截断”日期部分:time
。
在此之后转换为created_at::time
只是为了返回与原始查询相同的数据类型。
由于窗口函数的当前实现,结果将由interval
自动排序。但不要依赖于此。如果您需要排序输出,请明确添加到结尾:
created_at