Jointure multiple

Petit nouveau ! | 8 Messages

22 déc. 2011, 13:39

Bonjour à tous,

Je vais en faire sourire plus d'un, c'est clair, mais je suis en pleine découverte de PHP/MySQL et je m'amuse comme un petit fou. Faut s'accrocher, mais c'est dingue tout ce qu'on peut faire avec ces deux outils :!: Comme je débute totalement, je bute sur quelques trucs qui vont probablement vous sembler rudimentaires... Mais bon, un petit coup de main ne serait pas de refus, si vous avez quelques minutes.

Mon principal problème est le suivant :

Je crée une base de données de mes CD audio. Pour l'instant, rien de spécial, j'ai crée deux tables :
- une table n°1 (nommée album) pour la liste des CD. Les colonnes de cette table sont les suivantes : id_cd, name_cd, author_cd, lenght_cd, track1. Le champ "track1" étant juste un nombre, qui correspond à l'ID de la table n°2
- une table n°2 (nommée tracklist) pour la liste complète des pistes que l'on peut trouver sur l'ensemble de tous les CD. Les colonnes sont les suivantes : id_track, title_track, author_track, lenght_track.

J'ai effectué une jointure entre la colonne track1 de la liste des CD (1ere table), et la colonne id_track de la liste des pistes audio (2eme table).

Pour l'instant tout se passe bien : quand on lance la requête en PHP, on affiche la liste des CD, avec le titre de l'album, le nom de l'interprète, la durée de l'album, et le nom de la piste audio 1 (qui correspond donc au texte trouvé dans title_track de la table n°2).

Mon problème : comment faire pour ajouter des pistes à un CD ? Dois-je créer autant de colonnes dans ma table n°1 (track1, track2, track3...) ? Et ensuite, comment faire une jointure entre les colonnes track1, track2, track 3 de la table n°1, et la colonne de id_track de la table n°2 ?

Voilà, ce que donne mon code pour l'instant :

$reponse = $bdd->query('select name_cd, track1, id_track, title_track FROM album LEFT JOIN tracklist ON album.track1=tracklist.id_track');

J'espère que mon message n'est pas trop long, j'ai essayé d'être le plus clair possible, mais ce n'est pas super évident. Si vous avez besoin d'autres précisions, dites-moi.
En tous les cas, merci d'avance de votre aide, car je sèche.
++@

Lost.

ViPHP
ViPHP | 2577 Messages

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.

Petit nouveau ! | 8 Messages

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:

ViPHP
AB
ViPHP | 5818 Messages

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.

Petit nouveau ! | 8 Messages

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.

Mammouth du PHP | 672 Messages

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.

Petit nouveau ! | 8 Messages

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 !!!

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 8758 Messages

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>';
?>
@+
Il en faut peu pour être heureux ......

Petit nouveau ! | 8 Messages

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é.

Petit nouveau ! | 8 Messages

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...

Eléphant du PHP | 209 Messages

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

Petit nouveau ! | 8 Messages

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.

Eléphant du PHP | 209 Messages

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

Petit nouveau ! | 8 Messages

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 :)

Eléphant du PHP | 209 Messages

13 janv. 2012, 00:31

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