选择直到postgresql中的行匹配?

时间:2015-06-17 19:43:15

标签: postgresql window-functions

有没有办法在满足某些条件之前选择行?即一种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

制作窗口功能顺序

3 个答案:

答案 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 ..但即使这是可能的,那么让查询赋值器用来限制搜索将是另一项挑战。]