postgresql 选择字段总和小于N的元素

yiytaume  于 2023-06-29  发布在  PostgreSQL
关注(0)|答案(2)|浏览(135)

给定此表:

# select * from messages;

| 冗长| verbosity |
| - -----| ------------ |
| 二十个| 20 |
| 二十个| 20 |
| 二十个| 20 |
| 三十| 30 |
| 一百| 100 |
(5行)我想选择verbosity的总和小于N的N条消息。因此,如果N = 70,所需的结果将是id为1、2和3的消息。解决方案独立于数据库是很重要的。它至少应该在PostgreSQL和SQLite上工作。
类似于:

SELECT * FROM messages GROUP BY id HAVING SUM(verbosity) < 70;

不会对verbosity列中的所有值求和。

8oomwypt

8oomwypt1#

SELECT m.id, sum(m1.verbosity) AS total
FROM   messages m
JOIN   messages m1 ON m1.id <= m.id
WHERE  m.verbosity < 70    -- optional, to avoid pointless evaluation
GROUP  BY m.id
HAVING SUM(m1.verbosity) < 70
ORDER  BY total DESC
LIMIT  1;

这里假设id是一个唯一的升序,就像您在示例中看到的那样。
在现代Postgres中-或者通常使用现代标准SQL(但在SQLite中 * 不是 *):

简单CTE

WITH cte AS (
   SELECT *, sum(verbosity) OVER (ORDER BY id) AS total
   FROM   messages
   )
SELECT *
FROM   cte
WHERE  total < 70
ORDER  BY id;

递归CTE

对于只检索一个小集合的大表应该更快。

WITH RECURSIVE cte AS (
   (  -- parentheses required
   SELECT id, verbosity, verbosity AS total
   FROM   messages
   ORDER  BY id
   LIMIT  1
   )

   UNION ALL 
   SELECT c1.id, c1.verbosity, c.total + c1.verbosity 
   FROM   cte c
   JOIN   LATERAL (
      SELECT *
      FROM   messages
      WHERE  id > c.id
      ORDER  BY id
      LIMIT  1
      ) c1 ON  c1.verbosity < 70 - c.total
   WHERE c.total < 70
   )
SELECT *
FROM   cte
ORDER  BY id;

所有标准SQL,**LIMIT**除外。
严格地说,没有“数据库独立”这样的东西。有各种SQL标准,但没有RDBMS完全遵守。LIMIT适用于PostgreSQL和SQLite(以及其他一些)。对SQL Server使用TOP 1,对Oracle使用rownum。这是维基百科上的一个完整列表。
SQL:2008标准是:

...
FETCH  FIRST 1 ROWS ONLY

... PostgreSQL支持-但几乎没有任何其他RDBMS。
适用于更多系统的纯替代方案是将其 Package 在子查询中并

SELECT max(total) FROM <subquery>

但这是缓慢和笨拙的。

老麻雀

jv2fixgn

jv2fixgn2#

这会有用的。。

select * 
from messages
where id<=
(
    select MAX(id) from
    (
        select m2.id, SUM(m1.verbosity) sv 
        from messages m1
        inner join messages m2 on m1.id <=m2.id
        group by m2.id
    ) v
    where sv<70
)

但是,您应该了解SQL是设计为基于集合的语言,而不是迭代语言,因此它设计为将数据视为一个集合,而不是逐行处理。

相关问题