SQL-Auswahlelemente, bei denen die Summe der Felder kleiner als N ist
-
12-12-2019 - |
Frage
Vorausgesetzt, ich habe eine Tabelle mit folgendem, sehr einfachem Inhalt:
# select * from messages;
id | verbosity
----+-----------
1 | 20
2 | 20
3 | 20
4 | 30
5 | 100
(5 rows)
Ich möchte N Nachrichten auswählen, deren Ausführlichkeitssumme niedriger als Y ist (zu Testzwecken sagen wir, es sollte 70 sein, dann sind korrekte Ergebnisse Nachrichten mit der ID 1,2,3).Es ist mir wirklich wichtig, dass die Lösung datenbankunabhängig ist (sie sollte zumindest auf Postgres und SQLite funktionieren).
Ich habe es mit so etwas versucht:
SELECT * FROM messages GROUP BY id HAVING SUM(verbosity) < 70;
Es scheint jedoch nicht wie erwartet zu funktionieren, da nicht alle Werte aus der Ausführlichkeitsspalte summiert werden.
Für Hinweise / Hilfe wäre ich sehr dankbar.
Lösung
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;
Dies setzt eine eindeutige, aufsteigende id
so wie du es in deinem Beispiel getan hast.
In modernen Postgres - oder generell mit modernes Standard-SQL (aber nicht in SQLite):
Einfacher WAK
WITH cte AS (
SELECT *, sum(verbosity) OVER (ORDER BY id) AS total
FROM messages
)
SELECT *
FROM cte
WHERE total <= 70
ORDER BY id;
Rekursiver WAK
Sollte für große Tische schneller sein, an denen Sie nur einen kleinen Satz abrufen.
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;
Alle Standardfunktionen, außer LIMIT
.
Streng genommen gibt es so etwas wie "datenbankunabhängig" nicht.Es gibt verschiedene SQL-Standards, aber kein RDBMS entspricht vollständig. LIMIT
funktioniert für PostgreSQL und SQLite (und einige andere).Verwenden TOP 1
für SQL Server, rownum
für Oracle.Hier ist ein umfassende Liste auf Wikipedia.
Der SQL: Standard 2008 wäre:
...
FETCH FIRST 1 ROWS ONLY
...welches PostgreSQL unterstützt - aber kaum ein anderes RDBMS.
Die reine Alternative, die mit mehr Systemen funktioniert, wäre, sie in eine Unterabfrage einzuschließen und
SELECT max(total) FROM <subquery>
Aber das ist langsam und unhandlich.
Andere Tipps
Das wird funktionieren ...
generasacodicetagpre.Sie sollten jedoch verstehen, dass SQL als festgelegte Set-basierte Sprache, anstatt ein iterativer, so konzipiert ist, so dass es so ausgelegt ist, dass Daten als Set als Set, nicht auf einer Reihe von Zeilenbasis, behandelt.