2009-10-22 6 views
6

¿Cómo puedo devolver lo que sería efectivamente un GROUP BY "contiguo" en MySQL? En otras palabras, ¿un GRUPO BY que respeta el orden del conjunto de registros?¿Cómo puedo hacer un grupo contiguo en MySQL?

Por ejemplo, SELECT MIN(col1), col2, COUNT(*) FROM table GROUP BY col2 ORDER BY col1 de la siguiente tabla, donde col1 es un índice ordenado único:

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

devuelve:

1 a 4 
3 b 2

pero tengo que devolver el siguiente:

1 a 2 
3 b 2 
5 a 2

Respuesta

5

Uso:

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 clave aquí es crear un valor artificial que permita la agrupación.

Anteriormente, corregir la respuesta 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

transpone +1 al lado derecho, luego haga que sea -1. por lo que no producirá escaneo de tabla. mysql no puede tener índice en la expresión –

+0

¡Eso es todo! ¡Muchas gracias! – Kuyenda

+0

Si usa 'CASE WHEN xt.id IS NULL O xt.val! = X.val', el SQL no se romperá cuando val sea intencionalmente NULL, pero aún así captura la primera comparación de desplazamiento. – Kuyenda

1

Si los números en col1 son contiguos, puede hacer esto:

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

Funciona así:

-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

Como es, que sólo devolverá 2, 4b - ver mi respuesta para correcciones. –

+0

@rexem: Sí, obtuve el alineamiento incorrecto, y la comparación con null necesitaba solución. – Guffa

0

Esto no funcionará:

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

Tal vez esto?

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

GROUP BY ignora el orden. De hecho, necesito un grupo por funcionalidad que respete el orden del conjunto de registros. – Kuyenda

+0

[La primera consulta funciona en SQL Server.] –

1

misma lógica que rexem, pero funciona en cualquier RDBMS de ventanas con capacidad (no funciona en MySQL todavía):

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'); 

fuente:

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

consulta al estilo de ventanas: (funciona en ventanas-c RDBMS 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 

Salida:

1 a 4 
5 b 2 
7 a 3 

Por cierto, esto es el resultado de la grouped_result sin 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 

Se siente bien volver a escribir mysqlism-consulta a ANSI -conformando uno :-) Por ahora, mientras que mysql todavía no tiene capacidad para ventanas, la respuesta de rexem es la mejor. Rexem, esa es una buena técnica de mysql (JOIN (SELECCIONAR @rownum: = 0)) allí, y afaik MSSQL y PostgreSQL no admiten variables declaradas implícitamente, ¡felicitaciones! :-)

0

Here es una descripción más larga de esencialmente la misma (creo) solución ofrecida por omg-ponies - "crear un valor artificial que permita la agrupación".

0

Sé que esta pregunta fue hecha hace dos años y medio (y no espero ningún voto positivo), pero acabo de encontrar exactamente el mismo problema, excepto que 'tabla' ya era una declaración SQL muy complicada, entonces no podía hacer ningún unirse sin copiar y pegar que

Así que tuvo otra idea: por fin col2 y restar el número de fila actual al valor de col1

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

lo que da un resultado como este:

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

Ahora sólo necesitan agrupar por el valor de la última columna

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 
Cuestiones relacionadas