Update et Group By

Eléphant du PHP | 91 Messages

24 nov. 2005, 18:26

Bonjour,

J'ai une table FACTURE (id_facture, montant_total, nb_article) et une table DETAIL_FACTURE(id_facture, article , nb_article , prix_unit, prix_total)

Chaque FACTURE est composée de UNE à N ligne dans DETAIL_FACTURE.

(oui c'est n'importe quoi comme modélisation, mais c'est juste pour ma question)

Est il possible de créer 1 ordre SQL qui mette à jour montant_total, nb_article de toutes les FACTURE ? ( montant_total = sum(prix_total) par facture et nb_article = sum(nb_total) )

PS : On ne peut pas utiliser Group By dans Update. cf. doc http://www.nexen.net/docs/mysql/annotee ... ien=update

PS 2 : Je ne veux pas utiliser 2 sous select pour ne pas parcourir 2 fois la table DETAIL_FACTURE.

Merci[/url]

Eléphant du PHP | 219 Messages

25 nov. 2005, 14:20

Salut,

oui, c'est vrai que les champs calculés dans les tables c'est pas top, mais bon tu l'as déjà dit ;)

Sinon je vois cela :

Code : Tout sélectionner

$sql = "UPDATE FACTURE SET montant_total = (SELECT sum(prix_total) FROM DETAIL_FACTURE WHERE id_facture = " . $id . ") WHERE id_facture = " . $id
le tout dans une boucle sur les id_facture.

Je ne vois pas d'ordre pour toutes les factures...

le 45,2kbps de la loose
Invité n'ayant pas de compte PHPfrance

25 nov. 2005, 16:37

Si tu utilises MySQL 5 tu peux aussi utiliser une table dérivée:

Code : Tout sélectionner

UPDATE FACTURE f, ( SELECT id_facture, SUM(prix_total) AS montant_total, SUM(nb_article) AS nb_article FROM DETAIL_FACTURE GROUP BY id_facture ) AS x SET f.montant_total = x.montant_total, f.nb_article = x.nb_article
Selon la taille de ta base de données c'est probablement ce qui offrira les meilleures performances. Sinon, si la base est vraiment grande tu peux "partitionner" l'UPDATE comme ça

Code : Tout sélectionner

UPDATE ( SELECT id_facture, SUM(prix_total) AS montant_total, SUM(nb_article) AS nb_article FROM DETAIL_FACTURE WHERE id_facture BETWEEN 1 AND 1000 GROUP BY id_facture ) AS x, FACTURE f SET f.montant_total = x.montant_total, f.nb_article = x.nb_article WHERE f.id_facture = x.id_facture
Dans l'exemple j'ai explicitement changé l'ordre des tables mais de toutes façons l'optimiseur de MySQL devrait le faire de façon transparente.

Dernière(s) chose(s), tout ceci n'est valide que s'il n'existe pas de factures "orphelines" (factures sans article correspondant dans DETAIL_FACTURE), que tout est indexé correctement (à priori, FACTURE.id_facture est PRIMARY et (id_facture, article) est UNIQUE PRIMARY chez DETAIL_FACTURE), et surtout que tu n'utilise cette requête dans des cas exceptionnels: en théorie le montant d'une facture ne change jamais et n'a donc pas besoin d'être mis à jour non ?

PS: @daoud, tout le monde n'a pas les moyens de s'offrir un SUM() à chaque requête donc conserver le total dans un champs est plutôt une bonne idée si c'est bien réalisé, non ? ;) D'ailleurs, en ce qui concerne les factures on ne pourrait pas vraiment utiliser une valeur dynamique car on a besoin du prix tel qu'il était au moment de la commande/facturation, pas le prix actuel. À moins de stocker tous les écarts de prix successifs ou d'utiliser une sorte de base de données temporelle, mais je digresse fortement là :lol:

le 45,2kbps de la loose
Invité n'ayant pas de compte PHPfrance

25 nov. 2005, 16:39

Oups, j'ai oublié une ligne dans la première requête, mais vous aurez corrigé de vous-même:

Code : Tout sélectionner

WHERE f.id_facture = x.id_facture

Eléphant du PHP | 219 Messages

02 déc. 2005, 10:35

PS: @daoud, tout le monde n'a pas les moyens de s'offrir un SUM() à chaque requête donc conserver le total dans un champs est plutôt une bonne idée si c'est bien réalisé, non ? ;) D'ailleurs, en ce qui concerne les factures on ne pourrait pas vraiment utiliser une valeur dynamique car on a besoin du prix tel qu'il était au moment de la commande/facturation, pas le prix actuel. À moins de stocker tous les écarts de prix successifs ou d'utiliser une sorte de base de données temporelle, mais je digresse fortement là :lol:
merci pour ce commentaire intéressant, je n'avais pas vu les choses sur cet angle important

:)

Eléphant du PHP | 91 Messages

10 déc. 2005, 19:50

Merci à tous pour vos réponses.

J'en retiendrai que l'utilisation des tables dérivées est la meilleur solution car on parcourt la table FACTURE une seule fois pour trouver le MONTANT_TOTAL et NOMBRE_ARTCILE_TOTAL.

La réponse de daoud avec son sous select ne calcul qu'un champs sur les deux ... et c'est la meilleur solution quand il n'y a qu'un champs à calculer.

Pour faire suite aux remarques (que j'approuve aussi) de "le 45,2kbps de la loose", mon exemple de facturation est totalement fictif, il s'agissait d'illustrer une UPDATE de deux colonnes d'une table pere en fonction dela somme des lignes d'une table fille.

Merci de votre aide.

Cordialement