Jointure multiple

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 : Jointure multiple

Re: Jointure multiple

par Cyrano » 17 janv. 2012, 18:15

$disquesId = $database->query('SELECT id_disque FROM disques LIMIT 0, 10');

$answer = $database->query("SELECT * FROM pistes
RIGHT JOIN disques ON pistes.id_disque=disques.id_disque
RIGHT JOIN chansons ON pistes.id_chanson=chansons.id_chanson
RIGHT JOIN artistes ON pistes.id_disque=artistes.id_artiste
WHERE id_disque IN ('.implode(",", $disquesId).')
ORDER BY pistes.id_artiste, pistes.id_disque, pistes.id_piste
");
J'ai peut-être fait une erreur au niveau des quotes...
Précisément : tu as inversé.

Le truc : commence par écrire une requête SQL normale avec des valeurs en dur, sans PHP, sans variables;
Exemple :
SELECT *
FROM pistes
  RIGHT JOIN disques ON pistes.id_disque=disques.id_disque
  RIGHT JOIN chansons ON pistes.id_chanson=chansons.id_chanson
  RIGHT JOIN artistes ON pistes.id_disque=artistes.id_artiste
WHERE id_disque IN (1,2,3,4)
ORDER BY pistes.id_artiste, pistes.id_disque, pistes.id_piste
Ensuite, colle le tout entre les guillemets d'une variable, ça devient alors :
$asnwer = "SELECT *
FROM pistes
  RIGHT JOIN disques ON pistes.id_disque=disques.id_disque
  RIGHT JOIN chansons ON pistes.id_chanson=chansons.id_chanson
  RIGHT JOIN artistes ON pistes.id_disque=artistes.id_artiste
WHERE id_disque IN (1,2,3,4)
ORDER BY pistes.id_artiste, pistes.id_disque, pistes.id_piste";
Maintenant, on va remplacer les valeurs en dur par une valeur définie en PHP : comme on a utilisé des guillemets, on va utiliser encore des guillemets pour sortir de la syntaxe SQL. Mais je te suggère de créer cette valeur dans une autre variable en amont de ta requête, et ensuite seulement l'insérer dans la chaine SQL, comme ceci :
$cdIds = implode(',', $disquesId);
$asnwer = "SELECT *
FROM pistes
  RIGHT JOIN disques ON pistes.id_disque=disques.id_disque
  RIGHT JOIN chansons ON pistes.id_chanson=chansons.id_chanson
  RIGHT JOIN artistes ON pistes.id_disque=artistes.id_artiste
WHERE id_disque IN (". $cdIds .")
ORDER BY pistes.id_artiste, pistes.id_disque, pistes.id_piste";
Refais l'essai ;)

Quant au sujet de départ, je n'ai parcouru l'ensemble que rapidement en diagonale, mais le problème de modélisation de la base de données n'est pas à prendre à la légère, c'est en général le cœur de l'application, si tu construis une base bancale, l'application sera bancale. Par ailleurs, si ton modèle de données doit être remanié,n'oublie pas que ça voudra dire qu'il faudra refaire toutes les requêtes qui passent par les tables modifiées, et le code PHP qui va avec probablement aussi. Intéresse-toi à la méthode MERISE pour la modélisation de bases de données, il y a un bon tuto sur le sujet sur developpez.com. Bon courage :)

Re: Jointure multiple

par Shenryu » 13 janv. 2012, 00:31

Il faut que tu convertisses le résultat de ta premiere requête en array simple.

Re: Jointure multiple

par LostUniverse » 12 janv. 2012, 20:48

Encore merci de ton aide, Shenryu.

J'ai essayé d'ajouter le implode () dans la requête, mais j'obtiens toujours une erreur :

Catchable fatal error: Object of class PDOStatement could not be converted to string in C:\wamp\www\temp\mycd.php on line 15


Mon code est désormais comme ça :
$disquesId = $database->query('SELECT id_disque FROM disques LIMIT 0, 10');

$answer = $database->query("SELECT * FROM pistes
RIGHT JOIN disques ON pistes.id_disque=disques.id_disque
RIGHT JOIN chansons ON pistes.id_chanson=chansons.id_chanson
RIGHT JOIN artistes ON pistes.id_disque=artistes.id_artiste
WHERE id_disque IN ('.implode(",", $disquesId).')
ORDER BY pistes.id_artiste, pistes.id_disque, pistes.id_piste
");
J'ai peut-être fait une erreur au niveau des quotes, mais même après les avoir triturées dans tous les sens, ça ne fonctionne pas...
Je désespère pas, on va bien finir par y arriver :)

Re: Jointure multiple

par Shenryu » 12 janv. 2012, 14:05

Le LIMIT dans une sous-requête n'existe pas sous Mysql.

Ce que tu dois pouvoir faire en revanche, c'est de récupérer tes id disque à partir d'une première requête qui contient un LIMIT et de placer tes résultats dans ta clause WHERE de ta seconde requête.

Première requête :
SELECT id_disque FROM disques LIMIT 0, 10;
Tu obtiens un résultat php sous forme de tableau de valeur $disquesId = array(1, 3, 5, 6, 7, 8, 12, 15, 17, 20); par exemple.

Seconde requête contenant ton tableau php :
SELECT * FROM pistes
RIGHT JOIN disques ON pistes.id_disque=disques.id_disque
RIGHT JOIN chansons ON pistes.id_chanson=chansons.id_chanson
RIGHT JOIN artistes ON pistes.id_disque=artistes.id_artiste
WHERE id_disque IN ('.implode(",", $disquesId).')
ORDER BY pistes.id_artiste, pistes.id_disque, pistes.id_piste

Re: Jointure multiple

par LostUniverse » 12 janv. 2012, 13:52

Merci de ton message Shenryu.
Je viens d'essayer, mais ça ne marche pas. J'obtiens un message d'erreur (Fatal error: Call to a member function fetch() on a non-object in C:\wamp\www\temp\mycd.php on line 18').
Ta réponse est peut-être un début de piste, je vais tenter d'autres approches dans le même esprit.

Re: Jointure multiple

par Shenryu » 12 janv. 2012, 12:59

Salut,

Essaye peut-être quelque chose du genre :
SELECT * FROM pistes
RIGHT JOIN disques ON pistes.id_disque=disques.id_disque
RIGHT JOIN chansons ON pistes.id_chanson=chansons.id_chanson
RIGHT JOIN artistes ON pistes.id_disque=artistes.id_artiste
WHERE id_disque IN (SELECT id_disque FROM disques LIMIT 0, 10)
ORDER BY pistes.id_artiste, pistes.id_disque, pistes.id_piste

Re: Jointure multiple

par LostUniverse » 12 janv. 2012, 10:50

Bon, j'ai parlé trop vite car je bloque depuis hier sur l'affichage "page par page".

Afin d'afficher la liste de CD sur plusieurs pages, j'intègre une limite dans ma requête (à l'aide d'une variable, mais on va faire simple). Disons que pour l'instant je veuille limiter l'affichage aux 10 premiers CD de ma liste, en y intégrant à chaque fois le contenu. En théorie, je devrais faire ça :
$answer = $database->query('SELECT *
FROM pistes
RIGHT JOIN disques
ON pistes.id_disque=disques.id_disque
RIGHT JOIN chansons
ON pistes.id_chanson=chansons.id_chanson
RIGHT JOIN artistes
ON pistes.id_disque=artistes.id_artiste
ORDER BY pistes.id_artiste, pistes.id_disque, pistes.id_piste
LIMIT 0,10
');
Le problème, c'est que ça me renvoie les dix premières chansons, et non les 10 premiers albums. Dans le SELECT, on peut évidemment ne travailler que sur la liste des CD (la table 'disques'), mais dans ce cas, je n'ai plus la liste des chansons de chaque CD. J'ai essayé à l'aide d'une sous-requête, mais ne suis pas arrivé au bon résultat. Je dois m'y prendre mal, mais je ne vois pas la solution #-o

Si quelqu'un voit une meilleure approche, ou a juste une petite piste : merci beaucoup par d'avance !
Je sèche totalement...

Re: Jointure multiple

par LostUniverse » 11 janv. 2012, 11:12

Hello à tous,

Tout d'abord, merci à moogli pour son dernier message, qui m'a pas mal aidé dans mon code. C'est clair que l'indentation, ça apporte une grande bouffée d'air frais. J'avais beaucoup avancé sur mon projet, mais hier, comme je ne m'y retrouvais vraiment plus, il a fallu appliquer le conseil de moogli. Après avoir indenté le code, j'ai retrouvé plein de trucs qui n'avaient rien à faire là (beaucoup de "{" et de ";" ). C'est carrément mieux maintenant.
Mon code avance. Pour intégrer plusieurs titres de CD à une même ligne, j'utilise la méthode suivante :
Chaque morceau comporte un numéro (le fameux "id_piste" conseillé par macgawel, un peu plus haut dans ce post). Le code regarde si cet id_piste est égale à 1. Ce qui signifie qu'il s'agit d'un nouvel album. Dans ce cas, la boucle s'exécute et affiche une ligne complète.
Si ce n'est pas le cas (id_piste est différent de 1), la boucle ne s'intéresse qu'aux cellules du tableau dans lesquelles il y a le numéro de la piste (colonne 2 dans le tableau final), le titre des chansons (colonne 3 dans le tableau final) et l'interprète (colonne 4). Je vous passe toutes les petites conditions que j'ai ajoutées à droite à gauche, mais ça fonctionne nickel !

Là je bute sur un autre point qui est l'affichage par page (je voudrais que la page affiche un certain nombre de CD, et pas un certain nombre de morceau comme c'est le cas pour l'instant), mais j'avance un peu chaque jour. Je vais bien finir par trouver :twisted:
Encore un grand merci à tous pour vos précieux conseils, qui m'ont bien fait progressé.

Re: Jointure multiple

par moogli » 31 déc. 2011, 15:26

salut,

il faut que tu organise une césure sur le "nom_cd" afin de pouvoir fermer le tableau et le ré ouvrir ensuite, ou simplement insérer une ligne vide d'une autre couleur.

pour être que te album ne se mélange pas je te conseil l'ajout d'un order by nom_cd, id_piste (parce que la si tu ajoute une piste, oubliée au départ par exemple, après avoir ajouté d'autre album c'est la merde, alors que ce n'est pas du tout un soucis pour le stockage faut juste y penser :mrgreen:

je te conseil aussi d'indenter ton code afin d'y voir plus clair (parce que la c'est pas simple voir rapidement qui est dans quel bloc.).
d'ailleurs tu a un bloc sans instruction au milieu du while c'est voulu ou il manque un truc ?

tu peux avoir un code dans ce style
<?php
echo '<table>';
echo '<tr><td>Album</td><td>N° de piste</td><td>Chanson</td><td>Interprète</td></tr>';
$color = 1;
$al = null;
while ($data = $answer->fetch()){
	if ($al === null) 
		$al = $data['npm_cd'];
	if ($data['nom_cd'] != $al) {
		echo '<tr style="background-color: #FF00AA;"><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td></tr>';
		$al = $data['npm_cd'];
	}
	if($color === 1){ // vérification du type car si $color contient "true" c'est bon, d'ailleurs $color contient quoi que ce soit hors false, 0 et null ça sera bon avec le == :)
		echo '<tr bgcolor="#AAAAAA">'; 
		$color=2;
	}
	else {
		echo '<tr bgcolor="#CCCCCC">'; 
		$color=1;
	}

	echo '<td>'. $data['nom_cd'].'</td>';
	echo '<td>'. $data['id_piste'].'</td>';
	echo '<td>'. $data['nom_chanson'].'</td>';
	echo '<td>'. $data['nom_artiste'].'</td>';
	echo '</tr>';
}   

$answer->closeCursor();
echo '</table>';
?>
@+

Re: Jointure multiple

par LostUniverse » 31 déc. 2011, 14:13

C'est exactement ça, macgawel : merci beaucoup pour ton exemple : super choix dans les titres des morceaux et des interprètes :lol:. Effectivement, je veux que ma basse soit à la fois interrogeable morceau (lister tous les albums avec Mon beau sapin à l'intérieur), mais je veux aussi que l'on puisse afficher le contenu d'un ou de plusieurs CD d'un même artiste.

J'étais parti dans la même veine que ce que tu as décrit, à l'exception des clés étrangères. J'étais totalement passé à côté de cette notion, merci de l'info. Je l'ai mise en application, ça va m'être très utile. La seule chose qui ne fonctionne pas pour l'instant, ce sont les champs vides, lorsqu'on ne connaît pas l'artiste ou le titre d'un morceau. J'ai une erreur, mais ce n'est pas bien grave pour l'instant.

Jusqu'à présent, tout se passe bien. En revanche, j'ai un petit souci lorsque je veux afficher le contenu des CDs. J'ai fait une boucle pour l'afficher, le contenu se présente bien, sauf que chaque piste est référencée sur une ligne.

Je m'explique, voilà ce que j'obtiens sous forme de tableau. J'ai repris la fameuse playlist de macgawel :

Album..............|.N° de piste.|.Chanson.........|.Interprète
------------------------------------------------------------------
Best of Tino Rossi..|.1...........|.Petit papa Noël.|.Tino rossi
------------------------------------------------------------------
Best of Tino Rossi..|.2...........|.Petit papa Noël.|.Tino rossi
------------------------------------------------------------------
Les chant de Noël..|.1...........|.Petit papa Noël.|.Chantal Goya
------------------------------------------------------------------
Les chant de Noël..|.2...........|.Bécassine.......|.Chantal Goya
------------------------------------------------------------------
Les chant de Noël..|.3...........|.Petit papa Noël |.Chantal Goya


Et voilà ce que j'aimerais avoir :

Album................|.N° de piste.|.Chanson.........|.Interprète
------------------------------------------------------------------
Best of Tino Rossi.|.1...........|.Petit papa Noël.|.Tino rossi
.........................|.2...........|.Petit papa Noël.|.Tino rossi
------------------------------------------------------------------
Les chant de Noël..|.1...........|.Petit papa Noël.|.Chantal Goya
..........................|.2...........|.Bécassine.......|.Chantal Goya
..........................|.3...........|.Petit papa Noël |.Chantal Goya

Cette présentation deviendra particulièrement intéressante lorsqu'il faudra ajouter la couverture de l'album, par exemple : plutôt que de l'avoir pour chaque ligne, j'aimerai l'avoir pour chaque album.
j'ai essayé d'intégrer un GROUP BY dans mon SELECT, mais les du coup, ne sont affichées que les pistes 1 de chaque album.

Si le cœur vous en dit, voici mon code :

<?php
try
{
$pdo_options[PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION;
$database = new PDO('mysql:host=localhost;dbname=mycd', 'chantal', 'goya');

$answer = $database->query('SELECT *
FROM pistes
RIGHT JOIN disques
ON pistes.id_disque=disques.id_disque
RIGHT JOIN chansons
ON pistes.id_chanson=chansons.id_chanson
RIGHT JOIN artistes
ON pistes.id_disque=artistes.id_artiste
');

echo '<table>';
echo '<tr><td>Album</td><td>N° de piste</td><td>Chanson</td><td>Interprète</td></tr>';
$color=1;
while ($data = $answer->fetch())
{
if($color==1){echo "<tr bgcolor='#AAAAAA'>"; $color=2;}
else {echo "<tr bgcolor='#CCCCCC'>"; $color=1;}

{
echo '<td>'. $data['nom_cd'].'</td>';
echo '<td>'. $data['id_piste'].'</td>';
echo '<td>'. $data['nom_chanson'].'</td>';
echo '<td>'. $data['nom_artiste'].'</td>';
echo '</tr>';
}   

}   

    $answer->closeCursor();
echo '</table>';
}

catch(Exception $e)
{
    die('Erreur : '.$e->getMessage());
}


?>


Merci d'avance si vous avez une solution. Et surtout : bonnes fêtes de fin d'année à tous !!!

Re: Jointure multiple

par macgawel » 30 déc. 2011, 14:42

Mais ce faisant, que faire lorsqu'une même piste (ou un titre de chanson) est présente sur plusieurs albums ? Je vais devoir également créer autant d'identifiants, donc de colonnes, qu'il y a de présence sur les albums ? ? Admettons qu'un titre soit présent sur un album en version studio, puis sur un album live, puis sur une compilation, puis sur un single en version étendue ou remixées, j'en suis déjà à 4 colonnes d'identifiant pour une seule piste...
Du coup, je me retrouve à peu près dans la même situation :oops:
En fait, il faut que tu réfléchisses à l'organisation de ta base...
En particulier, quel sera l'utilisation ?
Par exemple, est-ce que tu veux pouvoir trouver tous les albums dans lesquels il y a "Mon beau sapin", ou juste lister pour chaque CD le contenu ?
Tu veux pouvoir différencier un Live d'un album studio ?
Et comment comptes-tu gérer les cas où une même chanson a été chantée par plusieurs auteurs
En fonction de ce que tu veux faire, il faudra ajuster l'organisation de tes tables...

Là comme ça, je dirais qu'il te faudrait dans l'idéal :
- Une table Artistes (id_artiste, nom_artiste)
- Une table Chansons (id_chanson, nom_chanson)
- Une table Disques (id_disque, nom_cd) (pas l'artiste, pour pouvoir gérer les CD genre "Le Noël des chanteurs d'opérette" :roll: )

Jusque là, c'est plutôt simple...Mais il va maintenant falloir gérer les pistes de tes CD. ET c'est là que ça se corse...
Une piste de CD est référencée de manière unique par le disque + le numéro de la piste. Et ce sont deux informations obligatoires. On utilisera donc les champs id_disque (clé étrangère liée à la table Disques) et id_piste comme index.
Et une piste de CD, c'est une chanson chantée par un chanteur. Mais (à voir suivant ton modèle) on peut ne pas connaître le nom/chanteur...
Du coup, il va falloir "pointer" sur les tables Artistes, Chansons et Disques.
On va donc rajouter id_artiste (clé étrangère liée à la table Artistes) et id_chanson (clé étrangère liée à la table Chansons).
Il ne restera plus qu'à ajouter les informations "secondaires", comme la durée.


Exemple :

Code : Tout sélectionner

ARTISTES (1, 'Tino rossi') (2, 'Chantal Goya') CHANSONS (1, 'Petit papa Noël') (2, 'Bécassine') DISQUES (1, 'Best of Tino Rossi') (2, 'Les chant de Noël') (3, 'Chantal Goy Live') PISTES (1, 1, 1, 1) => Best of Tino Rossi, première piste, Petit papa Noël par Tino Rossi (1,2,,1) => Best of Tino Rossi, deuxième piste, titre inconnu par Tino Rossi (2,1,1,1) => Les chants de noël, première piste, Petit papa Noël par Tino Rossi (2,2,1,2) => Les chants de noël, deuxième piste, Petit papa Noël par Chantal Goya (2,3,1,) => Les chants de noël, troisième piste, Petit papa Noël (chanteur inconnu)
Après, c'est juste un exemple d'organisation, à toi de voir ce que tu attends de ta BDD.

Re: Jointure multiple

par LostUniverse » 29 déc. 2011, 12:36

Hello AB, je te remercie de ton aide, ça marche parfaitement avec ta méthode ! Merci également à Mazarini, qui avait commencé à m'aiguiller lors de mon premier post.
Bon, je ne parviens pas encore à tout faire avec mes requêtes, mais je progresse un peu chaque jour. La base prend forme, la mise en page en php aussi. J'espère que dans quelques mois, en relisant mes messages, je rigolerai bien en me disant "comment c'était simple, pourtant" :)
++@
Lost.

Re: Jointure multiple

par AB » 22 déc. 2011, 18:47

Nan mais faut pas créer des colonnes pour chaque titre dans ta table 2, faut les empiler en les associant au cd de ta table 1

Exemple basique table 2 :

id cd | Titres
--------------
cd_1 | titre1
cd_1 | titre2
cd_1 | titre3
cd_1 | titre4
cd_1 | titre5
cd_2 | titre1
cd_2 | titre2
cd_2 | titre3
cd_2 | titre4
cd_2 | titre5
etc.

Re: Jointure multiple

par LostUniverse » 22 déc. 2011, 18:11

Bonjour Mazarini,

Merci de ta réponse. Je saisis bien l'idée. Mais ce faisant, que faire lorsqu'une même piste (ou un titre de chanson) est présente sur plusieurs albums ? Je vais devoir également créer autant d'identifiants, donc de colonnes, qu'il y a de présence sur les albums ? ? Admettons qu'un titre soit présent sur un album en version studio, puis sur un album live, puis sur une compilation, puis sur un single en version étendue ou remixées, j'en suis déjà à 4 colonnes d'identifiant pour une seule piste...
Du coup, je me retrouve à peu près dans la même situation :oops:

Re: Jointure multiple

par Mazarini » 22 déc. 2011, 16:55

Bonjour,

C'est dans la table des track qu'il faut mettre l'identifiant de l'album et non pas dans la table des albums qu'il faut mettre un numéro de track.