2009-03-18 22 views
15

Tengo dos consultas. Uno de ellos tiene sentido para mí, el otro no. Primero:MySQL: Total GROUP BY WITH ROLLUP curiosidad

SELECT gender AS 'Gender', count(*) AS '#' 
    FROM registrations 
    GROUP BY gender WITH ROLLUP 

Eso me da esto:

Gender  # 
Female  20 
Male  19 
NULL  39 

Por lo tanto, obtener el recuento y el recuento total. Lo que esperaba. Siguiente:

SELECT c.printable_name AS 'Country', count(*) AS '#' 
    FROM registrations r 
    INNER JOIN country c ON r.country = c.country_id 
    GROUP BY country WITH ROLLUP 

Country   # 
Denmark   9 
Norway   10 
Sweden   18 
United States 1 
Uzbekistan  1 
Uzbekistan  39 

Mismo resultado. Pero ¿por qué recibo Uzbekistán por el total?

+0

¿Cuán seguro está de que el problema no es cómo se muestran los resultados? –

+0

ocurre tanto en mi código, como en phpmyadmin. Desearía intentar probarlo con el navegador de búsqueda mysql habitual o algo así, pero no tengo acceso. – Svish

+0

Estaba jugando con Rollup también. Creo que el problema aquí es que se está uniendo al ID del país con el nombre del país, por lo que no todos los valores NULL se unen, y por alguna razón, mantenga el último valor del nombre en lugar de no tener un nombre. – sphism

Respuesta

36

Pero ¿Por qué recibo Uzbekistán para el total ??

Porque no SELECCIONA el elemento que está AGRANDANDO. Si dijo:

GROUP BY c.printable_name 

Obtendrá el NULL esperado. Sin embargo, está agrupando por una columna diferente, por lo que MySQL no sabe que imprimible_name está participando en un grupo de resumen, y selecciona cualquier valor anterior de esa columna, en la unión de todos los registros. (Por lo tanto, es posible que vea otros países además de Uzbekistán.)

Esto es parte de un problema más amplio con MySQL siendo permisivo en lo que puede SELECCIONAR en una consulta GROUP BY. Por ejemplo, puede decir:

SELECT gender FROM registrations GROUP BY country; 

y MySQL estará feliz de tomar uno de los valores de género para un registro de cada país, a pesar de que no existe una relación causal directa (también conocido como “dependencia funcional”) entre el país y el género . Otros DBMS se niegan el comando anterior con el argumento de que no se garantiza que sea un género por país (*)

Ahora, esto:.

SELECT c.printable_name AS 'Country', count(*) AS '#' 
FROM registrations r 
INNER JOIN country c ON r.country = c.country_id 
GROUP BY country 

está bien, porque hay una dependencia funcional entre r.country y c.printable_name (suponiendo que haya descrito correctamente su country_id como PRIMARY KEY).

Sin embargo, la extensión WITH ROLLUP de MySQL es un poco un truco en la forma en que funciona. En la etapa de la fila de resumen al final, se ejecuta en todo el conjunto de resultados de preagrupación para tomar sus valores, y luego establece la columna de agrupación por NULO. Tampoco anula otras columnas que tienen una dependencia funcional en esa columna. Probablemente debería, pero MySQL actualmente no entiende realmente todo sobre las dependencias funcionales.

Si selecciona c.printable_name, le mostrará el valor del nombre del país elegido al azar, y si selecciona c.country_id le mostrará la ID de país elegida al azar, aunque c.country_id es el criterio de unión, por lo que debe ser el mismo que r.country, que es NULL.

Lo que puede hacer para solucionar el problema es:

  • grupo por printable_name lugar; debe estar bien si printable_names son únicos, o
  • seleccione “r.country”, así como printable_name, y comprobar que por ser NULL, o
  • olvidemos CON ROLLUP y hacer una consulta independiente de la suma final. Esto será un poco más lento, pero también será compatible con ANSI SQL-92 para que su aplicación pueda funcionar en otras bases de datos.

(*: MySQL tiene una opción sql_mode ONLY_FULL_GROUP_BY que se supone que para hacer frente a este problema, pero va demasiado lejos y sólo le permite seleccionar las columnas de cada grupo por, no columnas que tienen una dependencia funcional en el GRUPO POR. Por lo tanto, hará consultas válidas fallan, así, por lo que es generalmente inútil.)

+0

+1 información impresionante – diEcho

0

Coz cuando utiliza el método JOIN, el siguiente elemento NULL de matriz tendrá el valor del elemento NOT NULL anterior. Pero no estoy seguro. Esa es mi experiencia cuando lo uso en PHP.

hm ... hay otro problema ... 'País' no puede ser porque es el nombre de la tabla. Así que cambia por otra cosa. Entonces el último resultado mostrará NULL. Aquí es mi propuesta:

$result = mysql_query("SELECT c.printable_name AS 'countryp', count(*) AS '#' 
FROM registrations r, country c WHERE r.country = c.country_id 
GROUP BY countryp WITH ROLLUP"); 

while($row = @mysql_fetch_array($result)) { 
    $r1 = $row["countryp"]; 
    $r2 = $row["#"]; 
    if ($r1 == NULL) $r1 = 'Total'; 
    echo "$r1 $r2<br />"; 
} 
+0

¿podría ajustar la consulta para que funcione de la misma manera pero con NULL como total en el otro? – Svish

+0

esto es simplemente extraño ... No entiendo esto, jaja. así que cuando uso AS 'countryp' y GROUP BY countryp, funciona. Cuando uso 'nombre', no es así. Cuando uso 'qe', lo hace ... – Svish

0
SELECT ifnull(c.printable_name, "Total Registration = ") AS 'Country', count(*) AS '#' 
FROM registrations r 
INNER JOIN country c ON r.country = c.country_id 
GROUP BY country WITH ROLLUP; 

Esto imprimiría 'registro total = 39', y sería la última fila/registro.