web-dev-qa-db-fra.com

Lorsque vous conservez l'historique des prix du prix du produit, quels sont les avantages et les inconvénients de suivre les événements de changement de prix uniquement?

Lors du suivi de quelque chose comme l'historique des prix, dans ma base de données relationnelle, quelle est la manière recommandée de stocker des changements de tarification? Par exemple, je peux:

  • gardez les événements de changement de prix uniquement (aka une date unique indiquant un nouveau prix a été saisi). Une date par rapport à l'historique des prix. alias price_change_date

ou

  • garder effective_price_start_date et effective_price_end_date (AKA, lors de la saisie d'un nouveau prix, l'ancien enregistrement reçoit et mis à jour "Date de fin", et le nouvel enregistrement devient la date actuelle comme "Date de début" et null (?) Pour "Date de fin"). C'est deux dates pour chaque enregistrement d'historique des prix.

Je pense que la première approche - ne conserve que l'enregistrement de date unique qui signifie "C'est la date à laquelle le prix a été saisi" est suffisant. Et peut-être un peu plus simple à maintenir.

Cependant, j'ai vu des suggestions dans d'autres réponses qui recommandent de garder une durée effective du prix, c'est-à-dire la date de début des prix et la date de fin. Mais dans cette approche, il y a des complications telles que

  • et si je veux revenir à la tarification précédente? Est-ce que je viens de créer un nouvel enregistrement avec la même valeur de prix mais des dates différentes?
  • Je dois maintenant traiter avec des problèmes de coin comme - je viens de passer un nouveau prix, que dois-je passer pour une date de fin efficace lorsqu'il est indéterminé? Est-ce que je place null puis interprétez-le comme "actuellement actif"?

Existe-t-il des problèmes spécifiques que je ferai face si je choisis d'aller avec la première approche (une colonne de date) plutôt que la deuxième approche (deux colonnes de date par enregistrement)

objectif

L'objectif est des objectifs de l'audit et des questions des clients. I.E. HR demande à quoi nous avons-nous facturé la partie X en janvier 2015? Comment le découvrions-nous?

Ou le client dit pourquoi vous chargeez-vous 200 $ de plus pour cela par rapport à l'année dernière lorsque je vous ai commandé? Les ventes peuvent revenir en arrière et dire Oh oui, nous avons changé le prix en décembre pour cette raison ou cette raison.

Comment les changements sont des changements - disons que HR dirige que, en janvier, nous devrions facturer 100 $ pour la partie x, mais à partir de février chargé de 200 $.

5
Dennis

conception de domaine axée sur l'approvisionnement de l'événement , affirmeriez que vous stockez les événements à l'aide d'une entrée immuable qui passe dans un journal des annexes. Vous stockez uniquement l'événement comme cela se produit, une date d'événement unique, (c'est-à-dire aucune date de fin).

Cependant, vous voulez également probablement connaître le prix actuel de tout, donc CQRS/ES dit que vous avez une autre table quelque part qui stocke simplement cela; La conception CQRS/ES appellerait ce tableau un cache, qui pourrait être jeté et reconstruit à volonté. (Alors, ils se concentrent sur le durcissement des bûches et non le cache.)

Lorsque vous stockez le prix actuel, vous n'avez pas besoin des informations de date de fin, car vous ne stockez que le prix actuel. Vous pouvez inclure une date de date, si vous le souhaitez, mais tout autre calcul concernant les prix anciens devra probablement revenir au magasin d'événements (ou stocker d'autres informations en cache).

(BTW, le magasin d'événements pourrait être la même base de données SQL ou quelque chose de différent.)

L'avantage de ceci est que les journaux immuables qu'aux annexes ne sont faciles dans une variété de technologies (SQL, NOSQL) et que le cache qui stocke la date actuelle n'est qu'une table très simple à utiliser.

6
Erik Eidt

Définitivement la deuxième approche est plus facile à travailler avec:

  • avec une seule date, c'est-à-dire la date de début d'un nouveau prix, vous ne savez pas si un enregistrement de prix donné est celui à utiliser pour un calcul à une date donnée: [.____]
    • vous devez toujours lire plusieurs enregistrements, accessibles de manière triée pour déterminer quel prix doit être utilisé
    • même pour le prix actuel, vous ne pouvez pas être sûr, car il pourrait y avoir déjà des records pour les changements de prix prévus dans le futur
  • avec la date de début et de fin, vous travaillez avec une certaine redondance. Mais il est beaucoup plus facile de faire des requêtes relationnelles qui utilisent le bon prix.

Lors de l'utilisation de la deuxième approche, la révolte à un prix précédent a d'abord être analysée d'une perspective commerciale:

  • est-il à propos de l'ancien prix mais d'une nouvelle date à venir? Dans ce cas, vous simplement ajouter un nouveau record de prix comme d'habitude
  • est-ce une erreur et le nouvel enregistrement doit être annulé? Dans ce cas, il est plus complexe:
  • vous devez trouver le précédent enregistrement de prix et le mettre à jour en réinitialisant la date de fin. La recherche de l'enregistrement précédent peut soit être utilisée avec un accès séquentiel à des enregistrements de prix ordonnés en diminution, soit simplement en utilisant la date d'extrémité connue (comme nous connaissons la date de début de l'enregistrement de prix à effacer).
  • mais vous devez également trouver un calcul stocké qui a utilisé le mauvais prix et la mise à jour
  • bien entendu, pour la cohérence des soucies, les étapes précédentes doivent être effectuées dans une seule transaction.

Pour la date de fin de fin indéterminée, il y a deux écoles:

  • le puriste laisserait le champ NULL pour montrer que cette information n'est pas connue.
  • le pragmatiste mettrait '31 .12,9999 'comme date de fin. Ce n'est pas si génial, mais cela permet une plus grande consistance (date de fin toujours présente) et une flexibilité (utilisation de la date de début ou de la date de fin du tri) dans les requêtes. Un major ERP Vendor utilise cette approche régulièrement, ce qui me fait dire que c'est une technique éprouvée, même si elle n'est pas si belle que la null.
5
Christophe

Je suis d'accord avec @christophe dans celui avec l'option 1 (une date unique par ligne) Trouver le prix pour une date donnée (en cours ou autrement) impliquerait la lecture de plusieurs enregistrements dans l'ordre (le moteur de base de données le fera, pas votre code). La requête serait comme ça:

select
    price
from
    price_history
where
    product_id = myProductID
    date <= myDate
order by date desc
limit 1;

Cette option a un avantage cependant: Vous n'avez pas à vous soucier des lacunes et des chevauchements (voir ci-dessous). Mais le gros inconvénient vient ​​Lorsque vous souhaitez modifier les périodes de temps, car vous devrez jouer avec plusieurs enregistrements et passer les processus de manière séquentielle afin d'aller de l'avant.

La deuxième option est donc meilleure, car la requête serait aussi simple que:

select
    price
from
    price_history
where
    product_id = myProductID and
    from_date <= myDate and
    ( thru_date >= MyDate or thru_date is null);

Évidemment, je pense que le prix réel devrait avoir NULL dans la colonne thru_date Beause Whe, tout simplement ne pas avoir une date à travers.

Il y a des choses à prendre en compte cependant:

  • Le pc de la table PRICE_HISTORY doit être (PRODUCT_ID,FROM_DATE) ou il devrait y avoir un index unique sur (PRODUCT_ID,FROM_DATE) si vous préférez que le PK soit un substitut.
  • Mind the Lapises: Il n'y a aucun moyen de, en utilisant des restrictions de base de données, pour éviter les lacunes, c'est-à-dire des périodes de temps sans prix. Donc, cela devra être pris en charge avec un déclencheur ou une procédure stockée.
  • Mind les chevauchements: identique que ci-dessus, un déclencheur ou SP doit être fourni pour éviter la date qui se chevauche pour le même produit, car cela entraînerait une erreur lorsqu'une requête pour Le prix donne plus d'une ligne.
  • Aussi qu'une seule ligne par produit peut avoir NULL dans THRU_DATE. Déclencheur ou SP à nouveau.

Bottom-Line:

Certaines personnes préfèrent l'option une (1 date par ligne) car cette façon n'a pas à respecter les lacunes et les chevauchements, mais la deuxième option est plus lisible par l'homme, imo.

En tout cas, ajoutez une table d'audit de prix pour refléter qui a changé quelle ligne une ligne quand. Si vous souhaitez enregistrer non seulement Old_Price et New_Price, mais aussi change dans de_date et thru_date, alors le PK sur la table PRICE_HISTORY doit être un substitut. Et ne confondez pas PRICE_HISTORY avec PRICE_AUDIT. Une table a le prix des produits au fil du temps, la seconde détient des modifications apportées aux enregistrements sur le premier.

Voir Ces autres réponses liées aux changements de prix au fil du temps, y compris AUDIT_TABLE et er diagrammes.

1
Tulains Córdova

Je vais aller avec l'option 2 comme la plupart des gens. Une conséquence positive de Datedate et de Todate est que vous pouvez mettre en œuvre élégamment des réductions temporaires sans risquer des lacunes:

Table Product_Price
Product_ID  Price  FromDate  ToDate
----------  -----  --------  --------
1           300    1/1/1900  Null
1           250    1/1/2016  8/1/2016

Ensuite, vous devez faire votre choix en obtenant le le plus bas Price valide à la date d'intérêt

Sauf si vous avez une circonstance très étrange où vous voulez un prix temporaire supérieur - auquel cas vous devez effectuer plusieurs inserts.

Un changement de prix permanent est mis en œuvre à l'aide d'une mise à jour de l'ancien prix avec une date de fin et d'une insertion du nouveau prix.

1
mcottle