几天前,我在 Facebook 上发现了一个用户的问题:如何从一个表中选择一个事件,它们之间的间隔不超过 10 分钟?
这篇文章我试图用一个例子来表示我是如何理解和解决上述问题的。
首先,让我们建立一个事件表,其中每一行都有一个时间戳。
testdb=> CREATE TABLE events(
pk int generated always as identity
, event text
, ts timestamp default CURRENT_TIMESTAMP
, PRIMARY KEY( pk )
);
CREATE TABLE
复制
现在,让我们用“随机”数据填充表格,以便在2 minutes每个范围内传播事件:
testdb=> insert into events( event, ts )
select 'event #' || v, current_timestamp - ( ( v * 2 ) || ' minutes' )::interval
from generate_series( 1, 100 ) v;
INSERT 0 100
复制
设置好表格和数据后,我们如何将每个元组与不在十分钟窗口之外的其他事件相关联?LATERAL来救援。
testdb=> SELECT e1.pk, e1.event, e2.*
FROM events e1,
LATERAL ( SELECT pk, event, ts - e1.ts as time_elapsed
FROM events
WHERE pk <> e1.pk AND e1.ts - ts <= '10 minutes'::interval ) e2
ORDER BY e1.pk LIMIT 20;
pk | event | pk | event | time_elapsed
-----+----------+-----+----------+--------------
501 | event #1 | 502 | event #2 | -00:02:00
501 | event #1 | 503 | event #3 | -00:04:00
501 | event #1 | 504 | event #4 | -00:06:00
501 | event #1 | 505 | event #5 | -00:08:00
501 | event #1 | 506 | event #6 | -00:10:00
502 | event #2 | 501 | event #1 | 00:02:00
502 | event #2 | 503 | event #3 | -00:02:00
502 | event #2 | 504 | event #4 | -00:04:00
502 | event #2 | 505 | event #5 | -00:06:00
502 | event #2 | 506 | event #6 | -00:08:00
502 | event #2 | 507 | event #7 | -00:10:00
503 | event #3 | 501 | event #1 | 00:04:00
503 | event #3 | 502 | event #2 | 00:02:00
503 | event #3 | 504 | event #4 | -00:02:00
503 | event #3 | 505 | event #5 | -00:04:00
503 | event #3 | 506 | event #6 | -00:06:00
503 | event #3 | 507 | event #7 | -00:08:00
503 | event #3 | 508 | event #8 | -00:10:00
504 | event #4 | 501 | event #1 | 00:06:00
504 | event #4 | 502 | event #2 | 00:04:00
(20 rows)
复制
让我们反汇编查询,看看它是如何工作的。子查询选择在一个10 minutes范围内并且与系统当前正在评估的查询(即e1.pk)不同的所有元组。但通常子查询会为外部查询评估一次,但请注意,子查询以 前缀LATERAL,简单来说,意味着评估外部结果集的每一行的子查询。这意味着LATERAL子查询可以访问外部查询行,并且可以对自己的结果集进行“推理”。
在处理时要记住的一件重要事情LATERAL是子查询必须使用别名引用,在我的例子中e2。请注意,在LATERAL子查询我确实计算了外部元组的时间戳和内部结果集的时间戳之间的时间差,正如您从输出列中看到的,time_elapsed每行相差 2 分钟,这就是我们生成行的方式。
如果不使用会发生什么LATERAL?好吧,您不能引用e1外部元组,即子查询无法交叉引用其范围之外的内容:
testdb=> SELECT e1.pk, e1.event, e2.*
FROM events e1,
( SELECT pk, event, ts - e1.ts as time_elapsed
FROM events WHERE pk <> e1.pk AND e1.ts - ts <= '10 minutes'::interval ) e2
ORDER BY e1.pk LIMIT 20;
ERROR: invalid reference to FROM-clause entry for table "e1"
LINE 1: ..., e2.* FROM events e1, ( SELECT pk, event, ts - e1.ts as t...
^
HINT: There is an entry for table "e1", but it cannot be referenced from this part of the query.
复制
如您所见,PostgreSQL 明确指出您不能e1从子查询的范围内引用(外部元组)。
LATERAL 加入
当然,可以LATERAL在连接中使用,在这种情况下,上面的查询可以重写为:
testdb=> SELECT e1.pk, e1.event, e2.*
FROM events e1 JOIN LATERAL
( SELECT pk, event, ts - e1.ts as time_elapsed
FROM events WHERE pk <> e1.pk AND e1.ts - ts <= '10 minutes'::interval ) e2
ON true
ORDER BY e1.pk LIMIT 20;
pk | event | pk | event | time_elapsed
-----+----------+-----+----------+--------------
501 | event #1 | 502 | event #2 | -00:02:00
501 | event #1 | 503 | event #3 | -00:04:00
501 | event #1 | 504 | event #4 | -00:06:00
501 | event #1 | 505 | event #5 | -00:08:00
501 | event #1 | 506 | event #6 | -00:10:00
502 | event #2 | 501 | event #1 | 00:02:00
502 | event #2 | 503 | event #3 | -00:02:00
502 | event #2 | 504 | event #4 | -00:04:00
502 | event #2 | 505 | event #5 | -00:06:00
502 | event #2 | 506 | event #6 | -00:08:00
502 | event #2 | 507 | event #7 | -00:10:00
503 | event #3 | 501 | event #1 | 00:04:00
503 | event #3 | 502 | event #2 | 00:02:00
503 | event #3 | 504 | event #4 | -00:02:00
503 | event #3 | 505 | event #5 | -00:04:00
503 | event #3 | 506 | event #6 | -00:06:00
503 | event #3 | 507 | event #7 | -00:08:00
503 | event #3 | 508 | event #8 | -00:10:00
504 | event #4 | 501 | event #1 | 00:06:00
504 | event #4 | 502 | event #2 | 00:04:00
(20 rows)
复制
结论
LATERAL 是 PostgreSQL 中非常强大的 SQL 运算符,可以帮助解决通常通过游标和迭代解决的问题。
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。
评论
相关阅读
王炸!OGG 23ai 终于支持从PostgreSQL备库抽取数据了
曹海峰
414次阅读
2025-03-09 12:54:06
玩一玩系列——玩玩login_hook(一款即将停止维护的PostgreSQL登录插件)
小满未满、
380次阅读
2025-03-08 18:19:28
明明想执行的SQL是DELETE、UPDATE,但为什么看到的是SELECT(FDW的实现原理解析)
小满未满、
361次阅读
2025-03-19 23:11:26
PostgreSQL初/中/高级认证考试(3.15)通过考生公示
开源软件联盟PostgreSQL分会
314次阅读
2025-03-20 09:50:36
IvorySQL 4.4 发布 - 基于 PostgreSQL 17.4,增强平台支持
通讯员
204次阅读
2025-03-20 15:31:04
套壳论
梧桐
201次阅读
2025-03-09 10:58:17
命名不规范,事后泪两行
xiongcc
188次阅读
2025-03-13 14:26:08
PG vs MySQL 执行计划解读的异同点
进击的CJR
128次阅读
2025-03-21 10:50:08
版本发布| IvorySQL 4.4 发布
IvorySQL开源数据库社区
120次阅读
2025-03-13 09:52:33
宝藏PEV,助力你成为SQL优化高手
xiongcc
116次阅读
2025-03-09 23:34:23