Page 1 sur 2
requête spéciale, je ne m'en sort pas.
Posté : 11 oct. 2011, 14:53
par Zahnzao
Salut,
Je voudrai faire un select sur une table. Je veux récupérer uniquement les id qui apparaissent au moins une fois dans une des lignes d'une autre table.
Je suppose que dois récupérer l'id grâce aux alias, puis ensuite faire un LIKE sur l'autre table.
"SELECT * FROM tags AS t, elements AS e WHERE e.tagsIDS LIKE..."
et ici je dois tester le présence de 't.id' dans 'e.tagsIDS' sachant que les ids sont entre quotes dans le champ tagdIDS.
La requête doit me revoyer les ID des tags.
Une idée ?
Re: requête spéciale, je ne m'en sort pas.
Posté : 11 oct. 2011, 15:05
par Cyrano
Je sens le modèle de conception boiteux... j'ai bien une idée mais il faudrait confirmer ça
Fais donc voir un exemple de quelques lignes de données de chaque tables qu'on ait une idée de ce à quoi ça peut bien ressembler. Et par la même occasion, indique par rapport à ces quelques lignes quel résultat tu attendrais de la requête que tu veux réaliser.
Re: requête spéciale, je ne m'en sort pas.
Posté : 11 oct. 2011, 15:13
par Zahnzao
J'en parlais justement ici
php-debutant/optimisation-foreach-requete-t260735.html
J'ai utilisé la fonction Serialize() pour insérer mes tags dans un seul champ, je sens que tu vas me dire de refaire mes tables !
Donc voila j'édite pour mettre quelques données.
elements -> id(int),Name(varchar),tagsIDS(varchar) + d'autres champs (date,categories etc..) mais inutiles ici
tags -> id(int),Name(varchar)
donc comme dit plus haut mon champ elements.tagsIDS est un serialize(). donc sous cette forme "a:2:{i:0;s:2:"20";i:1;s:2:"18";}"
Re: requête spéciale, je ne m'en sort pas.
Posté : 11 oct. 2011, 15:26
par Cyrano
...je sens que tu vas me dire de refaire mes tables !
Ben si tu n'es qu'en début de projet, ça va ressembler à ça effectivement.
En fait si je résume bien, un tag peut concerner plusieurs éléments, et un élément peut comporter plusieurs tags : on est typiquement dans une relation 0:n/0:n et il conviendrait donc de traduire ça avec une table relationnelle entre tag et element, par exemple une table tag_has_elements donc la clé primaire serait alors une clé composite , formée par les deux clés étrangères tag_id et elt_id.
De cette manière, un tag ne pourra être présent qu'une seule fois dans un élément donné.
Partant de là, la recherche des tags présents au moins une fois dans un élément se fera avec une simple jointure interne : si un tag n'est présent dans aucun élément, la ligne ne sera tout simplement pas retournée.
Est-ce que tu visualises correctement ce schéma ou pas trop ?
Sinon, je présume que ta colonne de tags dans la table element comporte la liste des tags séparés par des virgules ou quelque chose d'approchant ?
Re: requête spéciale, je ne m'en sort pas.
Posté : 11 oct. 2011, 15:58
par Zahnzao
Je venais d'éditer, tu as les infos sur le type de données dans mon post précédent. A noter que ce sont les IDs des tags et non les tags en texte.
Pour clarifier le tout, j'ai une page admin, qui n'est qu'une série de 3 formulaires. Le premier est juste un ajout de catégorie à la bdd. Le second, idem, ajout de tags. Ce ne sont que des champs textes, rien de tordus. Donc je crée mes tags à l'avance.
Le troisième formulaire ajoute des éléments, j'ai donc une liste déroulante à choix unique pour la catégorie, et des checkbox pour les tags.
Lors du traitement, je serialize() mon $_GET['tags'] et je l'inclus tel quel dans la BDD, c'est donc un varchar.
Concernant le reste de ton post, si je comprend bien, ce serai une table reprenant chaque ajout de tag avec en référence l'id de l'élément et l'id du tag. Donc si un élément contient 3 tags, il y'aurai 3 enregistrements dans cette table. C'est ca ?
J'aurai besoin d'éclaircissement sur ceci:
la clé primaire serait alors une clé composite , formée par les deux clés étrangères tag_id et elt_id
Une clé composite ?
Merci à toi.
Re: requête spéciale, je ne m'en sort pas.
Posté : 11 oct. 2011, 16:41
par Cyrano
Très sommairement :
+---------------------+ +-----------------+ +-------------+----+
| element | | element_has_tag | | tag | |
+----------------+----+ +------------+----+ +-------------+----+
| elt_id | PK |-------| elt_id | PK | ,---| tag_id | PK |
| elt_libelle | | | tag_id | PK |---` | tag_libelle | |
+----------------+----+ +------------+----+ +-------------+----+
Voilà ;a quoi ressembleraient tes tables : la table element_has_tag a une clé primaire composée des deux colonnes elt_id et tag_id qui sont en même temps des clés étrangères correspondant aux clés primaires des deux autres tables.
Donc lorsqu'il doit y avoir un lien entre un élément et un tag, ça va correspondre à une ligne dans la table relationnelle. Et le fait que la clé soit composite fait que chaque paire est unique, donc tu pourras retrouver plusieurs fois le même elt_id dans la table, ou plusieurs fois le même tag_id, mais jamais plus d'une seule fois une faire elt_id + tag_id. Et si d'aventure tu voulais pouvoir justement avoir plusieurs fois cette paire, alors il faudrait introduire un troisième identifiant dans la table relationnelle et l'ajouter à la clé primaire.
Est-ce qu'à ce stade la logique commence à t'apparaître un peu ?
Re: requête spéciale, je ne m'en sort pas.
Posté : 11 oct. 2011, 17:25
par Zahnzao
Ok j'ai compris !
Je me suis créé une nouvelle base, avec exactement ces 3 tables pour les tests. J'ai un peu cherché de mon coté pour créer cette liaison sous phpmyadmin.
Je rentre donc dans ma table element_has_tag et je clique sur "gerer des relations".
pour elt_id je met en 'relation interne' -> liaison.element.elt_id
pour tag_id je met -> liaison.tag.tag_id
J'ai aussi un choix 'Contrainte de clé étrangère' je dois y mettre quelque chose ? et colonne descriptive ?
Lorsque je selectionne quelque chose dans 'contrainte clé étrangère' j'ai les options 'on delete' et 'on update' qui s'affichent
Re: requête spéciale, je ne m'en sort pas.
Posté : 11 oct. 2011, 17:33
par Cyrano
Si tu utilises le moteur InnoDB, tu peux effectivement ajouter les contraintes de clé étrangères. Dans ce cas, si tu mets ON DELETE RESTRICT et ON UPDATE RESTRICT, il se passera que tu ne pourras pas supprimer un tag ou un élément s'il existe une relation : il faudra d'abord modifier ou supprimer la relation avant de toucher à l'élément ou au tag, enfin ça concerne surtout la suppression. Ça évite de laisser dans ta base des données orphelines.
Si tu utilises ON DELETE CASCADE, en supprimant un tag ou un élément, ça va automatiquement supprimer la relation qui va avec au cas o`<u il y en aurait une, mais attention, c'est un truc dangereux si on est pas très attentif, et en cas d'erreur de manipulation, on peut perdre des données qui seront irrécupérables.
Mais si tu utilise MyISAM, laisse tomber, ce ne sera pas pris en compte, tu devras donc assurer l'intégrité référentielle par programmation.
Re: requête spéciale, je ne m'en sort pas.
Posté : 11 oct. 2011, 17:47
par Zahnzao
Ok, je suis en InnoDB. Je préfère restrict dans ce cas. Voici la requête qui à été exécutée.
ALTER TABLE `element_has_tag` ADD FOREIGN KEY ( `elt_id` ) REFERENCES `liaison`.`element` (
`elt_id`
) ON DELETE RESTRICT ON UPDATE RESTRICT ;
ALTER TABLE `element_has_tag` ADD FOREIGN KEY ( `tag_id` ) REFERENCES `liaison`.`tag` (
`tag_id`
) ON DELETE RESTRICT ON UPDATE RESTRICT ;
Je pense maintenant pouvoir faire un brin de recherche pour comprendre comment appliquer tout cela a mon script. J'ai déja repéré quelques tutos sur le sujet.
Je te remercie.
Re: requête spéciale, je ne m'en sort pas.
Posté : 11 oct. 2011, 23:30
par Zahnzao
Bon j'ai fais quelques tests, j'ai rempli mes tables manuellement, je comprend le principe mais la construction de la requête me pose problème.
j'ai créé un petit code de test reprenant le schéma exact de cyrano ci dessus.
$bd_nom_serveur='localhost';
$bd_login='root';
$bd_mot_de_passe='';
$bd_nom_bd='liaison';
mysql_connect($bd_nom_serveur, $bd_login, $bd_mot_de_passe);
mysql_select_db($bd_nom_bd);
mysql_query("set names 'utf8'");
$idElement = 1; //simule un id
$sql="SELECT * FROM element WHERE elt_id=$idElement
INNER JOIN tag
ON element.elt_id = tag.elt_id";
$query = mysql_query($sql) or exit('Erreur SQL : '.mysql_error().' Ligne : '. __LINE__ .'.');
while($data = mysql_fetch_assoc($query)){
echo $data['id']."<br />";
}
j'ai beau retourner la requête dans tout les sens, il m'affiche une erreur de syntaxe sur la requête.
Re: requête spéciale, je ne m'en sort pas.
Posté : 11 oct. 2011, 23:55
par Cyrano
Insère un var_dump() de la requête entre sa définition et son exécution et observe ce qui s'affiche : si ça a l'air normal, copie la requête et teste la directement dans phpMyAdmin et vois quel résultat ça retourne.
Re: requête spéciale, je ne m'en sort pas.
Posté : 12 oct. 2011, 11:20
par Zahnzao
Aaaah ca avance dans la compréhension, j'ai enfin réussi une requête valable.
// Uniquement les tags utilisés
$sql="SELECT tag.tag_id,tag.tag_libelle FROM tag
INNER JOIN element_has_tag
ON element_has_tag.tag_id = tag.tag_id
GROUP BY tag.tag_id";
var_dump($sql);
$query = mysql_query($sql) or exit('Erreur SQL : '.mysql_error().' Ligne : '. __LINE__ .'.');
while($data = mysql_fetch_assoc($query)){;
var_dump($data);
}
Ca me sort bien mes 3 tags et non le 4ème qui n'est pas utilisé.
Code : Tout sélectionner
array
'tag_id' => string '1' (length=1)
'tag_libelle' => string 'TestTag1' (length=8)
array
'tag_id' => string '2' (length=1)
'tag_libelle' => string 'TestTag2' (length=8)
array
'tag_id' => string '3' (length=1)
'tag_libelle' => string 'TestTag3' (length=8)
C'était la plus simple.
Maintenant la suivante. Ici j'ai un élément défini, et je dois récupérer l'id et le libelle de l'élément ainsi que l'id et le libellé de chaque tag lié a cet élément, donc je dois jouer sur 3 tables
// Uniquement les tags liés à un élément
$idElement = 2;
$sql="SELECT tag.tag_id,tag.tag_libelle,element.elt_id,element.elt_libelle FROM tag,element
INNER JOIN element_has_tag
ON element_has_tag.elt_id = $idElement ";
var_dump($sql);
$query = mysql_query($sql) or exit('Erreur SQL : '.mysql_error().' Ligne : '. __LINE__ .'.');
while($data = mysql_fetch_assoc($query)){;
var_dump($data);
}
ici il me sort carrément 12 résultats alors qu'il devrait y'en avoir que 3.
Dans le select, j'ai bien ce que je dois récupéré, donc je suppose que mon problème se situe dans la jointure.
Re: requête spéciale, je ne m'en sort pas.
Posté : 12 oct. 2011, 12:02
par Cyrano
Tout juste : regarde dans la FAQ, il y a un tuto sur les bases fondamentales des jointures.
Re: requête spéciale, je ne m'en sort pas.
Posté : 12 oct. 2011, 12:21
par Zahnzao
Tu parles bien de celui ci ?
faq-tutoriels/les-jointures-niveau-debutant-t21507.html
Je l'ai lu et bien compris, le problème ici, c'est que j'ai un ID élément défini à la base et que je dois récupérer les informations de 2 tables (element et tag) liées par une troisième (element_has_tag).
C'est fou parce que j'arrive à te l'expliquer mais je n'arrive pas à le réaliser...
Re: requête spéciale, je ne m'en sort pas.
Posté : 12 oct. 2011, 12:30
par Cyrano
ta jointure ne comporte qu'une clause de tri mais pas la clause de jointure elle-même entre les deux tables, donc tu obtiens un produit cartésien... en d'autres termes, il en manque un bout.