有没有办法在满足某些条件之前选择行?即一种 limit
,但不限于 N
行,但直到第一个非匹配行的所有行?
例如,假设我有 table :
CREATE TABLE t (id SERIAL PRIMARY KEY, rank INTEGER, value INTEGER);
INSERT INTO t (rank, value) VALUES ( 1, 1), (2, 1), (2,2),(3,1);
那是:
test=# SELECT * FROM t;
id | rank | value
----+------+-------
1 | 1 | 1
2 | 2 | 1
3 | 2 | 2
4 | 3 | 1
(4 rows)
我想按等级排序,并选择直到第一行超过1 .
即 SELECT * FROM t ORDER BY rank UNTIL value>1
我想要前两行回来?
一种解决方案是使用子查询和 bool_or
:
SELECT * FROM
( SELECT id, rank, value, bool_and(value<2) OVER (order by rank, id) AS ok FROM t ORDER BY rank) t2
WHERE ok=true
但是,即使我只想要一小撮,也不会最终通过所有行?
(真实世界的背景:我在表中有时间戳事件,我可以使用窗口查询超前/滞后来选择两个事件之间的时间,我希望 now
中的所有事件都返回,只要它们发生的间隔不到10分钟 - lead/lag
窗口查询使事情变得复杂,所以这里的简化示例)
编辑:按 rank, id
制作窗口功能顺序
3 回答
这可能并不比你的解决方案好,因为你提出了一个问题,“这最终会不是通过所有行?”
我可以告诉你 - 解释计划与你的解决方案不同 . 我不知道PostgreSQL的内容是如何工作的,但如果我正在写一个“max”函数,我会认为它总是O(n) . 相比之下,您的订单是平均情况O(n log n),最差情况是O(n ^ 2) .
也就是说,我不能否认这会经历所有行:
但有一点需要澄清的是,除非您扫描所有行,否则我不确定如何确定最小值 . 每次在所有记录中调用聚合概念时,这是否意味着您必须读取所有行?
你想要的是一种停止条件 . 据我所知,SQL中没有这样的东西,至少是PostgreSQL的方言 .
您可以做的是使用PL / PgSQL过程从游标中读取行并返回它们直到满足停止条件 . 它赢了't be super fast, but it'好吧 . 对于带有
IF expression THEN exit; ELSE return next; END IF;
的查询,它只是一个FOR
循环 . 不需要显式游标,因为如果你FOR
循环查询,PL / PgSQL将在内部使用一个游标 .另一种选择是在应用程序中创建一个游标并从中读取行块,然后在满足停止条件时丢弃最后一个块的一部分 .
无论哪种方式,光标都将是您想要的 .
顺便说一下,停止表达式实际上并不太难以在PostgreSQL中实现 . 您必须实现新的执行程序节点类型,但新的CustomScan支持将使扩展中的实际操作成为可能 . 然后你只需要评估一个表达式来决定是否继续获取行 .
您可以尝试以下内容:
它将通过表的第一部分进行两次传递(您可以通过在(rank,value = 1)上创建索引来切割)但如果您有一个排名索引,则不应评估表的其余部分 .
[如果你可以在where子句中使用窗口表达式,你可以使用窗口表达式来确保任何先前的行没有value = 1 ..但即使这是可能的,那么让查询求值程序用来限制搜索将是另一个挑战 . ]