[MySQL 5.0.51 (Free.fr)]Jointure sur trois tables

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 : [MySQL 5.0.51 (Free.fr)]Jointure sur trois tables

par Cyrano » 26 janv. 2008, 18:26

Ok, c'est un peu ce que je craignais. Un type ENUM serait plus approprié. Mais surtout, on a là une table relationnelle typique entre la table "deputes" et la table "lois" et dans ce cas, on fait sauter la colonne "id" de la table "votes" et la clé primaire est alors composée des deux clés étrangères, ce qui donnerait :

Code : Tout sélectionner

CREATE TABLE IF NOT EXISTS `votes` ( `id_dep` int(150) NOT NULL COMMENT 'ID du député afin de lui rattacher son vote.', `id_loi` int(150) NOT NULL COMMENT 'ID de la loi concerné par le vote dont il est question.', `vote` ENUM('Absent','Pour','Contre','Abstention') NOT NULL default 'Absent' COMMENT 'Le député a t-il voté pour la loi ?', PRIMARY KEY (`id_dep`,`id_loi`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci COMMENT='Table contenant les votes de chaque député concernant ch';
Avec pour résultat une table qui ressemble à ceci :

Code : Tout sélectionner

mysql> DESCRIBE `votes`; +--------+---------------------------------------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +--------+---------------------------------------------+------+-----+---------+-------+ | id_dep | int(150) | NO | PRI | NULL | | | id_loi | int(150) | NO | PRI | NULL | | | vote | enum('Absent','Pour','Contre','Abstention') | NO | | Absent | | +--------+---------------------------------------------+------+-----+---------+-------+ 3 rows in set (0.02 sec)
La clé primaire est une clé multiple et tu ne peux avoir qu'un seul vote par député pour une loi donnée. La colonne "id" devient parfaitement inutile.

Reste donc à voir le processus de gestion des enregistrements : si on suit la logique, il faudrait que pour chaque loi on enregistre les votes pour tous les députés en laissant la valeur par défaut si on ignore comment tel ou tel député à voté.

Dans ce cas, on aura alors de toutes façons une ligne par député pour chaque loi dans la table des votes. Dans ce cas, la requête suivante va donner le produit cartésien recherché :

Code : Tout sélectionner

mysql> SELECT l.id, l.intitule, d.id, d.nom, v.vote -> FROM lois l -> LEFT OUTER JOIN votes v -> ON l.id = v.id_loi -> LEFT OUTER JOIN deputes d -> ON v.id_dep = d.id -> AND d.id IN (SELECT DISTINCT d1.id FROM deputes d1) -> WHERE l.id IN (1, 2, 3) -> ORDER BY l.id, d.id; +----+-------------------+------+-----------+--------+ | id | intitule | id | nom | vote | +----+-------------------+------+-----------+--------+ | 1 | Impôt 45 centimes | 1 | Lamartine | Pour | | 1 | Impôt 45 centimes | 2 | Leroux | Absent | | 1 | Impôt 45 centimes | 3 | Albert | Contre | | 2 | Salaire président | 1 | Lamartine | Contre | | 2 | Salaire président | 2 | Leroux | Contre | | 2 | Salaire président | 3 | Albert | Absent | | 3 | Loi sur la presse | 1 | Lamartine | Absent | | 3 | Loi sur la presse | 2 | Leroux | Pour | | 3 | Loi sur la presse | 3 | Albert | Pour | +----+-------------------+------+-----------+--------+ 9 rows in set (0.00 sec)
Partant de là, tu as toutes les informations nécessaires et tu peux les traiter dans une boucle pour trier et afficher dans le bon ordre dans la construction de ton tableau.

par Leotus » 26 janv. 2008, 14:41

Pour le champ "vote" de la table "votes", c'est un int(1) qui prend la valeur 0, 1, 2, 3 ou 4 : 0 quand le vote est inconnue, 1 quand le vote est positif, 2 quand le vote est négatif, 3 en cas d'abstention, 4 quand le député était absent.

Quant à la structure de la table "votes" la voici :

Code : Tout sélectionner

CREATE TABLE IF NOT EXISTS `votes` ( `id` int(150) NOT NULL auto_increment COMMENT 'ID du vote ; il y a un vote par loi et par député.', `id_dep` int(150) NOT NULL COMMENT 'ID du député afin de lui rattacher son vote.', `id_loi` int(150) NOT NULL COMMENT 'ID de la loi concerné par le vote dont il est question.', `vote` int(1) NOT NULL COMMENT 'Le député a t-il voté pour la loi ?', PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci COMMENT='Table contenant les votes de chaque député concernant chaque' AUTO_INCREMENT=1121 ;

par Cyrano » 26 janv. 2008, 09:26

Ok, ça va effectivement faciliter la tâche... à un détail près :

La valeur de la colonne `vote` de la table `votes` est de type INT ce qui ne correspond pas aux valeurs "Pour", "Contre" et "Absent" et il n'y a pas de valeur par défaut : au passage, un TINYINT aurait largement suffi. Mais j'ai un doute, ça voudrait dire que tu as par exemple une valeur 1 si c'est "pour", 0 si c'est "contre" et s'il n'y a pas de ligne, c'est que le député était absent, c'est bien ça ?

Dans ce cas, il va devenir compliqué de faire ça avec une unique requête. Je vais attendre que tu confirmes ou infirmes ma déduction avant d'aller plus loin... d'autant que la structure de la table `votes` ne m'apparaît pas correcte.

par Leotus » 26 janv. 2008, 00:29

Merci beaucoup de te pencher sur mon problème, j'ai fait un petit exemple avec contenu des tables et résultat souhaité ici : http://leo.dumont.free.fr/exemple.html

par Cyrano » 25 janv. 2008, 16:49

J'ai un peu de mal (et surtout assez peu de temps disponible) pour visualiser correctement. Il faudrait que tu mettes un petit jeu d'essai pour peupler les tables.

Un exemple avec 2 ou 3 députés et 4 ou 5 lois (et les correspondances dans la table des votes) avec le résultat que tu attends serait suffisant. Si on sait ce qu'on cherche, il sera plus facile de formuler la question correspondante : il suffira de la traduire en SQL par la suite ;)

par Leotus » 25 janv. 2008, 16:44

Merci beaucoup pour ta réponse, ta solution me permet d'avancer, mais je n'arrive toujours pas à ce que je veux exactement.

En utilisant ta méthode (on peut consulter le résultat ici, j'obtiens effectivement les votes des députés correspondants aux lois, mais je ne sais pas comment construire un tableau croisé avec les députés en colonnes et les lois en ligne. Pour l'instant j'arrive seulement à afficher autant de fois le nom des lois qu'il y a de votes (soit 35 car j'ai 35 députés).

Je pense que je cale au niveau de l'affichage des données, il faudrait que je fasse plusieurs boucles while(mysql_fetch_array) pour construire les différentes parties du tableau, mais comment faire alors que je n'ai qu'une seule requête ?

Je cherche à obtenir quelque chose dans comme ça, mais dans le cas présent les votes contenus dans les cellules ne correspondent pas du tout aux lois/députés et mon tableau part complètement en live sur la droite. J'arrive à ce résultat en faisant plusieurs requêtes :

Code : Tout sélectionner

$req_votes = mysql_query("SELECT intitule FROM lois"); while ($data_votes = mysql_fetch_array($req_votes)) { echo '<td>'.$data_votes['intitule'].'</td>'; } echo'</tr>'; $req_votes2 = mysql_query("SELECT nom FROM deputes"); while ($data_votes2 = mysql_fetch_array($req_votes2)) { echo'<tr> <td>'.$data_votes2['nom'].'</td>'; $req_votes3 = mysql_query("SELECT * FROM lois l, votes v, deputes d WHERE l.id = v.id_loi AND v.id_dep = d.id AND l.id IN (1, 2, 3, 4)"); while ($data_votes3 = mysql_fetch_array($req_votes3)) { echo'<td>'.$data_votes3['vote'].'</td>'; } echo'</tr>'; }

par Cyrano » 25 janv. 2008, 15:14

Ok, alors je propose une reformulation. Si on part du principe que tu as identifié les lois pour lesquelles tu souhaites avoir les informations, tu disposes d'une liste d'identifiant pour ces lois. partant de là, ce que personnellement je ferais ressemblerait à ceci :

Code : Tout sélectionner

SELECT * FROM lois l, votes v, deputes d WHERE l.id = v.id_loi AND v.id_dep = d.id AND l.id IN (1, 2, 3, 4)
Où les nombre entre les parenthèses sont les identifiants des lois mentionnés plus haut.

Si tu n'es pas familier avec les jointures, jette un petit coup d'œil dans la FAQ, j'ai laissé il y a quelques temps un micro-tuto sur le sujet (spécial débutant) ;)

par Leotus » 25 janv. 2008, 14:59

Merci pour ta réponse. Effectivement j'ai fait une erreur en tapant la requête sur le forum, mais elle n'était pas présente dans mon script.

[Note : ce message a été posté de manière anonyme avant d'être réattribué à son auteur]

par Cyrano » 25 janv. 2008, 14:38

Tu as une faute de frappe dans ta requête :

Code : Tout sélectionner

SELECT * FROM votes INNER JOIN lois ON votes.id_loi = lois.id INNER JOIN deputes ON votes.id_dep : deputes.id --------------------^
Au lieu de

Code : Tout sélectionner

SELECT * FROM votes INNER JOIN lois ON votes.id_loi = lois.id INNER JOIN deputes ON votes.id_dep = deputes.id --------------------^

[MySQL 5.0.51 (Free.fr)]Jointure sur trois tables

par Leotus » 24 janv. 2008, 21:20

Bonsoir,

Je cherche à afficher un tableau croisé qui contiendrait des infos stockées dans trois tables différentes et je cale sur la requête à formuler.
Il s'agit d'afficher les votes de députés concernant un certain nombre de lois.

J'ai un première table intitulé "deputes" dont voici la structure :

Code : Tout sélectionner

CREATE TABLE IF NOT EXISTS `deputes` ( `id` int(150) NOT NULL auto_increment COMMENT 'ID député sur lequel est greffée la clef primaire de la table.', `nom` varchar(150) collate latin1_general_ci NOT NULL COMMENT 'Nom du député.', `prenom` varchar(150) collate latin1_general_ci default NULL COMMENT 'Prénom du député.', `nbr_suff` int(240) NOT NULL COMMENT 'Nombre de suffrages totalisés par le député lors de son élection', `clivage` varchar(100) collate latin1_general_ci default NULL COMMENT 'Orientation politique du député, ce champ relève du codage.', `membre_gvt_prov` tinyint(1) NOT NULL COMMENT 'Le député était-il membre du gouvernement provisoir ?', `elu_prec` tinyint(1) NOT NULL COMMENT 'Le député avait-il été élu avant 1848 ?', `election` varchar(50) collate latin1_general_ci NOT NULL COMMENT 'Trois sessions d''élections en avril, en juin et en septembre', `date_naissance` date default NULL COMMENT 'Date de naissance.', `date_mort` date default NULL COMMENT 'Date de mort.', PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci COMMENT='Table contenant les 34 députés de la Seine cités dans la source' AUTO_INCREMENT=37 ;
J'ai aussi une seconde table, nommée "lois" qui contient toutes les infos sur les lois :

Code : Tout sélectionner

CREATE TABLE IF NOT EXISTS `lois` ( `id` int(150) NOT NULL auto_increment COMMENT 'ID loi sur lequel est greffée la clef primaire de la table.', `intitule` varchar(240) collate latin1_general_ci NOT NULL COMMENT 'Intitulé de la loi.', `voix_pr` int(255) default NULL COMMENT 'Nombre de voix pour la loi.', `voix_ct` int(255) default NULL COMMENT 'Nombre de voix contre la loi.', `date_loi` date default NULL COMMENT 'Date du vote de la loi.', PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci COMMENT='Table contenant les 35 lois citées par la source.' AUTO_INCREMENT=34 ;
J'ai enfin une dernière table "votes" qui récupère les IDs des députés et des lois enregistrés au préalable et qui stocke les différents votes :

Code : Tout sélectionner

CREATE TABLE IF NOT EXISTS `votes` ( `id` int(150) NOT NULL auto_increment COMMENT 'ID du vote ; il y a un vote par loi et par député.', `id_dep` int(150) NOT NULL COMMENT 'ID du député afin de lui rattacher son vote.', `id_loi` int(150) NOT NULL COMMENT 'ID de la loi concerné par le vote dont il est question.', `vote` int(1) NOT NULL COMMENT 'Le député a t-il voté pour la loi ?', PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci COMMENT='Table contenant les votes de chaque député concernant chaque' AUTO_INCREMENT=1121 ;
Je cherche à afficher un tableau croisé qui afficherait le vote de chaque député concernant chaque loi, avec en ligne les noms des députés et en colonne les intitulés des lois. J'ai fait un petit schéma pour être plus clair : http://leo.dumont.free.fr/schema.html
Mon problème est de trouver une requête qui demanderait au serveur d'afficher dans chaque cellule le vote du député de la ligne concernant la loi de la colonne. Par exemple pour la cellule colorée en rouge, il faudrait que se soit le vote du député 1 concernant la loi 1 et ainsi de suite pour chaque cellule.

Mais je ne sais pas comment m'y prendre. J'ai tenté de faire plusieurs requêtes différentes, mais ça ne fonctionne pas :

Code : Tout sélectionner

$req_votes = mysql_query("SELECT intitule FROM lois"); while ($data_votes = mysql_fetch_array($req_votes)) { echo '<td>'.$data_votes['intitule'].'</td>'; } echo'</tr>'; $req_votes2 = mysql_query("SELECT nom FROM deputes"); while ($data_votes2 = mysql_fetch_array($req_votes2)) { echo'<tr> <td>'.$data_votes2['nom'].'</td>'; $req_votes3 = mysql_query("SELECT vote FROM votes"); while ($data_votes3 = mysql_fetch_array($req_votes3)) { echo'<td>'.$data_votes3['vote'].'</td>'; } echo'</tr>'; }
Je me doute qu'il faut utiliser une jointure, mais je connais mal cet aspect du SQL, c'est pouquoi je me tourne vers vous.
En farfouillant un peu sur le web je suis arrivé à faire la requête suivante, mais ça ne me donne toujours pas ce que je veux :

Code : Tout sélectionner

SELECT * FROM votes INNER JOIN lois ON votes.id_loi=lois.id INNER JOIN deputes ON votes.id_dep:deputes.id
Merci d'avance.