Mysql
 sql >> Base de données >  >> RDS >> Mysql

MySQL :Curiosité totale GROUP BY WITH ROLLUP

Parce que vous ne sélectionnez pas l'élément que vous groupez PAR. Si vous avez dit :

GROUP BY c.printable_name

Vous obtiendrez le NULL attendu. Cependant, vous regroupez par une colonne différente afin que MySQL ne sache pas que printable_name participe à un groupe de cumul et sélectionne toute ancienne valeur de cette colonne, dans la jointure de tous inscriptions. (Il est donc possible que vous voyiez d'autres pays que l'Ouzbékistan.)

Cela fait partie d'un problème plus large avec MySQL étant permissif sur ce que vous pouvez SELECT dans une requête GROUP BY. Par exemple, vous pouvez dire :

SELECT gender FROM registrations GROUP BY country;

et MySQL se fera un plaisir de choisir l'une des valeurs de genre pour un enregistrement dans chaque pays, même s'il n'y a pas de lien de causalité direct (alias «dépendance fonctionnelle») entre le pays et le sexe. D'autres SGBD refuseront la commande ci-dessus au motif qu'il n'est pas garanti qu'il y ait un sexe par pays.(*)

Maintenant, ceci :

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 OK, car il existe une dépendance fonctionnelle entre r.country et c.printable_name (en supposant que vous avez correctement décrit votre country_id en tant que PRIMARY KEY).

Cependant, l'extension WITH ROLLUP de MySQL est un peu un hack dans son fonctionnement. À la fin de l'étape de la ligne de cumul, il s'exécute sur l'ensemble du jeu de résultats de pré-regroupement pour saisir ses valeurs, et ensuite définit la colonne group-by sur NULL. Cela n'annule pas non plus les autres colonnes qui ont une dépendance fonctionnelle sur cette colonne. Cela devrait probablement être le cas, mais MySQL ne comprend actuellement pas vraiment tout ce qui concerne les dépendances fonctionnelles.

Donc, si vous sélectionnez c.printable_name, il vous montrera la valeur de nom de pays qu'il a choisie au hasard, et si vous sélectionnez c.country_id, il vous montrera l'ID de pays qu'il a choisi au hasard — même si c.country_id est le critère de jointure, il doit donc l'être identique à r.country, qui est NULL !

Voici ce que vous pouvez faire pour contourner le problème :

  • regrouper par printable_name à la place ; devrait être OK si printable_names sont uniques, ou
  • sélectionnez "r.country" ainsi que printable_name, et vérifiez qu'il est NULL, ou
  • oublier WITH ROLLUP et faire une requête séparée pour la somme finale. Ce sera un peu plus lent, mais il sera également conforme à la norme ANSI SQL-92 afin que votre application puisse fonctionner sur d'autres bases de données.

(* :MySQL a une option SQL_MODE ONLY_FULL_GROUP_BY qui est censé résoudre ce problème, mais il va beaucoup trop loin et ne vous permet de sélectionner que des colonnes de GROUP BY, pas des colonnes qui ont une dépendance fonctionnelle sur GROUP BY. Ainsi, les requêtes valides échoueront également, ce qui les rendra généralement inutiles.)