PHP Mysql jointure [Résolu]

Signaler
Messages postés
52
Date d'inscription
vendredi 13 décembre 2019
Statut
Membre
Dernière intervention
28 juillet 2020
-
Messages postés
12140
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
13 septembre 2020
-
Bonjour a tous et meilleur vœux,

J'ai 3 tables que je lie entre elles pour mon activité, elles sont reliés par un identifiant.

la table invoice
la table statut
la comments

A la création d'une facture, (la table invoice) me fourni le numéro de facture, ce numéro de facture je le récupère, je le renomme ensuite je le répercute sur les autres tables.

Les tables sont reliés par le même identifiant de facture, sauf que le nom de leur champs respectifs est différents. Je les relie en faisant des jointures.

Pour l'instant, la requêtes est comme ci-dessous :

	<?php
	      
             $db = getDB();
		try		//Connection a la bdd
		{
		}
		catch (Exception $e)
		{
			die('Erreur : ' . $e->getMessage());
		}
		$reponse = $db->query('SELECT * FROM invoice  
		
		LEFT JOIN comments ON  (invoice.id_invoice=comments.comments_id) 		
		LEFT JOIN statut ON  (invoice.id_invoice=statut.statut_id)   

WHERE  type=1  AND (statut_type=1 OR statut_type=2)      group by id_invoice DESC  ');

echo '<center>
		<div class="table-responsive">
		<table class=" table table-striped table-bordered ">
		';
		echo '<tr>';
		echo '<th align="center" >Invoice</th>';
		echo '<th align="center">Statut</th>';
		echo '<th align="center">Client</th>';		
		echo '<th align="center">Titre</th>';	
		echo '<th align="center">Date</th>';	
		echo '</tr>';
		
            while($donnees = $reponse->fetch())	// Renvoit les valeurs de la bdd
                        {       	
            	
            	
            	
            	if($donnees['statut_type']==1) {
            		$statut = '<div align="center"> <button type="button" class="btn btn-block btn-warning btn-xs flat">ouverte</button></div>';

            	}
            	
            	if($donnees['statut_type']==2) {
            		$statut = '<div align="center"> <button type="button" class="btn btn-block btn-primary btn-xs flat">envoyé</button></div>';
            	} 

            	if($donnees['statut_type']==3) {
            		$statut = '<div align="center"> <button type="button" class="btn btn-block btn-success btn-xs flat">payé</button></div>';
            	} 
            	
            	if($donnees['statut_type']==4) {
            		$statut = '<div align="center"> <button type="button" class="btn btn-block btn-danger btn-xs flat">annulé</button></div>';

            	}       	

            	
            	echo '<tr>';
            	echo '<td align="center" width="2%" ><a href="'.URLSITE.'form/facture.php?id_invoice='. $donnees['id_invoice'] .' "><button type="button" class="btn btn-block btn-info btn-xs    
            	      flat">' . $donnees['id_invoice'] . '</td></button></a>';
            	echo '<td  width="5%" >' . $statut . '</td>';
            	echo '<td align="left" >'.$donnees['showin'].' </td>';
            	echo '<td align="left" >' . $donnees['titre'] . '</td>';
            	echo '<td align="center"  width="5%">' . $donnees['date_ouverture'] . '</td>';       	
            	echo '</tr>';
            }
            echo '</table></div></center>';
            $pdo = null;
            ?>



id_invoice, statut_id, comments_id sont reliés par le même numéro. ça fonctionne bien, j'aurais aimer que tout les champs de cette correspondance s'apelle "id_invoice", quand j'ai essayé j'ai eu un message d'erreur :

in where clause is ambiguous


Comment avoir les mêmes champs sur les tables et les relier?

6 réponses

Messages postés
3617
Date d'inscription
jeudi 16 juin 2005
Statut
Membre
Dernière intervention
10 septembre 2020
967
Bonjour,

J'ai l'impression que les autres réponses sont parties un peu loin...
Dans ta requête originale, tu avais trois champs de jointure dont le nom était distinct, et une requête qui se terminait par « group by id_invoice ».
Comme un seul de ces champs portait le nom id_invoice, la base savait duquel il s'agissait.

Par contre, une fois que tu as renommé tes colonnes pour qu'elles s'appellent toutes id_invoice, la base ne sait plus de laquelle tu parles dans le group_by. Même si leur valeur est égale par ailleurs, puisqu'il s'agit de la clause de jointure.

La solution est toute bête : faire exactement comme dans la jointure, et préfixer le nom de la colonne par le nom de la table dont elle est issue, pour indiquer à la base de quelle colonne id_invoice tu parles parmi les trois.
Comme tu as mis des LEFT JOIN, il faut utiliser la table source, invoice. Donc, « group by invoice.id_invoice ».

Xavier
Messages postés
12140
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
13 septembre 2020
683
bonjour, se pourrait-il qu'un des champs type et/ou statut_type soit présent dans plusieurs tables?
j'ai l'impression qu'il manque une partie du message d'erreur.
Messages postés
52
Date d'inscription
vendredi 13 décembre 2019
Statut
Membre
Dernière intervention
28 juillet 2020

Non les champs type et/ou statut_type sont présent que dans la table statut.

Exact, voici ci-dessous les messages d'erreur en entier

( ! ) Fatal error: Uncaught PDOException: SQLSTATE[23000]: Integrity constraint violation: 1052 Champ: 'id_invoice' dans group statement est ambigu in C:\wamp\www\gestion\app\array\invoice.php on line 28



( ! ) PDOException: SQLSTATE[23000]: Integrity constraint violation: 1052 Champ: 'id_invoice' dans group statement est ambigu in C:\wamp\www\gestion\app\array\invoice.php on line 28


J'ai l'impression que le problème vient de la ligne ci-dessous: Il ne dois pas savoir lequel utiliser

group by id_invoice DESC


Quand je l'ai retiré ça m'affiche un tableau avec plusieurs fois les mêmes lignes. Bon je viens de remplacer

group by id_invoice DESC


par

group by invoice.id_invoice DESC


est c'est ok

Entre temps, j'avais fait des recherches et j'étais tomber sur des alias est-ce que c'est d'actualités ou pas pour mon cas??
Après je cloture.

Merci
Messages postés
52
Date d'inscription
vendredi 13 décembre 2019
Statut
Membre
Dernière intervention
28 juillet 2020
>
Messages postés
12140
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
13 septembre 2020

J'avais déjà eu ce problème sur un test similaire ou j'avais eu ce message de "in where clause is ambiguous" . Je n'avais pas reproduit le problème pour avoir le message avant de poster (je m'étais rapeller qu'il y avait ambigous et clause dans le message d'erreur, j'ai donc fait une recherche sur internet sur ces 2 mots la, d’où l'erreur.

Bref pour être plus précis et factuel

Table Invoice => id_invoice (id_customers) (id_user session)
Table Comments => comments_id (id_user1 session)
Table Statut => => statut_id (id_user2 session)

Table price => price_id ( id_price) (id_user3 session)
Table Items => Items_id (id_price1) (id_user4 session)
Table promo => promo_id ( id_price2 ) (id_user5 session)

Table payements => payement_id (id_user6 session)


(id_invoice, comments_id, statut_id, payement_id, items_id, price_id) sont liés par l'id_invoice
(id_price, id_price1, id_price2) sont liés par l'id_price

En plus toutes les tables ont un champs pour l'id_user qui les lies aussi.

Ce qui fait que pour les tables (price, items, promo), il y a 3 champs qui les lient entre elles.

On est 3 a utiliser le programme on a donc chacun nos id de session, ce qui fait que dans toutes mes tables j'ai aussi un champs correspondant a l'id de session.

Jusque la tout fonctionne correctement, chacun peut voir juste son travail sur les pages faites pour, et on peut voir l'ensemble du travail de tout le monde aussi sur d'autres.

Dans mes requêtes je peux afficher, une facture avec tout ses items et je peux aussi afficher tout les paiements concernant la facture, je peux savoir toutes les factures et tout les paiements d'un clients. En gros je peux faire toutes les requêtes que je veux. Dans les test que j'ai effectué avec mes collègues, Il n'y a pas de problèmes de données de l'un qui s'affiche chez l'autres etc...

Mon soucis ce n'est pas l'amélioration au niveau performance ou autres dans ce cas précis, j'ai plusieurs champs correspondant a id_invoice qui ont plusieurs noms différents selon les tables, idem pour l'id_price et l'id_user.

Le but recherché c'est d'avoir le même nom de champs sur toutes les tables qui sont liés entres elles par le même champ, ça me parait plus logique. (Ce n'est pas une obligation non plus).

J'ai eu un message plus bas concernant les clés étrangères, j'ai pas bien compris.

Une clé étrangère, dans une base de données relationnelle, est une contrainte qui garantit l'intégrité référentielle entre deux tables.

Définition trouvé sur Wikipédia:
Une clé étrangère identifie une colonne ou un ensemble de colonnes d'une table comme référençant une colonne ou un ensemble de colonnes d'une autre table (la table référencée). Les colonnes de la table référencée doivent faire partie d'une contrainte de clé primaire ou d'une contrainte d'unicité. La contrainte de clé étrangère garantit que les valeurs de chaque ligne de la table référençant existent dans la table référencée : ainsi une ligne de la table référençant ne peut pas contenir un ensemble de valeurs qui n'existe pas dans la table référencée.

Une contrainte de clé étrangère permet ainsi d'établir des liens entre plusieurs tables : il s'agit d'un des principes fondamentaux des bases de données relationnelles.


Donc si j'ai bien compris ma clé primaire c'est :

table invoice
id_invoice = clé primaire
id_user = clé primaire

et mes clés étrangères sont:

table comments
comments_id =(id_invoice) = clé étrangère
id_user1 = (id_user) = clé étrangère

table statut
statut_id =(id_invoice) = clé étrangère
id_user2 = (id_user) = clé étrangère

C'est bien ça non?
Messages postés
12140
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
13 septembre 2020
683 >
Messages postés
52
Date d'inscription
vendredi 13 décembre 2019
Statut
Membre
Dernière intervention
28 juillet 2020

je pense que tu as compris le concept de clé étrangère.
si tu veux donner des faits:
- donne, pour chaque table, son nom et la liste des noms des champs. utilise des mots plutôt que des signes tels que ==> et ()
- précise quelle est la clé primaire de chaque table
- précise, pour chaque table, quelles sont les clés étrangères, et à quelle clé primaire de quelle table chacune correspond
- quand tu as un problème avec une requête que tu viens de modifier, donne la syntaxe de la requête modifiée, pas uniquement la syntaxe de la requête qui fonctionnait.
- n'écris pas qu'un ensemble de champs sont liés par l'un deux, cela ne veut rien dire
- quand tu utilises un nom de champ hors du contexte de sa table, précise aussi le nom de sa table
.
il est préférable que les noms des champs correspondent à leur signification. utiliser statut_id pour un id de facture est contre-productif.
Messages postés
52
Date d'inscription
vendredi 13 décembre 2019
Statut
Membre
Dernière intervention
28 juillet 2020
>
Messages postés
12140
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
13 septembre 2020

Voici le nom des tables en gras avec leurs champs respectifs.

Utilisateurs(id_user, nom, prenom, password)
Invoice (id_invoice, id_customer, id_user)
Statut (id_statut, statut_id, uids, type, statut_type, begining_date, ending_date)
Comments (id_comments, comments_id, uidc, title, comments)
Payements (id_payements, payements_id, uidpa, amount, method, methodid, payement_date)
Price (id_price, price_id, uidp, quantity, price_ht, tax, tax_amount)
Items (id_item, item_id, uidi, id_price1, item, designation)
Promo (id_promo, promo_id, uidpr, nature_promo, percent, amount_promo)
Acompte (id_acompte, acompte_id, uidac, amount_acompte, date_acompte)

Clé primaire table Invoice : (id_invoice, id_user)
Clé primaire table Price: (id_price )

Clé étrangère table Statut: (statut_id, uids</bold>)
Clé étrangère table Comments : (comments_id, uidc)
Clé étrangère table Payements: (payements_id, uidpa)
Clé étrangère table Price: (price_id, uidp)
Clé étrangère table Items: (items_id, id_price1, uidi)
Clé étrangère table Promo: (promo_id, id_price2, uidpr)
Clé étrangère table Acompte: (acompte_id, uidac)


POUR CE QUI EST DES CLES PRIMAIRES ET ETRANGERES, JE N'AI ENCORE RIEN FAIT COTE BASE DE DONNEES, AU NIVEAU DE PHPMYADMIN JE PRECISE

Voici un exemple de requête ci-dessous:

$reponse = $db->query('SELECT * FROM invoice  

   LEFT JOIN comments ON  (invoice.id_invoice=comments.comments_id AND invoice.uid=comments.uidc)   
   LEFT JOIN statut ON  (invoice.id_invoice=statut.statut_id)   
   LEFT JOIN customers ON (invoice.customers_id=customers.id_customers) 
   LEFT JOIN utilisateurs ON (invoice.uid=utilisateurs.id_user) 
   LEFT JOIN price ON (invoice.id_invoice=price.price_id AND invoice.uid=price.uidp) 


   WHERE  id_user =' .$_SESSION['id_user'].'  AND type=1  AND (statut_type=1 OR statut_type=2)      
                       group by    id_invoice DESC  ');


Tout les champs qui termine par( xxxxx_id) sont des clés étrangères liés a la clé primaire (id_invoice)
Tout les champs qui commence par( uidxx) sont des clés étrangères liés a la clé primaire (id_user)
table Item (champs id_price1)
et table (promo champs id_price2) sont liés a la table Price (id_price)
Messages postés
52
Date d'inscription
vendredi 13 décembre 2019
Statut
Membre
Dernière intervention
28 juillet 2020
>
Messages postés
52
Date d'inscription
vendredi 13 décembre 2019
Statut
Membre
Dernière intervention
28 juillet 2020

Le but est de mettre des id_invoice et id_user partout
Messages postés
12140
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
13 septembre 2020
683 >
Messages postés
52
Date d'inscription
vendredi 13 décembre 2019
Statut
Membre
Dernière intervention
28 juillet 2020

prenant la table items comme exemple, il serait correct d'écrire qu'elle a deux clés étrangères:
la clé étrangère (items_id) référençant la clé (id_price) de la table price
et la clé étrangère (id_price1, uidi) référençant la clé (id_invoice, id_user) de la table invoice
Bonjour,
Une première règle absolue pour les bases de données:

Chaque table contient un ou plusieurs(dans le cas de jointures) champs qui sont identifiants. Un champ identifiant signifie qu'il est unique.

Sinon pour éviter les erreurs et confusion on peut nommer les clés étrangères (un champ identifiant venant d'une autre table) avec un autre nom ou en rajoutant FK(foreign key) devant, voire indiquer aussi d'où vient la clé étrangére permettant l'identification à plusieurs champ(et donc une jointure de table).

Cela évite les confusions. Dans cet ordre d'idée je vous invite à ne pas nommer un champ exactement pareil ce qui évitera les méprises.

Vérifiez aussi que la première régle que j'indique et respectée et quels sont les champs identifiants(ils sont d'ailleurs indiqué dans la structure de la table lors de la création).


Côté SQL une table étant une liste de champs (lignes) et de colonnes pour renommer ceux ci ils faut donc tout détruire et créer de nouvelles tables en transférant le contenu(migration) vers elles.
Ce genre d'opérations étant fastidieuse(bien qu'assez simple à faire côté code comme tout ce qui touche au langage SQL) et aussi risque d'erreurs et de pertes de données(sans compter les modifications à faire au programme hors SQL pour utiliser la mise à jour des noms de champs concernés) je vous conseille fortement de ne pas faire ce genre de choses, du moins sans une réelle nécessité, hors vous n'indiquez pas quel est le but ni pourquoi changer un nom...et votre changement de nom n''étant en aucun cas une amélioration du programme(plutôt l'inverse comme indiqué ci dessus) je ne vois pas l'intérêt de changer un nom de champ...

Rappel pour les jointures de table :

https://sql.sh/cours/jointures

Pour le reste tout dépends de votre analyse pour les tables, il serais plus judicieux d'indiquer leur structure (toutes et pour toutes, le MCD/MLD si ces termes vous parlent) si vous voulez une réponse sur ce sujet.

Important/!\

Les jointures doivent se faire à la création des tables et selon la logique de conception afin d'éviter des erreurs vers lesquelles vous semblez allez: Un champ ne doit pas être présent dans plusieurs tables(sauf si jointure pour pouvoir identifier chaque donnée auquel cas il deviens une clé étangére -on reporte l'identifiant d'une table dans une autre et l'identifiant 'clé primaire' + la ou les clés étrangères garantissent l'intégrité des tables ) et un ou plusieurs champs (les identifiants naturels ou non) permettent que chaque ligne reste unique, donc qu'on puisse retrouver les données quand on en a besoin.

Voilà ce que vous proposez c'est un peu pour améliorer votre vélo de le démonter en roulant pour le remonter mais plus mal toujours en espérant rouler mieux...non ça ne marchera pas.

Indiquez quand même pourquoi vous voulez renommer les champs et qu'est ce que cela doit changer à votre programme... parce que là je ne comprends absolument pas votre but ni en quoi rennomer des champs peut changer quoi que ce soit dans votre programme...
Explication sommaires : https://www.commentcamarche.net/contents/1063-sql-jointures

plus détaillé ici: https://www.fbotutos.com/jointure-sql-creer-une-requete-de-jointure.html

Remarquez que les jointures sont faites AU MOMENT DE LA CREATION DES TABLES, en fait il faut simplement répercuter le ou les champs identifiants des tables à lier dans la table à lier, qui deviennent des clés étrangères:

identifiant = valeur unique d'une table permettant de repérer chaque ligne de données. Certaines tables nécessitent parfois plusieurs identifiants. Par exemple une table "personnes" contenant nom, prénom peut avoir comme identifiant nom + prénom. En effet si 2 personnes enregistrées dans la table ont le même prénom ou le même nom de famille on ne peut les différencier = table bonne à jeter, plus aucune donné n'est accessible.(ps: je ne traite pas le cas de 2 personnes différentes ayant même nom et prénom car ce n'est pas le sujet, c'est évidement à prendre en compte de la manière appropriée mais pas utile dans la démonstration que je fais).

Lors de la jointure avec la table personne il faut bien évidemment 'reporter' tous les champs identifiants vers la table à joindre.

clé étrangère = identifiant d'une autre table permettant une liaison de celles ci entre elles
Une clé étrangère est l'identifiant (ou les identifiants si plusieurs) d'une autre table. Les identifiants d'une table jointes à une autre sont donc ses propres identifiants(ex: un numéro de facture) + tous les identifiants de l'autre table jointe.

Il va de soi qu'il n'existe pas pour la même personne différents identifiants(soit Nom + prénom) mais que cette donnée est simplement utilisée (répercutée) lors de la création d'un nouveau champ dans l'une ou l'autre des tables.

Parfois il est plus simple de regrouper certaines tables, tout dépends des besoins du système d'informations évidemment.
Une question simple pour savoir cela :
Si pour les deux tables il peut exister plusieurs valeurs pour un identifiant complet(dans mon exemple simple nom+prénom) alors ce sont 2 tables différentes.
Sinon on peut regrouper en une table.

Par exemple en plus de la table personne ajoutant une table facture qui contient les montants, dates et numéro de facture(identifiant naturel) et les clés étrangères nom+ prénom pour lier à la table personne.
Le cas 1 de plusieurs tables correspond au fait que pour un même identifiant de la table personne il peut exister plusieurs factures(donc plusieurs lignes qui seront différenciés par les 3 champs: n° de facture+nom+prénom).

Au contraire si pour chaque personne il ne peut exister qu'une seule facture alors on a tout intérêt à regrouper les tables, l'identifiant sera uniquement nom+ prénom, le numéro de facture n'est plus nécessaire puisque chaque personne n'a qu'une et une seule facture il suffit de trouver le nom + prénom pour avoir la ligne avec la facture associée.
Et plus d'infos sur la création de tables(regardez attentivement les propriétés indiquées pour les champs dans la page 2, 3 et 4):

https://www.1keydata.com/fr/sql/sql-create-table.php
Messages postés
52
Date d'inscription
vendredi 13 décembre 2019
Statut
Membre
Dernière intervention
28 juillet 2020

Merci