我有一个 TIMESTAMP 列:
dates
2021-06-24 05:47:05
2021-06-24 09:47:05
2021-06-24 13:47:05
2021-06-24 17:47:05
我想选择在同一天的下一个时间戳之前 3 小时或更长时间的给定日期的第一个时间戳。
expected output:
2021-06-24 05:47:05
但是,如果没有时间戳比任何其他时间戳(在给定日期)早 3 小时以上,则应返回当天的最后一个时间戳。
我有一个 TIMESTAMP 列:
dates
2021-06-24 05:47:05
2021-06-24 09:47:05
2021-06-24 13:47:05
2021-06-24 17:47:05
我想选择在同一天的下一个时间戳之前 3 小时或更长时间的给定日期的第一个时间戳。
expected output:
2021-06-24 05:47:05
但是,如果没有时间戳比任何其他时间戳(在给定日期)早 3 小时以上,则应返回当天的最后一个时间戳。
这是一个完全修改过的答案,比前一个更有效。旧答案可以通过查看编辑历史或作为本文底部的脚注来查看。
下面的所有代码的小提琴都可以在这里的小提琴中找到。
所以,我们有我们的测试表:
填充它 - 添加一天的记录,没有间隔 > 3 小时:
并且(演示逻辑)然后运行以下 SQL:
结果:
我们已经使用了
LEAD()
窗口函数。窗口功能非常强大,我强烈建议您花一些精力来学习如何使用它们 - 他们会多次回报您的努力!the_date
它根据中的标准提供了 的值和它后面的值之间的比较- 您可以通过改变函数本身的子句ORDER BY
来做很多聪明的事情- 可以在这里看到改变其他参数。ORDER BY
LEAD()
该
PARTITION BY the_date::DATE
子句是为数据集中的每个日期提供单独的结果。特别注意 NULL - 由于分区,您不能拥有跨越数天的 LEAD,因此任何给定日期的最后一个时间戳的 LEAD 值将始终是NULL
- 这与要求有关 - 见下文。另外,请注意
NULL
减去任何东西NULL
(NULL
加号相同) - 我们说NULL
s “传播”。所以,现在我们运行这个 SQL:
结果:
想要的结果!但是,这是怎么回事?从这里:
或者,换一种方式(来自同一个链接):
或者从这里的 PostgreSQL 文档:
如您所见,这(如窗口函数)显然是 PostgreSQL 程序员武器库中非常强大的工具,非常值得花时间和精力去学习。
一个有趣的替代方法是使用
ROW_NUMBER()
窗口函数,如果你想要前两个间隙或最后一个记录,如下所示:结果:
请注意,我们现在有 2021-06-24 的两条记录。
最后,仅作记录,原始解决方案:
结果:
小提琴底部给出了 3 种解决方案的性能分析 - 它表明该
DISTINCT ON
解决方案的性能明显优于其他解决方案 - 但是ROW_NUMBER()
有可能更加灵活!然而,警告一句——在我们无法控制的服务器上对一个非常小的数据集进行性能分析,也不知道其他地方发生了什么,这可能存在缺陷——我建议您在自己的硬件上使用合理的数据集进行基准测试。将来,当您提出这种性质的问题时,您能否提供一个涵盖所有案例的样本数据 - 即在这种情况下,哪些地方有差距,哪些地方没有。这减少了出错的可能性并消除了重复劳动——帮助我们为您提供帮助。另外,请始终包含您的 PostgreSQL 版本。