OVH Cloud OVH Cloud

[MySQL] sous requete ?

11 réponses
Avatar
Jérémy Cluzel
Bonjour,

J'ai un pb avec la requete suivante:

SELECT CONCAT( etb_id, '-', ofr_id ) AS code, COUNT( DISTINCT CONCAT(
etb_id, '-', ofr_id ) ) AS count
FROM radacct, card
WHERE card.card_username = radacct.UserName
GROUP BY UserName
HAVING MIN( AcctStartTime ) LIKE '2007-01-10%'

elle me retourne les resultats suivants:

code count
30-2 1
7-1 1
13-1 1
20-2 1
20-6 1
20-6 1
13-3 1
13-3 1
11-3 1
29-5 1
13-5 1

mais, moi, ce que je voudrais, ca serait plutot un truc comme ca:

code count
30-2 1
7-1 1
13-1 1
20-2 1
20-6 2
13-3 2
11-3 1
29-5 1
13-5 1

j'ai oublié un truc ? ou il faut que je passe par une sous requete ?

Jeremy

10 réponses

1 2
Avatar
Pif
Désolé, je comprends pas tout.. il n'y a pas de commentaire sur le
contenu ni le schéma ni les noms d'attributs...

en tout cas, plusieurs choses me font bizarre :
- tu fais un group by sur UserName, mais cet attribut n'est pas projeté...
- c'est le meme contenu qui est projeté à gauche et à droite dans les
requetes... "CONCAT( etb_id, '-', ofr_id )". Il est normal que pour
chaque tuple à gauche tu n'en ai qu'un à droite, si ton username est
bijectif avec etb_id ...

Bon, je suis pas sur d'avoir répondu...

Jérémy Cluzel a écrit :
Bonjour,

J'ai un pb avec la requete suivante:

SELECT CONCAT( etb_id, '-', ofr_id ) AS code, COUNT( DISTINCT CONCAT(
etb_id, '-', ofr_id ) ) AS count
FROM radacct, card
WHERE card.card_username = radacct.UserName
GROUP BY UserName
HAVING MIN( AcctStartTime ) LIKE '2007-01-10%'

elle me retourne les resultats suivants:

code count
30-2 1
7-1 1
13-1 1
20-2 1
20-6 1
20-6 1
13-3 1
13-3 1
11-3 1
29-5 1
13-5 1

mais, moi, ce que je voudrais, ca serait plutot un truc comme ca:

code count
30-2 1
7-1 1
13-1 1
20-2 1
20-6 2
13-3 2
11-3 1
29-5 1
13-5 1

j'ai oublié un truc ? ou il faut que je passe par une sous requete ?

Jeremy
Avatar
Antoun
Je crois comprendre que ce que tu veux c'est ça :

SELECT CONCAT( etb_id, '-', ofr_id ) AS code, COUNT(*) AS count
FROM radacct, card
WHERE card.card_username = radacct.UserName
GROUP BY CONCAT( etb_id, '-', ofr_id )
HAVING MIN( AcctStartTime ) LIKE '2007-01-10%'

Par contre je suis d'accord avec Pif sur le fait que je ne vois pas du tout
ce que vient faire le username là-dedans...

--
Antoun

Guide complet MySQL 5, par Antoine Dinimant, éd. MicroApplication
http://www.microapp.com/livre_mysql_7873.html
Avatar
Pif
J'en profite pour une petite question sur le comportement du SGBDR...

Dans le group by tu peux réutiliser l'alias "code". Pour le coup, quand
tu ne le fais pas, fait il deux fois le calcul ou est il "intelligent"
au point de n'exécuter qu'une fois le code ?

Merci.


Antoun a écrit :
Je crois comprendre que ce que tu veux c'est ça :

SELECT CONCAT( etb_id, '-', ofr_id ) AS code, COUNT(*) AS count
FROM radacct, card
WHERE card.card_username = radacct.UserName
GROUP BY CONCAT( etb_id, '-', ofr_id )
HAVING MIN( AcctStartTime ) LIKE '2007-01-10%'

Par contre je suis d'accord avec Pif sur le fait que je ne vois pas du tout
ce que vient faire le username là-dedans...

--
Antoun

Guide complet MySQL 5, par Antoine Dinimant, éd. MicroApplication
http://www.microapp.com/livre_mysql_7873.html


Avatar
Pif
Heu...

petites questions : ce que tu proposes, c'est pas exactement une
jointure... et justement, une jointure ou un IN son plus lourd...
l'autre fois, dans un autre fil, tout le monde me prenait pour un
imbécile quand je criais que c'était une opération très couteuse...


Alors, on avis c'est quoi ? (nb : je ne t'accuses de rien, tu ne
t'étais pas prononcé à l'époque ;) ).

Sinon, petite question, y a-t-il une différence d'optimisation entre
le jointure et le IN ?

Dans la requete que tu propose, l'avantage est que le select imbriqué
est exécuté une fois, il l'est pour chaque itération avec un IN. Du
coup, la jointure prend moins plus de place en mémoire, le IN non mais
plus que la jointure en temps, et le select que tu fais est l'idéal
sauf qu'il n'est évalué qu'une fois, ce qui convient dans notre cas
présent. C'est bien le résumé de la différence entre les trois ?
Question subsidiaire, si tu fais une jointure naturelle avec un where
x=y et pas un join, c'est géré comment, le système détecte et fait
les memes optimisations que dans le join ?
Avatar
Antoun
Pif wrote:
Heu...

petites questions : ce que tu proposes, c'est pas exactement une
jointure...



si, c'est une jointure avec une sous-requête dans le FROM

et justement, une jointure ou un IN son plus lourd...



c'est une question ? le IN (sous-requête) est très mal implémenté par
MySQL. A l'inverse, la jointure est l'opération de base de tous les SGBD
relationnels, elle est donc bien optimisée.

l'autre fois, dans un autre fil, tout le monde me prenait pour un
imbécile quand je criais que c'était une opération très couteuse...



Si tu parlais de IN, tu avais raison, et si tu parlais de la jointure,
c'est tout le monde qui avait raison ;-)

Alors, on avis c'est quoi ? (nb : je ne t'accuses de rien, tu ne
t'étais pas prononcé à l'époque ;) ).



Lire deux tables avec une jointure est plus coûteux que de lire une
table toute seule. Mais on n'a pas inventé le modèle relationnel pour
dire qu'il ne faut faire que du mono-table.

Dans la requete que tu propose, l'avantage est que le select imbriqué
est exécuté une fois, il l'est pour chaque itération avec un IN.



je devine que c'est effectivement là le problème de IN (sous-requête),
mais je n'en suis pas sûr à 100%.

Du
coup, la jointure prend moins plus de place en mémoire,



moins plus ???

le IN non mais
plus que la jointure en temps, et le select que tu fais est l'idéal
sauf qu'il n'est évalué qu'une fois, ce qui convient dans notre cas
présent. C'est bien le résumé de la différence entre les trois ?



euh... je n'ai rien compris. C'est quoi le troisième ?

En fait, MySQL implémente les sous-requête dans le FROM en créant une
table temporaire. Cela peut être pénalisant (ça veut dire des accès
disque) ou avantageux selon le volume, mais de toute façon c'est bcp
mieux qu'un IN !

Question subsidiaire, si tu fais une jointure naturelle avec un where
x=y et pas un join, c'est géré comment, le système détecte et fait
les memes optimisations que dans le join ?



Je ne suis pas un spécialiste de l'optimisation, et je n'en sais rien.
Fred "SQLpro" Brouard dit qu'il existe des cas où le JOIN permet à
l'optimiseur de mieux calculer son plan d'exécution et je le crois, mais
je ne sais pas si ça s'applique à MySQL.

Dans tous les cas, il faut préférer le JOIN pour des raisons de
lisibilité. Enfin, la "jointure naturelle" (NATURAL JOIN) est un concept
qui n'a rien à voir avec le fait de faire les jointures dans le WHERE.
Cf. http://dev.mysql.com/doc/refman/5.0/fr/join.html.


--
Antoun

Guide complet MySQL 5, par Antoine Dinimant, éd. MicroApplication
http://www.microapp.com/livre_mysql_7873.html
Avatar
Pif
Antoun a écrit :

si, c'est une jointure avec une sous-requête dans le FROM



oups, pardon, j'avais ma lu la position du select....

c'est une question ? le IN (sous-requête) est très mal implémenté par
MySQL. A l'inverse, la jointure est l'opération de base de tous les SGBD
relationnels, elle est donc bien optimisée.



oui, mais sachant que grossomodo

moins plus ???
euh... je n'ai rien compris. C'est quoi le troisième ?



pardon, donc l'idée, c'est que la jointure explose en volume puisqu'elle
est basée grossomodo sur un produit cartésien et une sélection, mais
elle est plus rapide que le IN.
Le IN au contraire prend plus de temps d'exécution mais moins de place
en mémoire ?

Comment est exécuté une jointure, connais tu sa complexité ?


Merci.
Avatar
helios
Pif a écrit :
Antoun a écrit :

si, c'est une jointure avec une sous-requête dans le FROM



oups, pardon, j'avais ma lu la position du select....

c'est une question ? le IN (sous-requête) est très mal implémenté par
MySQL. A l'inverse, la jointure est l'opération de base de tous les
SGBD relationnels, elle est donc bien optimisée.



oui, mais sachant que grossomodo

moins plus ???
euh... je n'ai rien compris. C'est quoi le troisième ?



pardon, donc l'idée, c'est que la jointure explose en volume puisqu'elle
est basée grossomodo sur un produit cartésien et une sélection, mais
elle est plus rapide que le IN.
Le IN au contraire prend plus de temps d'exécution mais moins de place
en mémoire ?

Comment est exécuté une jointure, connais tu sa complexité ?


Merci.




et il se prétend prof d'informatique le pif gadget
Avatar
Pif
Non, je ne suis pas Professeur en informatique.

[Fin de réponse au TROLL pitoyable]

helios a écrit :

et il se prétend prof d'informatique le pif gadget
Avatar
Antoun
pardon, donc l'idée, c'est que la jointure explose en volume puisqu'elle
est basée grossomodo sur un produit cartésien et une sélection,



pas du tout. La jointure correspond *logiquement* à un produit cartésien
filtré, mais cela ne veut pas dire que *procéduralement* l'optimiseur va
d'abord générer le produit cartésien et ensuite le filtrer !

En (très) gros, l'optimiseur va prendre la première table et ne retenir
que les lignes qui vérifient le WHERE. Pour ces lignes-là, il va ensuite
rechercher dans la deuxième table les lignes susceptibles de
correspondre. Mais à aucun moment on ne génère vraiment le produit
cartésien.

mais
elle est plus rapide que le IN.
Le IN au contraire prend plus de temps d'exécution mais moins de place
en mémoire ?



fondamentalement, le IN avec une sous-requête non-corrélée n'est pas
différent d'une jointure. S'il est lent, c'est juste parce que MySQL le
gère très mal, sans doute parce qu'il fait comme si la sous-requête
était corrélée.
Avatar
Pif
sur MySQL 4.1, j'ai déjà observé qu'il y avait une explosion de mémoire
(et temps) pour une jointure alors que la taille du résultat ne le
justifiais pas...

Du coup, j'ai procédé moi même au découpement de la requête en Java
(JDBC), ce qui avait donné lieu à de vives critiques lors d'un fil
précédent.

Je ne dis pas qu'un autre SGBDR aurait fait la même chose, mais je dis
qu'une jointure ca coute cher...

A priori, ca coute au moins
- R x log(S) ou S est la table référencée (CIR ou s'il y a un index
sur le bon attribut de S),
- ou R x S s'il n'y en a pas...

(en supposant R et S deux cardinalités des deux tables mise en jeux).

Sinon, je demande à ce qu'on m'explique l'algo miraculeux ?
1 2