Important: veuillez envisager la mise à niveau vers MySQL 8+ et utiliser la fonction ROW_NUMBER () définie et documentée, et abandonner les anciens hacks liés à une ancienne version limitée de MySQL.
Maintenant, voici un de ces hacks:
Les réponses ici qui utilisent la plupart du temps des variables dans la requête semblent ignorer le fait que la documentation dit (paraphrase):
Ne vous fiez pas aux éléments de la liste SELECT en cours d'évaluation de haut en bas. Ne pas affecter de variables dans un élément SELECT et les utiliser dans un autre
En tant que tel, il y a un risque qu'ils produisent la mauvaise réponse, car ils font généralement un
select
(row number variable that uses partition variable),
(assign partition variable)
Si celles-ci sont évaluées de bas en haut, le numéro de ligne cessera de fonctionner (pas de partitions)
Nous devons donc utiliser quelque chose avec un ordre d'exécution garanti. Entrez CASE WHEN:
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
Comme le contour ld, l'ordre d'affectation de prevcol est important - prevcol doit être comparé à la valeur de la ligne actuelle avant de lui affecter une valeur à partir de la ligne actuelle (sinon ce serait la valeur col des lignes actuelles, pas la valeur col de la ligne précédente) .
Voici comment cela s'intègre:
Le premier QUAND est évalué. Si le col de cette ligne est le même que le col de la ligne précédente, @r est incrémenté et renvoyé par CASE. Ces valeurs de led de retour sont stockées dans @r. C'est une caractéristique de MySQL que l'affectation renvoie la nouvelle valeur de ce qui est attribué à @r dans les lignes de résultat.
Pour la première ligne du jeu de résultats, @prevcol est null (il est initialisé à null dans la sous-requête), donc ce prédicat est faux. Ce premier prédicat renvoie également false chaque fois que col change (la ligne actuelle est différente de la ligne précédente). Cela entraîne l'évaluation du deuxième QUAND.
Le deuxième prédicat WHEN est toujours faux, et il existe uniquement pour attribuer une nouvelle valeur à @prevcol. Parce que le col de cette ligne est différent du col de la ligne précédente (nous le savons parce que s'il était le même, le premier QUAND aurait été utilisé), nous devons attribuer la nouvelle valeur pour la conserver pour les tests la prochaine fois. Étant donné que l'affectation est effectuée et que le résultat de l'affectation est comparé à null et que tout ce qui est égal à null est faux, ce prédicat est toujours faux. Mais au moins l'évaluer a fait son travail en gardant la valeur de col de cette ligne, donc il peut être évalué par rapport à la valeur de col de la ligne suivante
Parce que le deuxième WHEN est faux, cela signifie que dans les situations où la colonne que nous partitionnons par (col) a changé, c'est ELSE qui donne une nouvelle valeur pour @r, en redémarrant la numérotation à partir de 1
Nous ceci arrivons à une situation où ceci:
SELECT
t.*,
ROW_NUMBER() OVER(PARTITION BY pcol1, pcol2, ... pcolX ORDER BY ocol1, ocol2, ... ocolX) rn
FROM
t
A la forme générale:
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
Notes de bas de page:
Le p dans pcol signifie "partition", le o dans ocol signifie "ordre" - dans la forme générale, j'ai supprimé le "prev" du nom de la variable pour réduire l'encombrement visuel
Les crochets autour (@pcolX := colX) = null
sont importants. Sans eux, vous affecterez null à @pcolX et les choses cesseront de fonctionner
C'est un compromis que l'ensemble de résultats doit également être ordonné par les colonnes de partition, pour que la comparaison de la colonne précédente fonctionne. Vous ne pouvez donc pas avoir votre numéro de commande ordonné selon une colonne, mais votre jeu de résultats ordonné à une autre. performance
Je n'y ai pas exploré au-delà du test du fonctionnement de la méthode, mais s'il y a un risque que les prédicats du deuxième WHEN soient optimisés (tout ce qui se compare à null est nul / faux alors pourquoi s'embêter à exécuter l'affectation) et non exécuté , il s'arrête également. Cela ne semble pas se produire dans mon expérience, mais je serai heureux d'accepter les commentaires et de proposer une solution si cela pouvait raisonnablement se produire
Il peut être judicieux de convertir les valeurs nulles qui créent @pcolX en types réels de vos colonnes, dans la sous-requête qui crée les variables @pcolX, à savoir: select @pcol1 := CAST(null as INT), @pcol2 := CAST(null as DATE)
greatest-n-per-group
pour vous guider vers des questions similaires.