2009-10-22 8 views
6

Comment puis-je retourner ce qui serait effectivement un "BY" contigu dans MySQL. En d'autres termes, un GROUP BY respecte l'ordre du jeu d'enregistrements?Comment puis-je faire un groupe contigu par MySQL?

Par exemple, SELECT MIN(col1), col2, COUNT(*) FROM table GROUP BY col2 ORDER BY col1 dans le tableau suivant où col1 est un index ordonné unique:

 
1 a 
2 a 
3 b 
4 b 
5 a 
6 a

retours:

1 a 4 
3 b 2

mais je dois retourner les éléments suivants:

1 a 2 
3 b 2 
5 a 2

Répondre

5

Utilisation:

SELECT MIN(t.id) 'mi', 
      t.val, 
      COUNT(*) 
    FROM (SELECT x.id, 
       x.val, 
       CASE 
        WHEN xt.val IS NULL OR xt.val != x.val THEN 
        @rownum := @rownum+1 
        ELSE 
        @rownum 
       END AS grp 
      FROM TABLE x 
      JOIN (SELECT @rownum := 0) r 
     LEFT JOIN (SELECT t.id +1 'id', 
         t.val 
        FROM TABLE t) xt ON xt.id = x.id) t 
GROUP BY t.val, t.grp 
ORDER BY mi 

La clé ici était de créer une valeur artificielle qui permettrait de regroupement.

Auparavant, corriger la réponse de Guffa:

SELECT t.id, t.val 
    FROM TABLE t 
LEFT JOIN TABLE t2 on t2.id + 1 = t.id 
    WHERE t2.val IS NULL 
     OR t.val <> t2.val 
+0

Transpose +1 vers le côté droit, puis placez-le -1. donc il ne donnera pas d'analyse de table. mysql ne peut pas avoir d'index sur l'expression –

+0

C'est tout! Merci beaucoup! – Kuyenda

+0

Si vous utilisez 'CASE WHERE xt.id IS NULL OU xt.val! = X.val' alors le SQL ne se cassera pas lorsque val est intentionnellement NULL, mais il capture toujours la première comparaison de décalage. – Kuyenda

1

Si les nombres dans col1 sont contigus, vous pouvez faire comme ceci:

select x.col1, x.col2 
from table x 
left join table y on x.col1 = y.col1 + 1 
where x.col2 <> isnull(y.col2, '') 

Il fonctionne comme ceci:

-x- -y- out 
1 a - - 1 a 
2 a 1 a 
3 b 2 a 3 b 
4 b 3 b 
5 a 4 b 5 a 
6 a 5 a 
+0

Comme il est, qui ne retournera 2 a, 4 b - voir ma réponse pour les corrections. –

+0

@rexem: Oui, l'alignement est incorrect, et la comparaison avec null a nécessité une correction. – Guffa

0

Cela ne fonctionnera pas:

SELECT min_col1 = MIN(col1), col2 
FROM table 
GROUP BY col2 
ORDER BY min_col1 

Peut-être?

SELECT min_col1, col2 
FROM (SELECT min_col1 = MIN(col1), col2 
     FROM table 
     GROUP BY col2) x 
ORDER BY min_col1 
+0

GROUP BY ignore l'ordre. J'ai besoin de groupe par fonctionnalité qui respecte l'ordre du jeu d'enregistrements. – Kuyenda

+0

[La première requête fonctionne dans SQL Server.] –

1

même logique que rexem, mais fonctionne sur tous les SGBDR de fenêtrage capable (ne fonctionne pas encore sur MySQL):

CREATE TABLE tbl 
(
id INT, 
val VARCHAR(1) 
); 

INSERT INTO tbl(id,val) 
VALUES(1,'a'),(2,'a'),(3,'a'),(4,'a'),(5,'b'),(6,'b'),(7,'a'),(8,'a'),(9,'a'); 

source:

1 a 
2 a 
3 a 
4 a 
5 b 
6 b 
7 a 
8 a 
9 a 

requête de style fenêtrage: (fonctionne sur fenêtrage-c SGBDR apable):

WITH grouped_result AS 
(
    SELECT x.id, x.val, 
     COUNT(CASE WHEN y.val IS NULL OR y.val <> x.val THEN 1 END) 
     OVER (ORDER BY x.id) AS grp 
    FROM tbl x LEFT JOIN tbl y ON y.id + 1 = x.id 
) 

SELECT MIN(id) mi, val, COUNT(*) 
FROM grouped_result 
GROUP BY val, grp 
ORDER BY mi 

Sortie:

1 a 4 
5 b 2 
7 a 3 

BTW, ceci est le résultat du grouped_result sans GROUP BY:

1 a 1 
2 a 1 
3 a 1 
4 a 1 
5 b 2 
6 b 2 
7 a 3 
8 a 3 
9 a 3 

Ça fait du bien ré-écriture mysqlism-requête à la norme ANSI -conforming one :-) Pour l'instant, alors que mysql n'a pas encore de capacité de fenêtrage, la réponse de rexem est la meilleure. Rexem, c'est une bonne technique mysql (JOIN (SELECT @rownum: = 0)) là, et afaik MSSQL et PostgreSQL ne supportent pas la variable déclarée implicitement, kudos! :-)

0

Here est une description plus longue d'essentiellement la même (je pense) la solution offerte par omg-ponies - "créer une valeur artificielle qui permettrait le regroupement".

0

Je sais que cette question a été posée il y a deux ans et demi (et je ne m'attends à aucun upvote), mais je viens de rencontrer exactement le même problème, sauf que 'table' était déjà une instruction SQL très compliquée. Je ne pouvais pas faire de se joindre sans copier-coller ce

donc j'ai eu une autre idée: commande par col2 et soustrayez le numéro de ligne à la valeur de col1

SELECT *, col1-(@rownum:[email protected]+1) FROM (SELECT * FROM table JOIN (SELECT @rownum:=0) AS i ORDER BY col2) AS t 

ce qui donne un résultat comme celui-ci:

1 a 0 
2 a 0 
5 a 2 
6 a 2 
3 b -2 
4 b -2 

Maintenant, il vous suffit de groupe par la valeur de la dernière colonne

SELECT MIN(col1) AS mi, col2, COUNT(*) FROM 
    (SELECT *, col1-(@rownum:[email protected]+1) AS grp FROM (SELECT * FROM table JOIN (SELECT @rownum:=0) AS i ORDER BY col2) AS t) AS x 
GROUP BY grp ORDER BY mi