有没有办法在满足某些条件之前选择行?即一种limit
但不限于N
行,但是直到第一个不匹配行的所有行?
例如,假设我有桌子:
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
答案 0 :(得分:1)
这可能不比你的解决方案好,因为你提出了这个问题,&#34;最终不会通过所有行?&#34;
我可以告诉你 - 解释计划与你的解决方案不同。我不知道PostgreSQL的内容是如何运作的,但是如果我正在写一个&#34; max&#34;功能,我认为它永远是O(n)。相比之下,您的订单是平均情况O(n log n),最差情况是O(n ^ 2)。
那就是说,我不能否认这会经历所有行:
select * from sandbox.t
where id < (select min (id) from sandbox.t where value > 1)
但要澄清的一点是,除非您扫描所有行,否则我不确定如何确定最小值。每当你在所有记录中调用聚合概念时,这是不是意味着你必须阅读所有行?
答案 1 :(得分:1)
你想要的是一种停止条件。据我所知,SQL中没有这样的东西,至少是PostgreSQL的方言。
您可以使用PL / PgSQL过程从游标中读取行并返回它们直到满足停止条件。它不会超快,但它会没事的。对于FOR
的查询,它只是一个IF expression THEN exit; ELSE return next; END IF;
循环。不需要显式游标,因为如果FOR
循环查询,PL / PgSQL将在内部使用它。
另一种选择是在应用程序中创建一个游标并从中读取行块,然后在满足停止条件后丢弃最后一个块的一部分。
无论哪种方式,光标都将是你想要的。
顺便说一下,停止表达式实际上并不太难以在PostgreSQL中实现。您必须实现新的执行程序节点类型,但新的CustomScan支持将使扩展中的实际操作成为可能。然后你只需要评估一个表达式来决定是否继续获取行。
答案 2 :(得分:0)
您可以尝试以下内容:
select * from t, (
select rank from t where value = 1 order by "rank" limit 1) x
where t.rank <= x.rank order by rank;
它将通过表的第一部分进行两次传递(您可以通过在(rank,value = 1)上创建索引来切割)但如果您有一个表,则不应评估表的其余部分排名指数。
[如果你可以在where子句中使用窗口表达式,你可以使用窗口表达式来确保任何先前的行没有值= 1 ..但即使这是可能的,那么让查询赋值器用来限制搜索将是另一项挑战。]