Est-ce une mauvaise pratique d'agréger les données de différentes tables en une seule?


12

Contexte

J'écris de nombreux rapports volumineux et je gère généralement une grande base de données de dossiers de santé (écriture de SP, fonctions, travaux, etc.). Le schéma d'origine et le logiciel qui l'utilise proviennent d'un fournisseur différent, donc je ne peux pas y changer grand-chose structurellement. Il existe de nombreux enregistrements qui nécessitent un suivi tels que les laboratoires, les procédures, les vaccins, etc. et ils sont dispersés sur des dizaines de tableaux, dont beaucoup sont gonflés et mal indexés (j'ai pu résoudre ce problème quelque peu).

Le problème

Le problème est que, parce que nous avons peu de contrôle sur la base de données et qu'elle peut changer à partir d'une mise à jour ou d'un correctif donné, cela rend la rédaction et la maintenance de ces rapports difficiles et fastidieuses, en particulier en cas de chevauchement important. Tout ce qu'il faut, c'est un patch et je suis coincé à réécrire de grandes parties d'une douzaine de rapports. De plus, les requêtes deviennent rapidement obscurcies et lentes à mesure que les jointures, les sélections imbriquées et les applications s'empilent.

Ma "solution"

Mon plan consistait à écrire tous ces enregistrements dans une table «fourre-tout» et à écrire des déclencheurs sur les tables d'origine pour conserver les enregistrements dans cette table agrégée. Bien sûr, je devrais m'assurer que mes déclencheurs étaient intacts après les mises à jour, mais cela serait beaucoup plus facile du point de vue de la maintenabilité et du simple référencement des données.

Le tableau serait mince et long, ne stockant que les données requises, quelque chose comme ceci:

CREATE TABLE dbo.HCM_Event_Log (
    id INT IDENTITY,
    type_id INT NULL,
    orig_id VARCHAR(36) NULL,
    patient_id UNIQUEIDENTIFIER NOT NULL,
    visit_id UNIQUEIDENTIFIER NULL,
    lookup_id VARCHAR(50) NULL,
    status VARCHAR(15) NULL,
    ordered_datetime DATETIME NULL,
    completed_datetime DATETIME NULL,
    CONSTRAINT PK_HCM_Event_Log PRIMARY KEY CLUSTERED (id)
)

Ensuite, j'aurais diverses tables relationnelles pour des choses comme les groupements type_id et item.

Je commence à deviner cette idée, car plusieurs de ces tableaux sont écrits dans une certaine mesure, les SP et les rapports que j'écrirais feraient également référence aux données. Je crains donc que cette table ne devienne un cauchemar de verrouillage et de performances avec autant d'E / S.

Ma question

Est-ce une mauvaise ou une bonne idée? Je me rends compte que chaque situation est différente dans SQL Server (2008 r2 Standard Edition BTW), et la règle «parfois», mais je suis vraiment à la recherche de conseils généraux.

J'ai commencé à envisager d'utiliser un courtier de services, mais je n'effectuerais que de simples mises à jour / insertions ( voir l'alternative à la réponse acceptée ). Dans de nombreux cas, les données doivent être en temps réel, donc l'utilisation d'une base de données de sauvegarde ne fonctionnerait pas vraiment. Les performances sont déjà quelque peu un problème pour nous, mais la plupart d'entre elles sont liées au matériel et seront bientôt résolues.


1
Pouvez-vous appliquer les pannes planifiées? Si aucune de ces mises à jour ne peut effacer un déclencheur et vous ne mettrez pas à jour vos agrégats, ce qui pourrait entraîner de mauvaises données.
Erik

Vous envisagez de regrouper toutes les informations sur les laboratoires, les procédures, les vaccins et les patients sur une seule table? Mauvaise idée. Vous pourrez peut-être utiliser un schéma en étoile, si cela convient au type de requêtes que vous exécutez.
Michael Green

1
Avez-vous envisagé de créer des vues indexées? Celles-ci mettraient une couche logique entre votre code et le fournisseur afin que vous puissiez simplement mettre à jour la vue si le fournisseur change les choses en dessous. De plus, les vues indexées seraient préremplies pour vous et fourniraient de bonnes performances de lecture. L'une des considérations les plus importantes pour ce faire est la charge qu'il impose aux opérations d'écriture des tables de base de données du fournisseur. Cependant, cela serait probablement une solution plus propre et plus facile à entretenir que l'utilisation de déclencheurs, etc.
Micah Nikkel

Désolé pour la réponse tardive, merci pour les commentaires. @Erik - Oui, nous avons prévu des mises à jour, et je vérifie que toutes mes modifications précédentes sont toujours en place via une série de scripts de liste de contrôle que j'exécute, donc il n'y aura pas de surprise là-bas et je garderai CREATE scripts pour tous les déclencheurs.
jreed121

@ MichaelGreen - Je vais examiner un schéma en étoile, mais je suis curieux de savoir pourquoi vous pensez que le fait d'avoir toutes ces données dans une table est une mauvaise idée? L'environnement d'application est complètement isolé sur un VPN, il n'est de toute façon pas accessible en dehors du réseau. Si quelque chose ne va pas avec la table, ce n'est pas la fin du monde parce que je pourrais tout simplement y réécrire. Le tableau ne sera pas utilisé pour les données essentielles à la mission, ou du moins il ne sera pas le seul, ni principal, lieu où les données sont stockées.
jreed121

Réponses:


8

Si je vous ai bien compris,

  • vous avez un grand système tiers,
  • vous n'avez pas beaucoup de contrôle dessus,
  • vous réalisez des rapports complexes qui lisent des données directement à partir de cette base de données tierce,
  • vos requêtes dépendent de la structure interne de la base de données tierce.

Je l'approcherais comme ceci:

  • Mettre en place ma propre base de données distincte, dont j'ai le contrôle total.
  • Mettre en place un processus de synchronisation qui lit les données des tables et colonnes pertinentes de la base de données tierce et insère / met à jour dans la mienne.
  • Développer mes rapports complexes sur la base de la structure stable de ma base de données.

Dans ce cas, vous pouvez affiner la structure et les index de votre base de données pour améliorer les performances de vos rapports, sans affecter le système tiers. À moins que la structure de données d'origine ne change radicalement, la logique de vos requêtes pour vos rapports ne changera pas si la base de données tierce change. Vous ne devriez ajuster que le processus de synchronisation.

Le processus de synchronisation est en fait le processus de conversion - vous convertissez les données d'une base de données tierce en la structure dont vous avez besoin. Une partie de ce processus de conversion pourrait résoudre les problèmes de normalisation que la base de données tierce d'origine pourrait avoir. Seule cette partie du système doit connaître et dépendre de la structure interne du système tiers. Vos principaux rapports et requêtes principales ne dépendraient que de votre base de données.

Donc, le point principal est - séparez et limitez la partie de votre système qui dépend des internes du système tiers.

mise à jour

Concernant l'exigence en temps réel. BTW, j'ai toujours pensé que la définition de "temps réel" est "temps de réponse garanti", pas "un petit temps de réponse". Cela dépend bien sûr de votre application. Dans ma pratique, il suffit que je synchronise deux bases de données dans la minute suivant le changement détecté. Si un utilisateur voit un rapport à l'écran et certaines modifications de données sous-jacentes, le rapport doit être réexécuté pour refléter cette modification. Vous pouvez interroger les modifications ou écouter certains événements / messages, mais la requête de rapport doit être exécutée à nouveau pour afficher les dernières modifications.

Vous avez déjà l'intention d'écrire des déclencheurs pour capturer les modifications dans les tables d'origine et d'écrire ces modifications dans une table générique. Donc, capturez les modifications comme vous le vouliez, mais écrivez-les dans des tableaux correctement normalisés, pas un seul.

C'est donc un cas extrême - la conversion de la structure de données tierce en votre structure de données interne est effectuée dans les déclencheurs qui se déclenchent sur INSERT/UPDATE/DELETEdes tables tierces. Cela peut être délicat. Le code des déclencheurs dépendrait de la structure interne des deux systèmes. Si la conversion n'est pas anodine, elle peut retarder l'original INSERT/UPDATE/DELETEau point de l'échouer. S'il y a un bug dans votre déclencheur, il peut affecter la transaction d'origine au point de l'échec. Si un système tiers change, cela peut casser votre déclencheur, ce qui entraînerait l'échec des transactions du système tiers.

Cas moins extrême. Pour rendre le code de vos déclencheurs plus simple et moins sujet aux erreurs, écrivez toutes les modifications capturées dans certaines tables de staging / audit / diff, définissez un indicateur / envoyez un message indiquant que des modifications sont en attente et lancez le processus de conversion principal qui irait à travers ces tables intermédiaires et effectuer la conversion. L'essentiel ici est que le processus de conversion potentiellement lourd devrait se produire en dehors de la portée de la transaction d'origine.

À un deuxième coup d'œil, cela ressemble à peu près à votre suggestion d'origine dans la question. Mais la différence est la suivante: les tables de capture globale contiennent uniquement des données temporairement; la quantité de données est petite - juste ce qui a changé; il n'est pas nécessaire que ce soit une seule table; éventuellement, les données seront stockées dans des tables permanentes distinctes correctement normalisées, dont vous avez le plein contrôle, qui sont indépendantes du système tiers et que vous pouvez régler pour vos requêtes.


Si vous optez pour le transfert par lots, nous avons eu du succès avec Change Tracking (et Change Data Capture, selon vos besoins) à un nombre de transactions assez élevé (100K par jour). C'est plus simple que d'implémenter vos propres tables de staging / audit / diff et peut être déployé sans changement de code d'application ni déclencheurs.
Michael Green

Qu'il s'agisse de déclencheurs ou de CDC, la seule façon de vous rapprocher du temps réel est le streaming ou la mise en file d'attente. La file d'attente est un bon compromis pour la latence et la rentabilité. Votre temps sera consacré à des méthodes pour traiter la file d'attente plus rapidement. laissant la majorité du travail asynchrone de l'application, et mettant moins de charge sur les transactions utilisateur. Dans le passé, j'ai fait cela contre Allscripts Sunrise EMR avec un service qui a traité la file d'attente avec des appels C # foreach parallèles. la latence typique pour les nouvelles données à traiter et disponibles dans l'entrepôt était inférieure à 30 secondes
Brad D

J'ai peut-être trop dit "en temps réel", je ne suis pas trop préoccupé par les millisecondes ou même 5 secondes, mais j'ai de nombreuses requêtes sur lesquelles notre personnel s'appuie pour gérer le flux de travail. Si un client a eu quelque chose à lui faire (procédure, vaccination, etc.), nous devrons le montrer dans un court laps de temps. Les conversions sont triviales, et / ou même pas des conversions. Je ne suis pas trop préoccupé par la modification des tables des fournisseurs, car elles ne changent pas souvent, et je dois le faire maintenant de toute façon, mais je pensais qu'il était plus facile de mettre à jour / recréer un déclencheur qu'une douzaine de rapports / requêtes / SPs. J'exécute des vérifications après chaque mise à jour.
jreed121

@ jreed121, je pense aussi qu'il est plus facile de mettre à jour les déclencheurs que les rapports. Vous aurez probablement un déclencheur sur chaque table source pour capturer les modifications, il est donc probable qu'il s'agisse de plusieurs déclencheurs. N'essayez toujours pas d'écrire toutes ces modifications capturées dans une énorme table dénormalisée. Écrivez-les dans un ensemble de tableaux correctement normalisés. Vos rapports doivent être basés sur ces tables normalisées que vous contrôlez et ne doivent pas dépendre de tables originales susceptibles de changer.
Vladimir Baranov

3

Dans tous les cas, placez-le dans un ensemble normalisé de tableaux afin de pouvoir modifier l'étape d'importation plutôt que de devoir modifier des rapports et des requêtes complexes. Mais les données devraient encore être normalisées, ce qui nécessitera plusieurs tables (mais avec de bons indices).

Comme d'autres l'ont mentionné, n'utilisez pas de déclencheurs, synchronisez par lots.

Ne vous inquiétez pas de nombreuses jointures, lorsque les données sont normalisées et indexées correctement, celles-ci n'ajoutent pas de coût ni de charge de gestion importants.

Le moment de se dénormaliser en quelque chose comme un entrepôt de données, c'est quand vous devez être en mesure de faire beaucoup de différents types de requêtes sur les données que vous ne pouvez pas prédire. Il a ses propres inconvénients et frais généraux et devrait être utilisé le cas échéant, et non comme une chose de choix.


3

J'ai travaillé avec une situation très similaire comme celle-ci dans le passé dans une entreprise de fabrication 24h / 24 et 7j / 7 et j'ai finalement décidé d'utiliser la réplication transactionnelle. Il est possible de configurer DDL pour qu'il soit répliqué de sorte que vous puissiez envoyer tout ce que les correctifs changeront à l'abonné. De toute évidence, il y a des avantages et des inconvénients à tout et vous devez les peser pour déterminer ce que vous pouvez soutenir par rapport à ce qui fonctionne le mieux pour l'entreprise.

Du côté positif:

  1. Le «temps réel» est limité uniquement aux performances de validation de réseau et de transaction sur l'abonné. D'après mon expérience avec un système TPS modérément élevé, nous avons été répliqués en moins de 10 secondes de données "en temps réel".
  2. Séparation des charges de travail. Vous exécutez actuellement une charge de travail mixte sur un serveur. Si vous pouvez séparer ces deux problèmes, vous pourrez peut-être bénéficier des performances des deux systèmes en supprimant une charge de travail de l'équation.
  3. Contrôle. Vous pourrez effectuer des modifications d'indexation / statistiques / maintenance en fonction de votre charge de travail de génération de rapports.

Il y a cependant des inconvénients:

  1. Coût. Une autre licence et plus de matériel (virtuel ou autre).
  2. Réplication. Cela fonctionne très bien une fois qu'il a été correctement configuré, mais cela peut être compliqué d'arriver à ce point.
  3. Entretien. Si vous apportez des modifications délétères aux structures (par exemple, supprimez un index), elles reviendront lorsque le cliché est appliqué (après que la publication a changé ou lorsque les articles ont changé).

2

Mon plan consistait à écrire tous ces enregistrements dans une table «fourre-tout» et à écrire des déclencheurs sur les tables d'origine pour conserver les enregistrements dans cette table agrégée.

Les déclencheurs ont tellement de problèmes que vous devriez les éviter:

  • Une erreur dans un déclencheur peut entraîner l'annulation de la transaction d'origine
  • Les déclencheurs qui gèrent correctement les opérations sur plusieurs lignes sont difficiles à écrire
  • Les déclencheurs peuvent confondre les applications clientes en modifiant l'ensemble de lignes renvoyé (par exemple, un déclencheur remplace le nombre de lignes affectées)
  • Lorsqu'un déclencheur en déclenche un autre, les résultats sont difficiles à prévoir

Une meilleure option est un travail qui copie périodiquement les données dans une nouvelle table. Vos rapports peuvent exécuter la copie. Un travail qui copie des lignes est facile à écrire et à gérer, et il n'y a aucun risque qu'il affecte le fonctionnement de l'application tierce.


1. Les déclencheurs seraient simples, donc les erreurs levées seraient minimes, voire inexistantes. 2. Le déclencheur lui-même ne gérerait pas plusieurs lignes (IE une ligne mise à jour dans le tableau avec le déclencheur ne provoquerait pas la mise à jour de plusieurs lignes ailleurs), mais plusieurs lignes pourraient être insérées / mises à jour / supprimées simultanément dans la source table - c'est ce que vous voulez dire? 3. cela ne pouvait-il pas être traité avec NOCOUNT? 4. Il n'y aurait pas de déclencheurs sur la table de destination, et je pourrais garantir la même chose pour les autres.
jreed121

Comme vous le dites, il est théoriquement possible de faire fonctionner les déclencheurs. C'est juste qu'en pratique, ils ne le font jamais.
Andomar
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.