Wie geht das?
Der frühere Titel dieser Frage lautete " Verwenden von Rang (@Rank: = @Rank + 1) in komplexen Abfragen mit Unterabfragen - funktioniert das? ", Weil ich nach einer Lösung mit Rängen gesucht habe, aber jetzt sehe ich, dass die von Bill veröffentlichte Lösung ist viel viel besser.
Ursprüngliche Frage:
Ich versuche, eine Abfrage zu erstellen, die den letzten Datensatz von jeder Gruppe in einer bestimmten Reihenfolge übernimmt:
SET @Rank=0;
select s.*
from (select GroupId, max(Rank) AS MaxRank
from (select GroupId, @Rank := @Rank + 1 AS Rank
from Table
order by OrderField
) as t
group by GroupId) as t
join (
select *, @Rank := @Rank + 1 AS Rank
from Table
order by OrderField
) as s
on t.GroupId = s.GroupId and t.MaxRank = s.Rank
order by OrderField
Ausdruck @Rank := @Rank + 1
wird normalerweise für Rang verwendet, aber für mich sieht er verdächtig aus, wenn er in 2 Unterabfragen verwendet wird, aber nur einmal initialisiert. Wird es so funktionieren?
Und zweitens, funktioniert es mit einer Unterabfrage, die mehrfach ausgewertet wird? Wie eine Unterabfrage in der where (oder have) -Klausel (eine andere Art, das Obige zu schreiben):
SET @Rank=0;
select Table.*, @Rank := @Rank + 1 AS Rank
from Table
having Rank = (select max(Rank) AS MaxRank
from (select GroupId, @Rank := @Rank + 1 AS Rank
from Table as t0
order by OrderField
) as t
where t.GroupId = table.GroupId
)
order by OrderField
Danke im Voraus!
Antworten:
Sie möchten also die Zeile mit der höchsten
OrderField
pro Gruppe erhalten? Ich würde es so machen:( BEARBEITEN von Tomas: Wenn sich innerhalb derselben Gruppe mehr Datensätze mit demselben OrderField befinden und Sie genau einen davon benötigen, können Sie die Bedingung erweitern:
Ende der Bearbeitung.)
Mit anderen Worten, geben Sie die Zeile zurück,
t1
für die keine andere Zeilet2
mit derselbenGroupId
und einer größeren vorhanden istOrderField
. Wennt2.*
NULL ist, bedeutet dies, dass der linke äußere Join keine solche Übereinstimmung gefunden hat und dahert1
den größten WertOrderField
in der Gruppe hat.Keine Ränge, keine Unterabfragen. Dies sollte schnell laufen und den Zugriff auf t2 mit "Using index" optimieren, wenn Sie einen zusammengesetzten Index aktiviert haben
(GroupId, OrderField)
.Informationen zur Leistung finden Sie in meiner Antwort zum Abrufen des letzten Datensatzes in jeder Gruppe . Ich habe eine Unterabfragemethode und die Verknüpfungsmethode mithilfe des Stapelüberlauf-Datendumps ausprobiert. Der Unterschied ist bemerkenswert: Die Join-Methode lief in meinem Test 278-mal schneller.
Es ist wichtig, dass Sie den richtigen Index haben, um die besten Ergebnisse zu erzielen!
In Bezug auf Ihre Methode, die die Variable @Rank verwendet, funktioniert sie nicht so, wie Sie sie geschrieben haben, da die Werte von @Rank nicht auf Null zurückgesetzt werden, nachdem die Abfrage die erste Tabelle verarbeitet hat. Ich zeige Ihnen ein Beispiel.
Ich habe einige Dummy-Daten eingefügt, mit einem zusätzlichen Feld, das null ist, außer in der Zeile, von der wir wissen, dass sie die größte pro Gruppe ist:
Wir können zeigen, dass der Rang für die erste Gruppe auf drei und für die zweite Gruppe auf sechs steigt und die innere Abfrage diese korrekt zurückgibt:
Führen Sie nun die Abfrage ohne Verknüpfungsbedingung aus, um ein kartesisches Produkt aller Zeilen zu erzwingen, und rufen Sie auch alle Spalten ab:
Wir können aus dem Obigen ersehen, dass der maximale Rang pro Gruppe korrekt ist, aber dann steigt der @Rank weiter an, wenn er die zweite abgeleitete Tabelle auf 7 und höher verarbeitet. Die Ränge aus der zweiten abgeleiteten Tabelle überschneiden sich also niemals mit den Rängen aus der ersten abgeleiteten Tabelle.
Sie müssten eine weitere abgeleitete Tabelle hinzufügen, um zu zwingen, dass @Rank zwischen der Verarbeitung der beiden Tabellen auf Null zurückgesetzt wird (und hoffen, dass das Optimierungsprogramm die Reihenfolge, in der Tabellen ausgewertet werden, nicht ändert, oder STRAIGHT_JOIN verwenden, um dies zu verhindern):
Aber die Optimierung dieser Abfrage ist schrecklich. Es kann keine Indizes verwenden, es erstellt zwei temporäre Tabellen, sortiert sie auf die harte Tour und verwendet sogar einen Verknüpfungspuffer, da es auch beim Verknüpfen von temporären Tabellen keinen Index verwenden kann. Dies ist eine Beispielausgabe von
EXPLAIN
:Während meine Lösung mit der linken äußeren Verbindung viel besser optimiert. Es verwendet keine temporäre Tabelle und sogar Berichte,
"Using index"
was bedeutet, dass der Join nur mithilfe des Index aufgelöst werden kann, ohne die Daten zu berühren.Sie werden wahrscheinlich Leute lesen, die in ihren Blogs behaupten, dass "Joins SQL langsam machen", aber das ist Unsinn. Eine schlechte Optimierung macht SQL langsam.
quelle
@Rank1
und@Rank2
eine für jede Unterabfrage verwendet habe? Würde das das Problem beheben? Wäre das schneller als Ihre Lösung?@Rank1
und@Rank2
würde keinen Unterschied machen.... AND t1.foo = t2.foo
später die richtigen Ergebnisse fürWHERE ... AND foo='bar'