Page 1 sur 1

Problème avec LEFT JOIN

Posté : 05 févr. 2009, 16:14
par VaN
Bonjour,

J'ai un petit problème pour faire un lien entre 2 tables, dont voici la structure :
CREATE TABLE `factures` (
`facture_id` int(11) NOT NULL auto_increment,
`facture_inscription_id` int(11) NOT NULL,
`facture_file` varchar(50) NOT NULL,
`facture_datetime` datetime NOT NULL,
PRIMARY KEY (`facture_id`),
KEY `facture_inscription_id` (`facture_inscription_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

CREATE TABLE `inscriptions` (
`inscription_id` int(11) NOT NULL auto_increment,
`inscription_user_id` int(11) NOT NULL,
`inscription_event_id` int(11) NOT NULL,
`inscription_datetime` datetime NOT NULL,
`inscription_statut` tinyint(4) NOT NULL,
`inscription_place` varchar(255) NOT NULL,
PRIMARY KEY (`inscription_id`),
KEY `inscription_user_id` (`inscription_user_id`,`inscription_event_id`),
KEY `inscription_place` (`inscription_place`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
J'ai une requête qui doit me permettre de récupérer les infos de chacune des lignes de la table INSCRIPTIONS, et accessoirement, la facture associée, si elle existe (d'où l'utilisation de LEFT JOIN) :
"SELECT inscription_id, user_login, event_name, inscription_datetime, inscription_statut, inscription_place, facture_id
FROM ".$cfg_prefixe."inscriptions, ".$cfg_prefixe."users, ".$cfg_prefixe."events
LEFT JOIN ".$cfg_prefixe."factures ON facture_inscription_id = inscription_id
WHERE inscription_user_id = user_id AND inscription_event_id = event_id AND inscription_statut = 0
ORDER BY inscription_id"
Mais cette requête me renvoie l'erreur suivante :
Champ 'inscription_id' inconnu dans on clause
Pourtant, le champs inscription_id est bien répertorié dans le début de la requête.

Qu'est ce qui cloche ?

Posté : 05 févr. 2009, 19:27
par ouckileou
Utilise le format : "table.colonne" ou "alias.colonne"
Tu seras sûr de taper au bon endroit et lorsque les noms de colonnes se ressemblent ça aide à la lecture.

Ton problème doit venir du fait que tu termines ton FROM par la table Events, la jointure se fait donc entre donc entre les tables "Factures" et "Events".

Donc à ta place, j'utiliserais systématique "INNER JOIN" ou "LEFT JOIN" pour les jointures (au lieu de "WHERE inscription_user_id = user_id AND inscription_event_id = event_id", ainsi tu éviteras tout sera bien structuré.

Code : Tout sélectionner

SELECT inscription_id, user_login, event_name, inscription_datetime, inscription_statut, inscription_place, facture_id FROM inscriptions i INNER JOIN users u ON i.inscription_user_id = u.user_id INNER JOIN events e ON i.inscription_event_id = e.event_id LEFT JOIN factures f ON f.facture_inscription_id = i.inscription_id WHERE i.inscription_statut = 0 ORDER BY i.inscription_id

Posté : 05 févr. 2009, 19:58
par VaN
Utilise le format : "table.colonne" ou "alias.colonne"
Tu seras sûr de taper au bon endroit et lorsque les noms de colonnes se ressemblent ça aide à la lecture.
Pour éviter cela justement, j'ai pris l'habitude de toujours spécifier le nom de la table (au singulier) dans le nom des champs de cette table. Normalement, je n'ai donc aucun nom de champ identique dans ma base de donnée.

Mais je vais faire comme tu m'as dit pour voir ce que me sort la requete. Je reviens poster pour dire si c'est ok ou non.

[EDIT]Je viens de relire ton post, et je viens de comprendre quelque chose. Jusqu'ici, j'utilisais LEFT JOIN "au petit bonheur la chance", sans comprendre que le LEFT signifiait la table juste à gauche (donc placée en dernier), dans l'instruction WHERE (qui a dit "RTFM !" ?). J'ai du avoir de la chance jusque là, que la table que je cherchais à joindre était toujours placée en dernière dans le WHERE. Je viens donc de placer ma table INSTRUCTIONS en fin de WHERE, et tout fonctionne comme attendu. Merci de m'avoir ouvert les yeux sur l'utilisation de LEFT JOIN : )

Problème [RESOLU]

Posté : 05 févr. 2009, 20:28
par ouckileou
Je me permets d'insister sur une utilisation de INNER JOIN, qui permettrai de ne même pas avoir à faire attention à ce genre de choses, et de séparer les jointures du filtrage (WHERE).

Mais bon après tu fais comme tu le sens.

Posté : 05 févr. 2009, 22:55
par VaN
Je préfère placer dans le WHERE les champs que je souhaite à tout prix sortir, et dans le JOIN, ceux qui sont optionnels : ) D'ailleurs les JOIN servent à ça nan ? Que la requête sorte des résultats, même si le JOIN ne retourne rien, contrairement au WHERE.

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

Posté : 06 févr. 2009, 01:02
par ouckileou
Je préfère placer dans le WHERE les champs que je souhaite à tout prix sortir, et dans le JOIN, ceux qui sont optionnels : ) D'ailleurs les JOIN servent à ça nan ? Que la requête sorte des résultats, même si le JOIN ne retourne rien, contrairement au WHERE.
Heu non...

Déjà je crois que tu confonds FROM et WHERE dans tes 2 derniers messages.

Ensuite, les champs ou colonnes que l'on souhaite sélectionner, sont dans le SELECT.
Le FROM sert à spécifier la ou les tables dans lesquelles ils se trouvent.

Le WHERE sert à filtrer les tuples (ou lignes) renvoyés grâce à des conditions.

Les JOIN servent à faire des jointures entre ces tables.

Donc il est vrai qu'on peut mettre plusieurs tables dans le FROM, et mettre les conditions de jointure dans le WHERE, mais souvent on préfère structurer les choses, séparer les jointures du filtrage :

SELECT t1.colonne1, t1.colonne2, t2.colonne1
FROM table1 t1
INNER JOIN table2 t2 ON t1.id = t2.id
WHERE t1.colonne4 > 5;

Quand tu dis optionnels, je penseq que tu veux dire que tu veux les données de la table 1 même s'il n'y a pas de jointure avec la table 2, c'est là qu'intervent LEFT OUTER JOIN.

Si tu ne veux que les lignes de la table 1 qui ont une correspondance dans la table 2, alors c'est INNER JOIN. C'est que fait une jointure placée dans le WHERE.

Effectivement, un petit tour dans le manuel te serait très utile :D

Posté : 06 févr. 2009, 01:16
par VaN
Oui oui, j'ai confondu, mes excuses.

Mais regarde cet exemple :
Disons que j'ai une table COMMANDES, et une table FACTURES.

Si je fais un
SELECT commande_id, facture_name FROM commandes, factures WHERE commande_id = facture_commande_id
et qu'une commande n'a pas encore de facture, et bien la requête ne me sortira pas cette ligne.

Par contre, si je fais un
SELECT commande_id, facture_name FROM commandes LEFT JOIN factures ON commande_id = facture_commande_id
la requête me sortira toute de même la ligne, mais la variable $result['facture_name'] sera vide.

Et dans le cas où je ne souhaite afficher que les commandes qui ont une facture, je suis obligé de passer par la première requête (ou alors de rajouter une clause WHERE dans la deuxième, où je specifie AND facture_file != '', ce qui complique donc la requête, niveau visiblité.

Posté : 06 févr. 2009, 01:35
par ouckileou
Oui ben c'est ce que j'ai dit, il y a deux types de jointures, INNER JOIN, et LEFT OUTER JOIN ( ou RIGHT OUTER JOIN mais c'est pareil)

Ta première requête équivaut à une INNER JOIN :

Code : Tout sélectionner

SELECT commande_id, facture_name FROM commandes INNER JOIN factures ON commande_id = facture_commande_id
Et là du coup, le WHERE servira uniquement au filtrage, par exemple pour ne prendre que les commandes/factures d'un client précis.

D'où une vraie meilleure lisibilité, les jointures et les conditions de filtrage sont séparées.