SQLを選択すると、フィールドの合計がnより小さい
-
12-12-2019 - |
質問
次のようなテーブルを持っていること、非常に単純なコンテンツ:
# select * from messages;
id | verbosity
----+-----------
1 | 20
2 | 20
3 | 20
4 | 30
5 | 100
(5 rows)
.
私はN個のメッセージを選択したい、どのverboityの合計はyより低い(テスト目的のためにそれが70になるべきであると言うと、正しい結果はID 1,2,3のメッセージになります)。 その解決策はデータベースに依存しないはずです(少なくともPostgresとSQLiteで動作する必要があります)。
私は次のようなもので試していました:
SELECT * FROM messages GROUP BY id HAVING SUM(verbosity) < 70;
.
しかしそれは実際には冗長列からのすべての値を合計しないので、予想通りに動作していないようです。
私はあらゆるヒント/ヘルプに非常に感謝します。
解決
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;
.
LIMIT
を除くすべての標準機能。
厳密に言えば、「データベースに依存しない」ということはありません。さまざまなSQL規格がありますが、RDBMSは完全に準拠していません。 LIMIT
はPostgreSQLとSQLite(そして他の何人か)に機能します。 Oracle用のTOP 1
for SQL Server用のrownum
を使用します。これは、ウィキペディアの包括的なリストです。
SQL:2008標準は次のとおりです。
...
FETCH FIRST 1 ROWS ONLY
.
...どのPostgreSQLがサポートしています - 他のRDBMSはほとんどありません。
より多くのシステムで動作する純粋な代替案は、それを副照会で包み、
を描くことです。SELECT max(total) FROM <subquery>
.
しかしそれは遅く、扱いにくいです。
他のヒント
これはうまくいきます...
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は、反復的なものではなくセットに基づく言語として設計されているので、行ごとに1行ではなくセットとしてデータを扱うように設計されています。