2009-11-19 10 views
10

Je suis en train de concevoir un panier. Pour contourner le problème des anciennes factures affichant des prix inexacts après la modification du prix d'un produit, j'ai déplacé le champ de prix de la table Product dans une table ProductPrice composée de 3 champs, pid, date et price. pid et date forment la clé primaire de la table. Voici un exemple de ce que la table ressemble à:SQL "GROUP BY" question

pid date  price 
1  1/1/09 50 
1  2/1/09 55 
1  3/1/09 54 

En utilisant SELECT et GROUP BY pour trouver le dernier prix de chaque produit, je suis venu avec:

SELECT pid, price, max(date) FROM ProductPrice GROUP BY pid 

La date et pid retour étaient exacts. J'ai reçu exactement 1 entrée pour chaque pid unique et la date qui l'accompagnait était la dernière date pour ce pid. Cependant, ce qui est venu comme une surprise était le prix retourné. Il est revenu le prix de la première ligne correspondant à la pid, qui dans ce cas était 50.

Après retravaillant ma déclaration, je suis venu avec ceci:

SELECT pp.pid, pp.price, pp.date FROM ProductPrice AS pp 
INNER JOIN (
    SELECT pid AS lastPid, max(date) AS lastDate FROM ProductPrice GROUP BY pid 
) AS m 
ON pp.pid = lastPid AND pp.date = lastDate 

Alors que la déclaration retravaillé donne le prix correct (54), il semble incroyable qu'une telle requête de sondage nécessite une jointure interne à exécuter. Ma question est la suivante: Ma deuxième déclaration est-elle le moyen le plus facile d'accomplir ce que j'ai à faire? Ou est-ce que je manque quelque chose ici? Merci d'avance!

James

+0

plus simple, mais seulement sur Postgresql: SELECT DISTINCT ON (pid) pid, date, prix DE ProductPrice ORDER BY pid, date DESC –

Répondre

9

La raison pour laquelle vous obtenez un prix arbitraire est que mysql ne peut pas savoir quelles colonnes sélectionner si vous GROUP BY quelque chose. Il sait qu'il a besoin d'un prix et une date date et peut chercher la dernière date que vous avez demandé avec max(date) mais choisit de retourner un prix qui est le plus efficace pour lui de récupérer - vous n'avez pas fourni un aggregate function pour cela colonne (votre première requête n'est pas valide SQL, en fait.)

Votre deuxième requête semble OK, mais voici une alternative plus courte:

SELECT pid, price, date 
FROM ProductPrice p 
WHERE date = (SELECT MAX(date) FROM ProductPrice tmp WHERE tmp.pid = p.pid) 

Mais si vous accédez au dernier prix beaucoup (que je pense que vous faites), je recommande réintégrant la vieille colonne à votre table d'origine pour conserver la valeur la plus récente, si vous avez la possibilité de modifier à nouveau la structure de la base de données.

+0

D'autres SGBD se plaignent que le prix n'est pas inclus en tant qu'agrégation ou en tant que groupement. –

+0

Je dois convenir que c'est beaucoup plus propre. – Zaid

1

Vous pourriez vouloir essayer:

SELECT pid, price, date FROM ProductPrice GROUP BY pid ORDER BY date DESC 

groupe a une fonctionnalité obscure, je suis trop toujours incertain si c'est le champ de droite ... mais il devrait être le premier dans le résultat.

+0

Cela ne fonctionnera pas. Il ordonne la table résultante par date décroissante, pas la table d'origine, de sorte que le dernier prix ne sera pas sélectionné. – Zaid

3

Je pense que vous avez brisé votre schéma de base de données.

Pour contourner le problème des vieilles factures indiquant les prix inexacts après le prix d'un produit obtient changé, je me suis déplacé le champ de prix de la table de produit dans une table de ProductPrice qui se compose de 3 champs, pid, date et prix. pid et date forment la clé primaire de la table.

Comme vous l'avez souligné, vous devez garder un historique des prix. Mais vous pouvez toujours conserver le prix actuel dans la table des produits en plus de cette nouvelle table. Cela rendrait votre vie beaucoup plus facile (et vos requêtes plus rapidement).

0

Voici une autre -possibly inefficient- un:

SELECT pid, substring_index(group_concat(price order by date desc), ',', 1) , max(date) 
    FROM ProductPrice 
GROUP BY pid 
0

Je pense que la clé ici est simple sonne requête - vous pouvez voir ce que vous voulez, mais les ordinateurs n'est pas humain et ainsi de produire la résultat souhaité des opérations basées sur un ensemble, vous devez être explicite comme dans la deuxième requête. La requête interne identifie le dernier prix pour chaque produit, puis la requête externe vous permet d'obtenir la valeur du dernier prix - c'est à peu près aussi simple que possible. En outre, si vous avez un système de facturation, vous devriez vraiment stocker le prix du produit (et les taux d'imposition ainsi que les "codes") avec la facture, c'est-à-dire que les tableaux de factures doivent contenir informations financières pour reproduire la facture. En général, vous voulez pas compter sur la possibilité de rechercher un prix (ou un taux d'imposition) dans une table mutable, même en tenant compte du système présenté ci-dessus. Indépendamment de cela, l'historique des prix a ses propres mérites.

1

Vous ne pouvez pas résoudre votre problème avec la clause GROUP BY, car pour chaque groupe de pid, MySQL récupérera simplement le premier pid, la date maximum et le premier prix trouvé (ce qui n'est pas ce dont vous avez besoin).

Vous pouvez soit utiliser un sous-requête (qui peut être inefficace):

SELECT pid, date, price 
FROM ProductPrice p1 
WHERE date = (SELECT MAX(p2.date) 
       FROM ProductPrice p2 
       WHERE p1.pid = p2.pid) 

ou vous pouvez simplement se joindre à la table avec elle-même:

SELECT p1.pid, p1.date, p1.price 
FROM  ProductPrice p1 
LEFT JOIN ProductPrice p2 ON p1.pid = p2.pid 
      AND p1.date < p2.date 
WHERE  p2.pid IS NULL 

Jetez un oeil à this section de documentation MySQL.

0

J'ai rencontré le même problème dans un de mes projets j'ai utilisé la sous-requête pour récupérer la date et ensuite la comparer mais cela rend le système lent lorsque les données augmentent. Il est donc préférable de stocker le dernier prix dans votre tableau Produits en plus de la nouvelle table que vous avez créée pour conserver l'historique des changements de prix.

Vous pouvez toujours utiliser l'une des requêtes suggérées ppl pour obtenir le dernier prix du produit à une date donnée. mais aussi vous pouvez ajouter un champ dans la même table est le dernier. Donc, pour une date, vous pouvez rendre le drapeau vrai une fois. et vous pouvez toujours trouver le dernier prix du produit pour une date particulière par une simple requête.