Mysql
 sql >> Base de données >  >> RDS >> Mysql

base de données de conception relative à l'attribut de temps

Voici un modèle pour répondre à votre exigence déclarée.

Lien vers le modèle de données de séries chronologiques

Lien vers la notation IDEF1X pour ceux qui ne connaissent pas la norme de modélisation relationnelle.

  • Normalisé à 5NF ; pas de colonnes en double ; pas d'anomalies de mise à jour, pas de nulls.

  • Lorsque le statut d'un produit change, insérez simplement une ligne dans ProductStatus, avec le DateTime actuel. Pas besoin de toucher aux lignes précédentes (qui étaient vraies et restent vraies). Aucune valeur fictive que les outils de rapport (autres que votre application) n'ont à interpréter.

  • Le DateTime est le DateTime réel auquel le produit a été placé dans ce statut ; le "De", si vous voulez. Le "To" est facilement dérivé :il s'agit du DateTime de la ligne suivante (DateTime> "From") pour le produit ; là où il n'existe pas, la valeur est la DateTime actuelle (utilisez ISNULL).

Le premier modèle est terminé; (ProductId, DateTime) est suffisant pour fournir l'unicité, pour la clé primaire. Cependant, puisque vous demandez de la vitesse pour certaines conditions de requête, nous pouvons améliorer le modèle au niveau physique et fournir :

  • Un index (nous avons déjà l'index PK, nous allons donc l'améliorer en premier, avant d'ajouter un deuxième index) pour prendre en charge les requêtes couvertes (celles basées sur n'importe quel arrangement de { ProductId | DateTime | Status } peuvent être fournies par l'index, sans avoir pour accéder aux lignes de données). Ce qui change la relation Status::ProductStatus de Non-identifiant (ligne pointillée) à Type d'identification (ligne continue).

  • L'arrangement PK est choisi sur la base que la plupart des requêtes seront des séries temporelles, basées sur Product⇢DateTime⇢Status.

  • Le deuxième index est fourni pour améliorer la vitesse des requêtes basées sur le statut.

  • Dans l'arrangement alternatif, c'est inversé; c'est-à-dire que nous voulons surtout connaître l'état actuel de tous les produits.

  • Dans toutes les versions de ProductStatus, la colonne DateTime dans l'index secondaire (pas le PK) est DESCending; le plus récent est le premier.

J'ai fourni la discussion que vous avez demandée. Bien sûr, vous devez expérimenter avec un ensemble de données de taille raisonnable et prendre vos propres décisions. S'il y a quelque chose ici que vous ne comprenez pas, veuillez demander, et je développerai.

Réponses aux commentaires

Signaler tous les produits avec l'état actuel de 2

SELECT  ProductId,
        Description
    FROM  Product       p,
          ProductStatus ps
    WHERE p.ProductId = ps.ProductId  -- Join
    AND   StatusCode  = 2             -- Request
    AND   DateTime    = (             -- Current Status on the left ...
        SELECT MAX(DateTime)          -- Current Status row for outer Product
            FROM  ProductStatus ps_inner
            WHERE p.ProductId = ps_inner.ProductId
            )
  • ProductId est indexé, premier col, des deux côtés

  • DateTime dans Indexé, 2e colonne dans l'option de requête couverte

  • StatusCode est indexé, 3ème colonne dans l'option de requête couverte

  • Depuis StatusCode dans l'index est DESCending, une seule extraction est nécessaire pour satisfaire la requête interne

  • les lignes sont requises en même temps, pour une seule requête ; ils sont proches les uns des autres (en raison de Clstered Index) ; presque toujours sur la même page en raison de la taille courte des lignes.

Il s'agit de SQL ordinaire, une sous-requête, utilisant la puissance du moteur SQL, traitement d'ensemble relationnel. C'est la méthode correcte , il n'y a rien de plus rapide et toute autre méthode serait plus lente. N'importe quel outil de rapport produira ce code en quelques clics, sans saisie.

Deux dates dans ProductStatus

Les colonnes telles que DateTimeFrom et DateTimeTo sont des erreurs grossières. Prenons-le par ordre d'importance.

  1. C'est une grossière erreur de normalisation. "DateTimeTo" est facilement dérivé de l'unique DateTime de la ligne suivante ; elle est donc redondante, une colonne dupliquée.

    • La précision n'entre pas en ligne de compte :cela est facilement résolu grâce au DataType (DATE, DATETIME, SMALLDATETIME). Que vous affichiez une seconde, une microseconde ou une nanoseconde de moins, c'est une décision commerciale ; cela n'a rien à voir avec les données stockées.
  2. L'implémentation d'une colonne DateTo est un doublon à 100 % (de DateTime de la ligne suivante). Cela prend deux fois l'espace disque . Pour une grande table, ce serait un gaspillage inutile important.

  3. Étant donné qu'il s'agit d'une ligne courte, vous aurez besoin de deux fois plus d'E/S logiques et physiques pour lire le tableau, à chaque accès.

  4. Et deux fois plus d'espace de cache (ou en d'autres termes, seulement la moitié du nombre de lignes tiendrait dans un espace de cache donné).

  5. En introduisant une colonne en double, vous avez introduit la possibilité d'erreur (la valeur peut maintenant être dérivée de deux manières :à partir de la colonne DateTimeTo en double ou du DateTimeFrom de la ligne suivante).

  6. Il s'agit également d'une anomalie de mise à jour . Lorsque vous mettez à jour n'importe quel DateTimeFrom est mis à jour, le DateTimeTo de la ligne précédente doit être récupéré (pas grave car il est proche) et mis à jour (gros problème car il s'agit d'un verbe supplémentaire qui peut être évité).

  7. "Plus court" et "raccourcis de codage" ne sont pas pertinents, SQL est un langage de manipulation de données lourd, mais SQL est tout ce que nous avons (Débrouille toi avec). Quiconque ne peut pas coder une sous-requête ne devrait vraiment pas coder. Quiconque duplique une colonne pour atténuer une "difficulté" mineure de codage ne devrait vraiment pas modéliser des bases de données.

Notez bien que si la règle d'ordre le plus élevé (Normalisation) a été maintenue, l'ensemble des problèmes d'ordre inférieur est éliminé.

Pensez en termes d'ensembles

  • Toute personne ayant des "difficultés" ou éprouvant des "douleurs" lors de l'écriture de SQL simple est paralysée dans l'exécution de sa fonction. Généralement, le développeur n'est pas penser en termes d'ensembles et la base de données relationnelle est un modèle orienté ensemble .

  • Pour la requête ci-dessus, nous avons besoin de Current DateTime ; puisque ProductStatus est un ensemble des états du produit dans l'ordre chronologique, nous avons simplement besoin du dernier, ou MAX(DateTime) de l'ensemble appartenant au Produit.

  • Examinons maintenant quelque chose de soi-disant "difficile", en termes de ensembles . Pour un rapport sur la durée pendant laquelle chaque produit a été dans un état particulier :le DateTimeFrom est une colonne disponible et définit la coupure horizontale, un sous-ensemble (nous pouvons exclure les lignes précédentes); le DateTimeTo est le plus ancien du sous ensemble des états du produit.

SELECT               ProductId,
                     Description,
        [DateFrom] = DateTime,
        [DateTo]   = (
        SELECT MIN(DateTime)                        -- earliest in subset
            FROM  ProductStatus ps_inner
            WHERE p.ProductId = ps_inner.ProductId  -- our Product
            AND   ps_inner.DateTime > ps.DateTime   -- defines subset, cutoff
            )
    FROM  Product       p,
          ProductStatus ps
    WHERE p.ProductId = ps.ProductId 
    AND   StatusCode  = 2             -- Request
  • Penser en termes d'obtention de la ligne suivante est orienté ligne, pas traitement orienté ensemble. Paralysant, lorsque l'on travaille avec une base de données orientée ensemble. Laissez l'Optimiser réfléchir à votre place. Vérifiez votre SHOWPLAN, cela optimise magnifiquement.

  • Incapacité à penser en ensembles , se limitant ainsi à n'écrire que des requêtes à un seul niveau, n'est pas une justification raisonnable pour :implémenter des duplications massives et mettre à jour les Anomalies dans la base de données; gaspiller des ressources en ligne et de l'espace disque ; garantissant la moitié des performances. Beaucoup moins cher d'apprendre à écrire des sous-requêtes SQL simples pour obtenir des données facilement dérivées.