332 votes

ROW_NUMBER() dans MySQL

Existe-t-il un moyen efficace de répliquer la fonction du serveur SQL dans MySQL ? ROW_NUMBER() ?

Par exemple :

SELECT 
    col1, col2, 
    ROW_NUMBER() OVER (PARTITION BY col1, col2 ORDER BY col3 DESC) AS intRow
FROM Table1

Je pourrais alors, par exemple, ajouter une condition pour limiter intRow à 1 pour obtenir une seule rangée avec la plus haute col3 pour chaque (col1, col2) paire.

9 votes

Tagué avec greatest-n-per-group pour vous guider vers des questions similaires.

1 votes

Supprimé Sql-Server car il s'agit de l'élément le plus voté de la liste des priorités. recherche combinée de tags mais n'est pas vraiment pertinent pour SQL Server.

0 votes

Pour une fonction simple de numéro de ligne mysql, consultez le site datamakessense.com/mysql-rownum-row-number-function

247voto

OMG Ponies Points 144785

Il n'existe pas de fonctionnalité de classement dans MySQL. Le plus proche que vous puissiez obtenir est d'utiliser une variable :

SELECT t.*, 
       @rownum := @rownum + 1 AS rank
  FROM YOUR_TABLE t, 
       (SELECT @rownum := 0) r

alors comment cela pourrait-il fonctionner dans mon cas ? J'aurais besoin de deux variables, une pour chaque col1 et col2 ? Col2 devrait être réinitialisée d'une manière ou d'une autre lorsque Col1 change ?

Oui. Si c'était Oracle, vous pourriez utiliser la fonction LEAD pour atteindre la valeur suivante. Heureusement, Quassnoi couvre la logique de ce que vous devez implémenter dans MySQL .

1 votes

Hmm....so comment cela fonctionnerait-il dans mon cas ? J'aurais besoin de deux variables, une pour chaque col1 et col2 ? Col2 devrait être réinitialisée d'une manière ou d'une autre lorsque Col1 change... ?

0 votes

Merci...comme je l'ai dit plus haut, cette réponse est également acceptée par celle de Bobince, mais je ne peux en cocher qu'une :-)

14 votes

L'assignation et la lecture de variables définies par l'utilisateur dans la même instruction ne sont pas fiables, comme le montre le document suivant : dev.mysql.com/doc/refman/5.0/fr/user-variables.html : " En règle générale, vous ne devez jamais affecter une valeur à une variable utilisateur et lire la valeur dans la même déclaration. Vous pourriez obtenir les résultats escomptés, mais ce n'est pas garanti. L'ordre d'évaluation des expressions impliquant des variables utilisateur est indéfini et peut changer en fonction des éléments contenus dans une instruction donnée."

122voto

bobince Points 270740

Je veux la ligne avec la col3 la plus élevée pour chaque paire (col1, col2).

C'est un maximum collectif L'une des questions SQL les plus fréquemment posées (car elle semble être facile, mais ne l'est pas vraiment).

J'opte souvent pour l'auto-jointure nulle :

SELECT t0.col3
FROM table AS t0
LEFT JOIN table AS t1 ON t0.col1=t1.col1 AND t0.col2=t1.col2 AND t1.col3>t0.col3
WHERE t1.col1 IS NULL;

"Obtenez les lignes de la table pour lesquelles aucune autre ligne avec col1,col2 correspondant n'a un col3 supérieur". (Vous remarquerez que cette solution et la plupart des autres solutions de maximisation par groupe renverront plusieurs lignes si plusieurs lignes ont les mêmes col1,col2,col3. Si c'est un problème, vous aurez peut-être besoin d'un post-traitement).

2 votes

Mais que se passe-t-il s'il y a deux valeurs maximales de col3 pour une paire (col1, col2) ? Vous vous retrouveriez avec deux lignes.

0 votes

@Paul : oui ! J'ai ajouté une note à ce sujet dans la réponse il y a un instant. Vous pouvez généralement laisser tomber facilement les rangées supplémentaires non désirées dans la couche d'application après coup sur une base aléatoire, mais si vous avez une lot de lignes ayant toutes la même col3, cela peut être problématique.

0 votes

En t-sql, j'ai tendance à en avoir besoin en tant que sous-requête dans le cadre d'une requête beaucoup plus large, donc le post-traitement n'est pas vraiment une option. De plus, que se passe-t-il si vous voulez les lignes avec les n valeurs les plus élevées de col3 ? Avec mon exemple t-sql, vous pouvez ajouter la contrainte de intRow <= n, mais cela serait très difficile avec une auto-jonction.

106voto

Mosty Mostacho Points 22803

Je finis toujours par suivre ce schéma. Étant donné ce tableau :

+------+------+
|    i |    j |
+------+------+
|    1 |   11 |
|    1 |   12 |
|    1 |   13 |
|    2 |   21 |
|    2 |   22 |
|    2 |   23 |
|    3 |   31 |
|    3 |   32 |
|    3 |   33 |
|    4 |   14 |
+------+------+

Vous pouvez obtenir ce résultat :

+------+------+------------+
|    i |    j | row_number |
+------+------+------------+
|    1 |   11 |          1 |
|    1 |   12 |          2 |
|    1 |   13 |          3 |
|    2 |   21 |          1 |
|    2 |   22 |          2 |
|    2 |   23 |          3 |
|    3 |   31 |          1 |
|    3 |   32 |          2 |
|    3 |   33 |          3 |
|    4 |   14 |          1 |
+------+------+------------+

En exécutant cette requête, qui ne nécessite aucune variable définie :

SELECT a.i, a.j, count(*) as row_number FROM test a
JOIN test b ON a.i = b.i AND a.j >= b.j
GROUP BY a.i, a.j

J'espère que cela vous aidera !

1 votes

Si les colonnes sont VARCHAR ou CHAR, comment pouvez-vous gérer cela avec cette structure ?

3 votes

Vous êtes génial Mosty, je cherche exactement ça.

0 votes

Je viens de donner ceci réponse en utilisant votre logique pour le numéro de ligne. Merci.

74voto

Peter Johnson Points 1156
SELECT 
    @i:=@i+1 AS iterator, 
    t.*
FROM 
    tablename AS t,
    (SELECT @i:=0) AS foo

1 votes

Le premier := semble manquer dans la réponse de @OMG Ponies. Merci d'avoir publié ce message, Peter Johnson.

0 votes

Je pense que (SELECT @i:=0) AS foo devrait être la première table dans l'instruction FROM, surtout si les autres tables utilisent des sous-sélections.

1 votes

Pourquoi avez-vous besoin du " as foo" ?

29voto

birch Points 61

Consultez cet article, il montre comment imiter la fonction SQL ROW_NUMBER() avec une partition by dans MySQL. J'ai rencontré ce même scénario dans une mise en œuvre de WordPress. J'avais besoin de ROW_NUMBER() et il n'y en avait pas.

http://www.explodybits.com/2011/11/mysql-row-number/

L'exemple de l'article utilise une seule partition par champ. Pour partitionner par des champs supplémentaires, vous pourriez faire quelque chose comme ceci :

  SELECT  @row_num := IF(@prev_value=concat_ws('',t.col1,t.col2),@row_num+1,1) AS RowNumber
         ,t.col1 
         ,t.col2
         ,t.Col3
         ,t.col4
         ,@prev_value := concat_ws('',t.col1,t.col2)
    FROM table1 t,
         (SELECT @row_num := 1) x,
         (SELECT @prev_value := '') y
   ORDER BY t.col1,t.col2,t.col3,t.col4 

L'utilisation de concat_ws gère les nullités. J'ai testé cela sur 3 champs utilisant un int, une date et un varchar. J'espère que cela vous aidera. Consultez l'article car il décompose et explique cette requête.

1 votes

Génial. Ça fait vraiment le partitionnement. Très pratique

2 votes

Comparé à l'auto jointure, c'est beaucoup plus efficace, mais il y a un problème avec la logique, l'ordre doit se produire avant le calcul du row_num, la concat n'est pas non plus nécessaire. ``` SELECT @row_num := IF(@prev_col1=t.col1 AND @prev_col2=t.col2), @row_num+1, 1) AS RowNumber ,t.col1 ,t.col2 ,t.col3 ,t.col4 ,@prev_col1 := t. col1 ,@prev_col2 := t.col2 FROM (SELECT * FROM table1 ORDER BY col1, col2, col3) t, (SELECT @row_num := 1, @prev_col1 := '', @prev_col2 := '') var ````

0 votes

Si vous avez besoin de mettre cela dans une sous-requête, alors ajoutez limit 18446744073709551615 pour forcer order by clause.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X