Besoin d'aide sur une requete SQL

Répondre


Cette question est un moyen d’empêcher des soumissions automatisées de formulaires par des robots.
Smileys
:D :) :( :o :shock: :? 8-) :lol: :x :P :oops: :cry: :evil: :twisted: :roll: :wink: :!: :?: :idea: :arrow: :| :mrgreen: =D> #-o =P~ :^o :non: :priere: 8-|
Voir plus de smileys
  Revue du sujet
 

  Étendre la vue Revue du sujet : Besoin d'aide sur une requete SQL

par Gorghor » 07 juin 2007, 02:06

Quant aux tables
albums

Field Type Null Key Default Extra
id int(5) PRI NULL auto_increment
Nom varchar(255)
Date decimal(10,1) 0.0
Prod varchar(255)
Cover varchar(50) Nocover.jpg
Tracklisting longtext
VoteCount int(8) 0
VoteTotal int(8) 0
idgroupe int(10) 0
idmembre int(5) 0
Lineup tinytext YES NULL
vues int(10) unsigned 0
kros tinyint(4) unsigned 0
coms tinyint(4) unsigned 0
user_votes
Field Type Null Key Default Extra
alb_id int(8) unsigned YES MUL NULL
user_id int(8) unsigned YES MUL NULL
vote tinyint(1) unsigned YES MUL NULL
rela_groupe_album
Field Type Null Key Default Extra
rel_id int(11) PRI NULL auto_increment
groupe_id int(11) MUL 0
album_id int(11) MUL 0
album_type tinyint(1) 0
et enfin
groupes
Field Type Null Key Default Extra
id int(5) PRI NULL auto_increment
Groupe varchar(255)
Pays tinyint(4) unsigned 0
Style varchar(255)
Logo varchar(255)
Photo1 varchar(255)
Photo2 varchar(255)
Photo3 varchar(255)
Site varchar(255)
Comment longtext
idmembre int(5) 0
Lineup text YES NULL
Statut tinyint(1) unsigned 0
Label varchar(255)
MAJ int(11) unsigned 0
vues int(10) unsigned 0

par Gorghor » 07 juin 2007, 01:56

Code : Tout sélectionner

id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY <derived2> system NULL NULL NULL NULL 0 const row not found 1 PRIMARY a eq_ref PRIMARY PRIMARY 4 const 1 Using where 1 PRIMARY r ref groupe_id,album_id album_id 4 gorghor.a.id 12 1 PRIMARY g eq_ref PRIMARY PRIMARY 4 gorghor.r.groupe_id 1 2 DERIVED v ref user_id,alb_id,vote alb_id 5 1 Using where; Using temporary; Using filesort 2 DERIVED p ref user_id,alb_id,vote user_id 5 gorghor.v.user_id 2 Using where
c'est ca que tu voulais? :oops:

Nos posts se sont croisés ;)

par Hubert Roksor » 07 juin 2007, 01:53

Plutôt dans phpMyAdmin, ou mieux encore dans le client MySQL si tu testes en local, mais c'est à toi de voir. Tu prends ta requête, tu rajoutes "EXPLAIN" devant et tu exécutes le tout. Ça te renvoit un tableau qui ressemble à ça :

Code : Tout sélectionner

id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE f ALL 27 Using filesort 1 SIMPLE p eq_ref PRIMARY PRIMARY 3 test.f.forum_last_post_id 1 1 SIMPLE u eq_ref PRIMARY PRIMARY 3 test.p.poster_id 1

par Gorghor » 07 juin 2007, 01:46

Excuse mon ignorance, mais comment je fais? Ca se fait directement à l'intérieur du script en php ou depuis la console?

par Hubert Roksor » 07 juin 2007, 01:33

Content que ça marche. Au passage, tu pourrais me donner le résultat d'un EXPLAIN ainsi que le schéma des tables stp ?

par Gorghor » 07 juin 2007, 01:25

Le résultat final dans son contexte: http://www.crypticmadness.com/Metal/album.php?id=74

:D

par Gorghor » 07 juin 2007, 01:03

Code : Tout sélectionner

"SELECT a.id, a.Nom, a.Cover, a.Date, g.Groupe, tmp.cnt FROM ( SELECT p.alb_id, COUNT(*) AS cnt FROM user_votes v JOIN user_votes p ON p.user_id = v.user_id AND p.alb_id <> v.alb_id AND p.vote IN (5, 6, 7) WHERE v.alb_id = '$ALB_ID' AND v.vote IN (5, 6, 7) GROUP BY p.alb_id ORDER BY cnt DESC LIMIT 5 ) AS tmp JOIN albums a ON a.id = tmp.alb_id JOIN rela_groupe_album r ON r.album_id = a.id JOIN groupes g ON g.id = r.groupe_id "
Fonctionne parfaitement.
Oui quand j'y pense c'était un peu bête de vouloir lister également les membres. Je m'emmelais légèrement les pinceaux là.

Cela dit, GROUP_CONCAT(xxx) AS yyy pourrait régler un autre problème qui me tracasse depuis longtemps!

Merci encore!

par Hubert Roksor » 07 juin 2007, 00:41

Si j'ai bien compris, tu veux trouver les albums les plus appréciés par les utilisateurs qui ont apprécié un album donné.

En entrée tu as le numéro d'album "v" et en sortie une liste d'albums "p" qui ont reçu une bonne note par les utilisateurs qui ont donné une bonne note à "v". Ça donnerait quelque chose comme

Code : Tout sélectionner

SELECT p.alb_id, COUNT(*) AS cnt FROM user_votes v JOIN user_votes p ON p.user_id = v.user_id AND p.alb_id <> v.alb_id AND p.vote IN (5, 6, 7) WHERE v.alb_id = '$ALB_ID' AND v.vote IN (5, 6, 7) GROUP BY p.alb_id ORDER BY cnt DESC LIMIT 5
En revanche impossible de récupérer en même temps les infos utilisateurs puisque les résultats sont groupés par album. Tu pourrais utiliser GROUP_CONCAT() [spécifique à MySQL] pour récupérer la liste des utilisateurs puis utiliser cette liste en PHP pour récupérer leurs noms dans une nouvelle requête, mais l'un dans l'autre ça ne paraît pas très utile. Enfin, à toi de voir.

Code : Tout sélectionner

SELECT p.alb_id, COUNT(*) AS cnt, GROUP_CONCAT(p.user_id) AS user_ids FROM ...identique...

par Gorghor » 07 juin 2007, 00:23

Re

Oki, je comprend mieux maintenant! Merci pour l'explication très claire. Ca va m'aider sur d'autres requetes aussi! (Tu avais oublié un FROM dans ton exemple de requête, donc je ne comprenais pas grand chose ;) )

Voici donc la version optimisée de la requête originale (testée et approuvée)

Code : Tout sélectionner

"SELECT a.id, a.Nom, a.Cover, a.Date, g.Groupe, u.username, tmp.thisvote, tmp.thatvote FROM ( SELECT v.alb_id, v.user_id, v.vote as thisvote, p.vote as thatvote, p.alb_id as thatid FROM user_votes v JOIN user_votes p ON p.user_id = v.user_id AND p.alb_id <> v.alb_id AND p.vote IN (5, 6, 7) WHERE v.alb_id = '$ALB_ID' AND v.vote IN (5, 6, 7) GROUP BY p.alb_id ORDER BY RAND() LIMIT 5 ) AS tmp JOIN albums a ON a.id = tmp.thatid JOIN rela_groupe_album r ON r.album_id = a.id JOIN groupes g ON g.id = r.groupe_id JOIN phpbb_users u ON u.user_id = tmp.user_id"
Bon ca fait une heure que j'essaye là donc je vais me permettre de vous demander:

Vous voyez le GROUP BY: je veux savoir combien d'entrées sont touchées à chaque fois par cette partie (donc en non-SQL, savoir combien de membres ont donné une note de plus de 4 a ces deux albums) et mémoriser ce nombre, pour ensuite faire un ORDER BY sur toute la requête par rapport a cette valeur.

Voilà. J'ai du mal (peut etre la fatigue) a voir comment réaliser cela, sans ajouter encore une requete.

par Hubert Roksor » 06 juin 2007, 23:39

Ce sont en fait deux choses différentes. Tu peux mettres des sous-requêtes un peu où tu veux, si tu en mets dans la liste des colonnes à récupérer alors oui ça va être lent parce que ça équivaut à exécuter une requête supplémentaire par ligne de résultat (10 résultats = 10 + 1 requêtes). Les requêtes dans la clause FROM sont appelées "tables dérivées" et ne sont exécutées qu'une seule fois.

Dans l'exemple précis, au lieu d'avoir une requête qui tire 5 enregistrement au hasard sur une jointure de 6 tables sur des milliers d'enregistrements on a une jointure de 2 tables sur des milliers d'enregistrement, puis une jointure de 5 tables sur 5 enregistrements. Pour donner une estimation de la complexité, au lieu d'avoir 6 x 1000 on a 2 x 1000 + 5 x 5.

par Gorghor » 06 juin 2007, 22:49

Je n'ai pas encore essayé la requete, je vais m'y appliquer de suite. Cela dit, j'avais cru comprendre que de mettre plusieurs select a la suite
SELECT a.* (SELECT blah blah)
était une méthode extremement lente. Mes sources étaient donc fausses?

@ Sékiltoyai: Je pense que comme ca , ca ira. Mais merci, j'y jetterai un oeil tout de même.

par Sékiltoyai » 06 juin 2007, 16:53

Et si tu ne trouves toujours pas ton bonheur, il y a plus bourrin encore : les VIEW et les TEMPORARY TABLE...

par Gorghor » 06 juin 2007, 16:38

Bon alors deja. Merci bcp pour ta réponse. Je ne connaissais pas encore ce principe (j ai "appris" le SQL a la volé, selon mes besoins, donc optimisation = 0).

Des que je serais chez moi, je tenterai de mettre cette requete sur le site et j essairai de formuler la requete que je voulais.

Merci encore.

par Hubert Roksor » 06 juin 2007, 16:27

Au passage, voici la même requête réécrite pour utiliser une table dérivée. Elle devrait retourner les même résultats tout en étant plus rapide. Si mes souvenirs sont bons, les tables dérivées sont possibles à partir de MySQL 4.1

Code : Tout sélectionner

SELECT a.id, a.Nom, a.Cover, a.Date, g.Groupe, u.username, tmp.thisvote, tmp.thatvote FROM ( SELECT v.alb_id, v.user_id, v.vote as thisvote, p.vote as thatvote FROM user_votes v JOIN user_votes p ON p.user_id = v.user_id AND p.alb_id <> v.alb_id AND p.vote IN (5, 6, 7) WHERE v.alb_id = '$ALB_ID' AND v.vote IN (5, 6, 7) GROUP BY p.alb_id ORDER BY RAND() LIMIT 5 ) AS tmp JOIN albums a ON a.id = tmp.alb_id JOIN rela_groupe_album r ON r.album_id = a.id JOIN groupes g ON g.id = r.groupe_id JOIN phpbb_users u ON u.user_id = tmp.user_id

par Hubert Roksor » 06 juin 2007, 16:21

Just un mot pour dire que l'utilisation de jointures sous forme de virgules rend la lecture des requêtes très difficiles car elle pousse les conditions de jointure dans la clause WHERE. Si tu réécris ta requête pour utiliser "JOIN" (synonyme de "INNER JOIN", synonyme des virgules) tout devient plus clair

Code : Tout sélectionner

SELECT a.id, a.Nom, a.Cover, a.Date, g.Groupe, u.username, v.vote as thisvote, p.vote as thatvote FROM user_votes v JOIN user_votes p ON p.user_id = v.user_id AND p.alb_id <> v.alb_id AND p.vote IN (5, 6, 7) JOIN albums a ON a.id = p.alb_id JOIN rela_groupe_album r ON r.album_id = a.id JOIN groupes g ON g.id = r.groupe_id JOIN phpbb_users u ON u.user_id = v.user_id WHERE v.alb_id = '$ALB_ID' AND v.vote IN (5, 6, 7) GROUP BY p.alb_id ORDER BY RAND() LIMIT 5
Pour le reste... c'est un problème compliqué donc très difficile à dire sans connaitre les données de la base. Exécute un EXPLAIN de ta requête et publie le résultat, ça peut aider.

Je vois que les données des tables a, r, g et u n'influe pas sur les résultats de la requête, donc tu devrais pouvoir faire le plus gros de la requête sous la forme d'une table dérivée (v,p) et joindre le résultat aux tables auxilliaires. De plus, vérifie que tu possèdes un index (probablement UNIQUE, d'ailleurs) sur (user_id, alb_id) ainsi que sur (alb_id) ou peut-être même sur (alb_id, vote) selon la répartition des votes (utile s'il y a une faible proportion de 5, 6 et 7, sinon oublie).

À part ça, j'ai un peu de mal à m'imaginer ce que tu cherches à faire (pour dire la vérité je ne suis pas super motivé non plus, essaie de le formuler sous la forme d'une requête si tu veux attirer mon attention :lol:), est-ce que la requête ci-dessus fait ce que tu veux obtenir ? Sinon, en quoi diffère-t'elle de ce que tu souhaites obtenir ? (en termes fonctionnels si possible, merci)