Pregunta

Estoy tratando de agregar funciones a una aplicación preexistente y me encontré con una vista de MySQL algo así:

SELECT
     AVG(table_name.col1),
     AVG(table_name.col2),
     AVG(table_name.col3),
     table_name.personID,
     table_name.col4
FROM table_name
GROUP BY table_name.personID;

OK, así que hay algunas funciones agregadas. Puede seleccionar personID porque lo está agrupando. Pero también está seleccionando una columna que no está en una función agregada y no es parte de la cláusula GROUP BY. ¿¿¿Cómo es esto posible??? ¿Simplemente elige un valor aleatorio porque los valores definitivamente no son únicos por grupo?

De donde vengo (servidor MSSQL), eso es un error. ¿Alguien puede explicarme este comportamiento y por qué está permitido en MySQL?

¿Fue útil?

Solución

Es cierto que esta característica permite algunas consultas ambiguas y silenciosamente devuelve un conjunto de resultados con un valor arbitrario seleccionado de esa columna. En la práctica, tiende a ser el valor de la fila dentro del grupo que se almacena físicamente primero.

Estas consultas no son ambiguas si solo elige columnas que dependen funcionalmente de las columnas en los criterios GROUP BY. En otras palabras, si solo puede haber un valor distinto de "ambiguo" columna por valor que define el grupo, no hay problema. Esta consulta sería ilegal en Microsoft SQL Server (y ANSI SQL), aunque lógicamente no puede generar ambigüedad:

SELECT AVG(table1.col1), table1.personID, persons.col4
FROM table1 JOIN persons ON (table1.personID = persons.id)
GROUP BY table1.personID;

Además, MySQL tiene un modo SQL para que se comporte según el estándar: ONLY_FULL_GROUP_BY

FWIW, SQLite también permite estas cláusulas GROUP BY ambiguas, pero elige el valor de la fila última en el grupo.


Al menos en la versión que probé. Lo que significa ser arbitrario es que MySQL o SQLite podrían cambiar su implementación en el futuro y tener un comportamiento diferente. Por lo tanto, no debe confiar en que el comportamiento se mantenga como está actualmente en casos ambiguos como este. Es mejor reescribir sus consultas para ser determinista y no ambiguo. Es por eso que MySQL 5.7 ahora habilita ONLY_FULL_GROUP_BY por defecto.

Otros consejos

Debería haber buscado en Google un poco más ... Parece que encontré mi respuesta .

  

MySQL extiende el uso de GROUP BY   que puedes usar columnas no agregadas   o cálculos en la lista SELECCIONAR   que no aparecen en el GROUP BY   cláusula. Puede usar esta función para   obtener un mejor rendimiento evitando   clasificación innecesaria de columnas y   agrupamiento. Por ejemplo, no necesitas   para agrupar en customer.name en el   siguiente consulta

     

En SQL estándar, tendría que agregar   customer.name a la cláusula GROUP BY.   En MySQL, el nombre es redundante.

Aún así, eso parece ... incorrecto.

select * from personel where p_id IN(select
min(dbo.personel.p_id)
FROM
personel
GROUP BY dbo.personel.p_adi)

Digamos que tiene una consulta como esta:

SELECT g, v 
FROM t
GROUP BY g;

En este caso, para cada valor posible para g , mysql elige uno de los valores correspondientes de v .

Sin embargo, cuál es el elegido, depende de algunas circunstancias.

Leí en alguna parte que para cada grupo de g, se mantiene el primer valor de v , en el orden en que se insertaron los registros en la tabla t .

Esto es bastante feo porque los registros en una tabla deben tratarse como un conjunto donde el orden de los elementos no debería importar. Esto es así '' mysql-ish '' ...

Si desea determinar qué valor debe mantener v , debe aplicar una subselección para t de esta manera:

SELECT g, v 
FROM (
    SELECT * 
        FROM t 
        ORDER BY g, v DESC
) q
GROUP BY g;

De esta forma, usted define en qué orden los registros de la subconsulta son procesados ??por la consulta externa, por lo que puede confiar en qué valor de v elegirá para los valores individuales de g .

Sin embargo, si necesita algunas condiciones WHERE, tenga mucho cuidado. Si agrega la condición WHERE a la subconsulta, mantendrá el comportamiento, siempre devolverá el valor que espera:

SELECT g, v 
FROM (
    SELECT * 
        FROM t 
        WHERE g = '737a8783-110c-447e-b4c2-1cbb7c6b72c9' 
        ORDER BY g, v DESC
) q
GROUP BY g;

Esto es lo que espera, la subselección filtra y ordena la tabla. Mantiene los registros donde g tiene el valor dado y la consulta externa devuelve ese g y el primer valor para v .

Sin embargo, si agrega la misma condición WHERE a la consulta externa, obtendrá un resultado no determinista:

SELECT g, v 
FROM (
    SELECT * 
        FROM t 
        -- WHERE g = '737a8783-110c-447e-b4c2-1cbb7c6b72c9' 
        ORDER BY g, v DESC
) q
WHERE g = '737a8783-110c-447e-b4c2-1cbb7c6b72c9'
GROUP BY g;

Sorprendentemente, puede obtener diferentes valores para v al ejecutar la misma consulta una y otra vez, lo cual es ... extraño. El comportamiento esperado es obtener todos los registros en el orden apropiado de la subconsulta, filtrándolos en la consulta externa y luego seleccionando los mismos que seleccionó en el ejemplo anterior. Pero no lo hace.

Selecciona un valor para v aparentemente al azar. La misma consulta devolvió valores diferentes para v si ejecuté más (~ 20) veces pero la distribución no fue uniforme.

Si en lugar de agregar un WHERE externo, especifica una condición HAVING como esta:

SELECT g, v 
FROM (
    SELECT * 
        FROM t1 
        -- WHERE g = '737a8783-110c-447e-b4c2-1cbb7c6b72c9' 
        ORDER BY g, v DESC
) q
-- WHERE g = '737a8783-110c-447e-b4c2-1cbb7c6b72c9'
GROUP BY g
HAVING g = '737a8783-110c-447e-b4c2-1cbb7c6b72c9';

Entonces obtienes un comportamiento consistente nuevamente.

CONCLUSIÓN: Sugeriría no confiar en esta técnica en absoluto. Si realmente quiere / necesita evitar las condiciones WHERE en la consulta externa. Úselo en la consulta interna si puede o una cláusula HAVING en la consulta externa.

Lo probé con estos datos:

CREATE TABLE t1 (
    v INT,
    g VARCHAR(36)
);

INSERT INTO t1 VALUES (1, '737a8783-110c-447e-b4c2-1cbb7c6b72c9');
INSERT INTO t1 VALUES (2, '737a8783-110c-447e-b4c2-1cbb7c6b72c9');

en mysql 5.6.41.

Tal vez es solo un error que se soluciona / solucionó en las versiones más recientes, por favor envíe sus comentarios si tiene experiencia con versiones más recientes.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top