Wählen Sie alle Zeilen mit einem Mindestwert aus

9

In SQLite 3 versuche ich herauszufinden, wie Zeilen basierend auf einem Mindestwert ausgewählt werden. Ich denke, dass ich eingeschränkt bin, weil ich nicht genug über die verwandte Terminologie weiß, um Google effektiv zu durchsuchen.

Der Tisch sieht aus wie:

num         text        num2      
----------  ----------  ----------
0           a           1         
0           a           2         
1           a           3         
1           b           4         

Ich möchte die Zeilen bekommen, wo num2ist 1, 2, und 4. Ich möchte die Auswahl basierend auf dem Mindestwert von num für jeden eindeutigen Wert der Textspalte vornehmen.

Also, für text = 'a', der Mindestwert von numist 0, also möchte ich die Zeilen 1 und 2. Für text = 'b', der Mindestwert von numist 1, also möchte ich Zeile 4.

Mit verschiedenen Kombinationen von Gruppieren nach kann ich entweder Zeilen 1und / 2oder Zeilen 1und erhalten 4. Ich habe das Gefühl, dass mir eine SQL-Komponente fehlt, die das tut, was ich will, aber ich konnte nicht herausfinden, was es sein könnte.

Was ist der richtige Weg, um diese Art von Abfrage durchzuführen?

Mögliche Lösung

Ich habe einen Weg gefunden, dies zu tun. Ich bin nicht seriös genug, um meine eigene Frage zu beantworten, daher mache ich das Update hier. Ich bin mir nicht sicher, ob es immer richtig ist oder wie die Effizienz ist. Kommentare sind willkommen.

Ich habe eine zusammengesetzte select-Anweisung verwendet, bei der eine Abfrage den Mindestwert von num für jeden eindeutigen Textwert ermittelt:

sqlite> select num, text from t group by text having num = min( num );
num         text      
----------  ----------
0           a         
1           b         

Dann habe ich dies mit der vollständigen Tabelle verbunden, um alle Zeilen zu erhalten, die diesen beiden Spalten entsprechen.

sqlite> with u as
      ( select num, text from t group by text having num = min( num ) )
        select t.* from t join u on t.num = u.num and t.text = u.text;
num         text        num2      
----------  ----------  ----------
0           a           1         
0           a           2         
1           b           4         
user35292
quelle

Antworten:

10

Wie Sie gesehen haben, funktioniert eine einfache GROUP BY nicht, da nur ein Datensatz pro Gruppe zurückgegeben wird.

Ihr Join funktioniert einwandfrei. Bei einer großen Tabelle ist dies nur dann effizient, wenn für die Verknüpfungsspalten ( numund text) ein Index vorhanden ist .

Alternativ können Sie eine korrelierte Unterabfrage verwenden:

SELECT *
FROM t
WHERE num = (SELECT MIN(num)
             FROM t AS t2
             WHERE t2.text = t.text);

SQLFiddle

Bei der Ausführung erfordert diese Abfrage keine temporäre Tabelle (Ihre Abfrage für das Ergebnis von u), sondern führt die Unterabfrage für jeden Datensatz in aus tund textsollte daher indiziert werden. (Oder verwenden Sie einen Index für beide textund num, um einen Deckungsindex zu erhalten .)

CL.
quelle
Er hat keine temporäre Tabelle in seiner Abfrage, nur einen CTE, was ganz anders ist.
Ypercubeᵀᴹ
Bei der Ausführung wird das Ergebnis der uAbfrage in einer temporären Tabelle gespeichert, unabhängig davon, ob es als CTE, Ansicht oder Inline als Unterabfrage geschrieben wurde.
CL.
Danke, diese Version ist viel einfacher zu schreiben als die, in die ich gestolpert bin. Die richtige Terminologie zu kennen, ist auch hilfreich für mich, um dies genauer zu untersuchen.
user35292
@CL Führt SQLite so Abfragen mit CTEs aus? Haben Sie eine Referenz dafür? Weil andere DBMS nicht unbedingt temporäre Tabellen für ctes verwenden.
Ypercubeᵀᴹ
@ypercube CTEs, Ansichten und Unterabfragen werden nach Möglichkeit reduziert oder als Coroutinen implementiert. Ein GROUP BY in einer nicht indizierten Spalte muss jedoch in der Lage sein, die Daten für alle Gruppen parallel zu erfassen, sodass eine temporäre Tabelle (in allen Datenbanken) erforderlich ist.
CL.
1

Ich neige dazu, solche Dinge mit einer äußeren Selbstverbindung zu tun:

SELECT
    M1.Num,
    M1.Text,
    M1.Num2
FROM
    MyDb M1
LEFT OUTER JOIN
    MyDB M2
ON
    M1.text = M2.text
AND
    M1.num > m2.num
WHERE
    M2.num is null

Das heißt im Grunde; Geben Sie mir alle Datensätze, die keinen höheren Wert haben, dh null.

KUSS
quelle
1

Wie können Sie die Antwort auf Ihre Frage beim nächsten Mal selbst finden? Meiner Meinung nach geschieht dies durch Zerlegen und Befolgen der Logik. Und das hast du richtig verstanden:

Ich möchte die Auswahl basierend auf dem Mindestwert von num für jeden eindeutigen Wert der Textspalte vornehmen

Dies bedeutet:

select text, min(num) from t group by text;

(Dies sollte Ihrer havingAbfrage entsprechen. Es könnte interessant sein, sich die Zeilen anzusehen, in denen numNULL gleich ist. Genauer gesagt: Sehen Sie sich an, welche Auswirkungen Zeilen mit Nullen haben, die Sie möglicherweise zuerst mit a herausfiltern möchten. where num is not null)

Von hier aus können Sie Ihr gewünschtes Ergebnis erzielen, indem Sie:

select * from t where (num, text) in ( *insert query above* )

Oder mit einem Join:

select t1.* from t t1,
    (select text, min(num) as n from t group by text) t2
where t1.num = t2.n and t1.text = t2.text.

Wenn die Leistung Ihrer Tabellen nicht ausreicht, sollten Sie sich komplexere Anweisungen ansehen.

Grimaldi
quelle
-2

Sollte diese Abfrage nicht genau das sein, was Sie brauchen?

select min(num), text, num2 group by text, num2
Jens W.
quelle
Dadurch werden alle vier Datensätze zurückgegeben, da die num2Werte eindeutig sind.
CL.