requête spéciale, je ne m'en sort pas.

Eléphant du PHP | 190 Messages

11 oct. 2011, 14:53

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 ?

Mammouth du PHP | 19672 Messages

11 oct. 2011, 15:05

Je sens le modèle de conception boiteux... j'ai bien une idée mais il faudrait confirmer ça :-k

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.
Codez en pensant que celui qui maintiendra votre code est un psychopathe qui connait votre adresse :axe:

Eléphant du PHP | 190 Messages

11 oct. 2011, 15:13

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";}"
Modifié en dernier par Zahnzao le 11 oct. 2011, 15:59, modifié 2 fois.

Mammouth du PHP | 19672 Messages

11 oct. 2011, 15:26

...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 ?
Codez en pensant que celui qui maintiendra votre code est un psychopathe qui connait votre adresse :axe:

Eléphant du PHP | 190 Messages

11 oct. 2011, 15:58

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.

Mammouth du PHP | 19672 Messages

11 oct. 2011, 16:41

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 ?
Codez en pensant que celui qui maintiendra votre code est un psychopathe qui connait votre adresse :axe:

Eléphant du PHP | 190 Messages

11 oct. 2011, 17:25

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

Mammouth du PHP | 19672 Messages

11 oct. 2011, 17:33

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.
Codez en pensant que celui qui maintiendra votre code est un psychopathe qui connait votre adresse :axe:

Eléphant du PHP | 190 Messages

11 oct. 2011, 17:47

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.

Eléphant du PHP | 190 Messages

11 oct. 2011, 23:30

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.

Mammouth du PHP | 19672 Messages

11 oct. 2011, 23:55

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.
Codez en pensant que celui qui maintiendra votre code est un psychopathe qui connait votre adresse :axe:

Eléphant du PHP | 190 Messages

12 oct. 2011, 11:20

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.

Mammouth du PHP | 19672 Messages

12 oct. 2011, 12:02

Tout juste : regarde dans la FAQ, il y a un tuto sur les bases fondamentales des jointures.
Codez en pensant que celui qui maintiendra votre code est un psychopathe qui connait votre adresse :axe:

Eléphant du PHP | 190 Messages

12 oct. 2011, 12:21

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

Mammouth du PHP | 19672 Messages

12 oct. 2011, 12:30

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.
Codez en pensant que celui qui maintiendra votre code est un psychopathe qui connait votre adresse :axe: