bug sql résolu par intuition et non par certitude

Administrateur PHPfrance
Administrateur PHPfrance | 149 Messages

04 oct. 2005, 18:43

Salut

Qui va me donner l'explication parfaite

voici une requete sql qui foire
SELECT g.id AS gid,
              g.maxUser - count(ug.user) AS  nbPlaces
             FROM `groups` AS  g                            
             LEFT JOIN  `rel_group_user` ug
             ON    `g`.`id` = `ug`.`team`
             GROUP BY (`g`.`id`)
             HAVING nbPlaces > 0 OR g.maxUser IS NULL 
             ORDER BY nbPlaces DESC
voici la requete corrigée
SELECT g.id AS gid, 
              g.maxUser - count(ug.user) AS  nbPlaces,
              g.maxUser g_maxUsers
             FROM `groups` AS  g                            
             LEFT JOIN  `rel_group_user` ug
             ON    `g`.`id` = `ug`.`team`
             GROUP BY (`g`.`id`)
             HAVING nbPlaces > 0 OR g_maxUsers IS NULL 
             ORDER BY nbPlaces DESC
Qui peut expliquer pourquoi il faut ajouter
g.maxUsers dans la liste des champs du select alors qu'il vient de la table FROM (donc le champs est toujours là pour tous les rows
--
Moosh - ancêtre de la communauté
http://www.phpfrance.com

Mammouth du PHP | 19672 Messages

04 oct. 2005, 19:43

Petit mystère, mais il y a un détail qui me surprend: j'ai toujours eu des problèmes en préfixant les champs tout en protégeant les noms, ainsi, si je fait `g`.`id`, ça va planter, mais avec g.id ça va fonctionner: Donc la première requête revue comme ceci devrait fonctionner ?

Code : Tout sélectionner

SELECT g.id AS gid, g.maxUser - count(ug.user) AS nbPlaces FROM `groups` AS g LEFT JOIN `rel_group_user` AS ug ON g.id = ug.team GROUP BY (g.id) HAVING nbPlaces > 0 OR g.maxUser IS NULL ORDER BY nbPlaces DESC
:?: Sinon, quel est le message d'erreur ?
Codez en pensant que celui qui maintiendra votre code est un psychopathe qui connait votre adresse :axe:

Hash Air
Invité n'ayant pas de compte PHPfrance

06 oct. 2005, 01:10

En théorie, les champs qui apparaissent dans la clause HAVING doivent provenir du SELECT. Puisqu'il n'y est pas dans le premier exemple je pense que MySQL le remplace par NULL et la clause "NULL IS NULL" annule l'effet de ton HAVING. Réessaie le premier exemple en citant explicitement ton champs maxUser dans le SELECT pour voir.

Un truc intéressant: si tu n'avais pas de référence au COUNT() dans ton HAVING, MySQL l'aurait certainement fusionné à la clause WHERE et on n'en aurait jamais parlé. :D

ViPHP
ViPHP | 1380 Messages

06 oct. 2005, 09:18

Intéressant.
En théorie, les champs qui apparaissent dans la clause HAVING doivent provenir du SELECT.
Oui et non. Apparemment, tout dépend de la version.

Un peu de théorie donc (tirée de la doc - en anglais car, une fois encore, la doc en français est curieusement muette sur le sujet):
  1. Avant 5.0.2 les colonnes du HAVING doivent faire référence aux colonnes définies dans un des groupes suivant:
    • SELECT
    • des fonctions de groupement (Aggregate Functions)
    • subqueries

    Code : Tout sélectionner

    SELECT COUNT(*) FROM table GROUP BY col1 HAVING col1 = 2; # retourne une erreur SELECT COUNT(*), col1 FROM table GROUP BY col1 HAVING col1 = 2; # correct
  2. Après 5.0.2 les colonnes du HAVING doivent faire référence aux colonnes définies dans un des groupes suivant:
    • SELECT
    • des fonctions de groupement (Aggregate Functions)
    • subqueries
    • GROUP BY

    Code : Tout sélectionner

    SELECT COUNT(*) FROM table GROUP BY col1 HAVING col1 = 2; # requête correcte
  3. Et, pour info, en SQL standard:
    • des fonctions de groupement (Aggregate Functions)
    • GROUP BY
Ca, c'est la théorie. En pratique, d'après ce qui précède, dans MySQL 4.1 une requête comme :

Code : Tout sélectionner

SELECT COUNT(col1) FROM table GROUP BY col1 HAVING col1 = 2;
devrait marcher puisque référence est faite à la fonction de groupement COUNT(). Et bien, non, ça ne marche pas. Donc il y aurait peut-être bien un bug ou, à tout le moins, une imprécision dans la doc. Je poste ceci sur le forum de MySQL AB et je posterai ici la réponse.
ripat

Modérateur PHPfrance
Modérateur PHPfrance | 6037 Messages

06 oct. 2005, 10:30

est -ce que c'est MySQL ?
Règle n°2 du webmaster : Toujours commencer par le HTML qu'on veut obtenir....toujours ! :priere:
J'aime apprendre de nouvelles choses.

Administrateur PHPfrance
Administrateur PHPfrance | 149 Messages

06 oct. 2005, 12:43

Merci Ripat, j'allais donner ma réponse finale mais tu es encore plus complet.


5.0.2 <- je suppose que tu parles de la version de Mysql
--
Moosh - ancêtre de la communauté
http://www.phpfrance.com

ViPHP
ViPHP | 1380 Messages

06 oct. 2005, 12:53

5.0.2 <- je suppose que tu parles de la version de Mysql
Oui. Pour la suite:
http://forums.mysql.com/read.php?10,480 ... #msg-48055
ripat

Modérateur PHPfrance
Modérateur PHPfrance | 2575 Messages

06 oct. 2005, 14:17

ça n'a rien à voir avec la version de Mysql, on l'a souligné tout à l'heure, l'erreur provient du fait que les champs négociés au niveau du HAVING doivent faire partie de l'agrégat GROUP BY ou du SELECT.

La preuve, pour résoudre le problème autrement on pourrait mettre la condition (nbPlaces >0 OR `g`.maxUser IS NULL) dans un WHERE sans solliciter le HAVING.

Le WHERE, contrairement au HAVING, a une vision globale des champs des tables sources ouvertes par le FROM et ne se contente pas des champs cités par le SELECT.

Si un WHERE il y'a le jeu de données sources est filtré à l'ouverture des tables sources selon les critères définis

Si un GROUP BY il y'a le HAVING filre les enregistrements groupés selon les critères de regroupement.

Le fltrage having s'effectue alors après le regroupement et doit utiliser alors les champs existant dans la clause GROUP BY ou à défaut SELECT

Globalement, la règle conceptuelle dit que seuls dépondent du select non pas dans l'ordre mais dans la pésence, les champs du regroupement qui ne doivent pas référencer un champ calculé. La condition having dépond des champs du Group By.

La séquence d'exécution d'une requête SELECT est :
1. Ouverture des sources et filtrage du jeu de données utiles : FROM et WHERE (éventuelment ORDER BY aussi pour appliquer un tri)

2. Construction du tuple selon la structure du SELECT

3. Regroupement (GROUP BY) +/- filtrage du regroupement (HAVING)
--------//////----//---//----//////
-------//---//----//---//----//---//
------//////----//////-----//////
-----||--------||--||---||
Prendre le recul n'est pas une perte de temps.


ps: Affrontez moi dans l'arène