Wichtig: Bitte erwägen Sie ein Upgrade auf MySQL 8+ und verwenden Sie die definierte und dokumentierte Funktion ROW_NUMBER (). Lassen Sie alte Hacks hinter sich, die an eine funktionsbeschränkte alte Version von MySQL gebunden sind
Hier ist einer dieser Hacks:
Die Antworten hier, die meistens / alle Abfragevariablen verwenden, scheinen die Tatsache zu ignorieren, dass in der Dokumentation steht (Paraphrase):
Verlassen Sie sich nicht darauf, dass Elemente in der SELECT-Liste in der Reihenfolge von oben nach unten ausgewertet werden. Weisen Sie keine Variablen in einem SELECT-Element zu und verwenden Sie sie in einem anderen
Als solches besteht das Risiko, dass sie die falsche Antwort ausgeben, weil sie normalerweise eine Antwort geben
select
(row number variable that uses partition variable),
(assign partition variable)
Wenn diese jemals von unten nach oben ausgewertet werden, funktioniert die Zeilennummer nicht mehr (keine Partitionen).
Wir müssen also etwas mit einer garantierten Ausführungsreihenfolge verwenden. Geben Sie CASE WHEN ein:
SELECT
t.*,
@r := CASE
WHEN col = @prevcol THEN @r + 1
WHEN (@prevcol := col) = null THEN null
ELSE 1 END AS rn
FROM
t,
(SELECT @r := 0, @prevcol := null) x
ORDER BY col
Als Gliederung ld ist die Reihenfolge der Zuweisung von prevcol wichtig - prevcol muss mit dem Wert der aktuellen Zeile verglichen werden, bevor wir ihm einen Wert aus der aktuellen Zeile zuweisen (andernfalls wäre es der Spaltenwert der aktuellen Zeilen, nicht der Spaltenwert der vorherigen Zeile). .
So passt das zusammen:
Das erste WANN wird ausgewertet. Wenn die Spalte dieser Zeile mit der Spalte der vorherigen Zeile übereinstimmt, wird @r inkrementiert und vom CASE zurückgegeben. Diese LED-Rückgabewerte werden in @r gespeichert. Es ist eine Funktion von MySQL, dass die Zuweisung den neuen Wert dessen, was in @r zugewiesen ist, in die Ergebniszeilen zurückgibt.
Für die erste Zeile in der Ergebnismenge ist @prevcol null (es wird in der Unterabfrage auf null initialisiert), daher ist dieses Prädikat falsch. Dieses erste Prädikat gibt bei jeder Änderung der Spalte auch false zurück (die aktuelle Zeile unterscheidet sich von der vorherigen Zeile). Dies bewirkt, dass das zweite WANN ausgewertet wird.
Das zweite WHEN-Prädikat ist immer falsch und dient lediglich dazu, @prevcol einen neuen Wert zuzuweisen. Da sich die Spalte dieser Zeile von der Spalte der vorherigen Zeile unterscheidet (wir wissen dies, weil bei gleicher Verwendung das erste WANN verwendet worden wäre), müssen wir den neuen Wert zuweisen, damit er beim nächsten Test beibehalten wird. Da die Zuweisung vorgenommen wird und dann das Ergebnis der Zuweisung mit null verglichen wird und alles, was mit null gleichgesetzt wird, falsch ist, ist dieses Prädikat immer falsch. Zumindest die Auswertung hat jedoch dazu beigetragen, den Wert von col aus dieser Zeile beizubehalten, sodass er anhand des col-Werts der nächsten Zeile ausgewertet werden kann
Da das zweite WHEN falsch ist, bedeutet dies, dass in Situationen, in denen sich die Spalte, nach der wir partitionieren (col), geändert hat, ELSE einen neuen Wert für @r angibt und die Nummerierung von 1 neu startet
Wir kommen zu einer Situation, in der dies:
SELECT
t.*,
ROW_NUMBER() OVER(PARTITION BY pcol1, pcol2, ... pcolX ORDER BY ocol1, ocol2, ... ocolX) rn
FROM
t
Hat die allgemeine Form:
SELECT
t.*,
@r := CASE
WHEN col1 = @pcol1 AND col2 = @pcol2 AND ... AND colX = @pcolX THEN @r + 1
WHEN (@pcol1 := pcol1) = null OR (@pcol2 := col2) = null OR ... OR (@pcolX := colX) = null THEN null
ELSE 1
END AS rn
FROM
t,
(SELECT @r := 0, @pcol1 := null, @pcol2 := null, ..., @pcolX := null) x
ORDER BY pcol1, pcol2, ..., pcolX, ocol1, ocol2, ..., ocolX
Fußnoten:
Das p in pcol bedeutet "Partition", das o in ocol bedeutet "Reihenfolge" - in der allgemeinen Form habe ich das "prev" aus dem Variablennamen entfernt, um die visuelle Unordnung zu verringern
Die Klammern (@pcolX := colX) = null
sind wichtig. Ohne sie weisen Sie @pcolX null zu und die Dinge funktionieren nicht mehr
Es ist ein Kompromiss, dass die Ergebnismenge auch nach den Partitionsspalten sortiert werden muss, damit der Vergleich der vorherigen Spalte funktioniert. Sie können Ihre Rownumber daher nicht nach einer Spalte sortieren lassen, sondern Ihre Ergebnismenge nach einer anderen. Möglicherweise können Sie dies mit Unterabfragen beheben. Ich glaube jedoch, dass in den Dokumenten auch angegeben ist, dass die Reihenfolge der Unterabfragen ignoriert werden kann, sofern nicht LIMIT verwendet wird und dies Auswirkungen haben könnte Performance
Ich habe mich nicht weiter damit befasst, als zu testen, ob die Methode funktioniert, aber wenn das Risiko besteht, dass die Prädikate im zweiten WANN wegoptimiert werden (alles im Vergleich zu null ist null / falsch, warum also die Aufgabe ausführen) und nicht ausgeführt werden hört es auch auf. Dies scheint meiner Erfahrung nach nicht zu geschehen, aber ich nehme gerne Kommentare an und schlage eine Lösung vor, wenn dies vernünftigerweise möglich ist
Es kann sinnvoll sein, die Nullen, mit denen @pcolX erstellt wird, in die tatsächlichen Typen Ihrer Spalten in der Unterabfrage umzuwandeln, mit der die Variablen @pcolX erstellt werden. select @pcol1 := CAST(null as INT), @pcol2 := CAST(null as DATE)
greatest-n-per-group
, um Sie zu ähnlichen Fragen zu führen.