Laquelle de ces conceptions de table est la plus performante?

16

On m'a demandé de créer quelque chose qui suit le coût quotidien à percevoir sur les comptes, et j'essaie de trouver un schéma de table de base de données qui prendrait en charge cela.

Voici ce que je sais

  • La société compte plus de 2,5 millions de comptes
  • Parmi ceux-ci, ils travaillent actuellement en moyenne 200 000 par mois (cela change avec les niveaux de dotation, qui sont actuellement faibles)
  • Ils ont 13 types de coûts différents qu'ils aimeraient suivre, et ils ont averti qu'ils pourraient en ajouter d'autres à l'avenir
  • Ils veulent que les coûts soient suivis quotidiennement
  • Les coûts ne sont pas répartis sur l'ensemble de l'inventaire. Ils sont répartis sur le nombre de comptes travaillés par mois (200 000), ou les utilisateurs peuvent saisir des identifiants de compte pour appliquer un coût à un groupe de comptes, ou ils peuvent simplement spécifier à quels comptes appliquer le coût.

Ma première pensée a été une base de données normalisée:

Identifiant de compte
Date
CostTypeId
Montant

Mon problème avec ceci est, faites le calcul. Ce tableau va devenir énorme rapidement. En supposant que les 13 types de coûts soient appliqués à tous les comptes actifs pour le mois en cours, c'est-à 200k * 13 * N days in month-dire environ 75 à 80 millions d'enregistrements par mois, soit près d'un milliard d'enregistrements par an.

Ma deuxième pensée a été de le dénormaliser un peu

Identifiant de compte
Date
Coût total
CostType1
CostType2
CostType3
CostType4
CostType5
CostType6
CostType7
CostType8
CostType9
CostType10
CostType11
CostType12
CostType13

Cette méthode est plus dénormalisée et peut créer jusqu'à 6 millions d'enregistrements par mois ( 200k * N days in month), soit environ 72 millions par an. C'est beaucoup moins que la première méthode, mais si l'entreprise décide d'un nouveau type de coût à l'avenir, une autre colonne de base de données devra être ajoutée.

Des deux méthodes, laquelle préférez-vous? Pourquoi? Y a-t-il une autre alternative à laquelle vous pourriez penser qui gérerait mieux cela?

Je suis plus intéressé par les rapports de performance, à la fois les rapports résumés et détaillés. Le travail qui répartira les coûts sur les comptes sera exécuté tous les soirs lorsque personne n'est présent. Une préoccupation secondaire est la taille de la base de données. La base de données existante fait déjà près de 300 Go, et je pense que l'espace sur le disque est d'environ 500 Go.

La base de données est SQL Server 2005

Rachel
la source
Obtenez donc un autre disque. Les disques sont bon marché. Vous pouvez avoir 2 To pour le coût d'une réunion pour en discuter.

Réponses:

9

Un milliard d'enregistrements par an, ce n'est pas beaucoup.

Avec le partitionnement (par type de coût peut-être) et l'archivage, il est gérable.

Le nombre d'éléments de données à stocker est toujours de 200k * 13 * N. En tant que colonnes, vous obtiendrez moins de lignes par page et cela prendra plus d'espace qu'en tant que lignes. Vous pouvez gagner si "CostType1" n'est pas un type de données de longueur fixe, mais il est marginal.

"KISS" comme on dit

gbn
la source
3
@Rachel Je recommande vivement d'implémenter un schéma de partitionnement avec un ensemble de données aussi volumineux. S'ils se concentrent sur le travail et les rapports mensuels, il est préférable de choisir une clé de partition qui peut coïncider avec cet état d'esprit. De plus, si vous configurez correctement votre partition, vous pouvez facilement basculer les données de la table vers des tables intermédiaires, ce qui permet de charger et de supprimer des données pour les ensembles de données en un clin d'œil qui prend quelques secondes au lieu de plusieurs heures.
David
6

Bien que votre conception puisse certainement faire une différence de jour comme de nuit, dans ce cas, je me concentrerais davantage sur les index, y compris en couvrant les index si nécessaire. J'examinerais également certains des outils que SQL Server vous offre pour gérer les très grandes tables, telles que le partitionnement de table.

Pensez-y de cette façon, même s'il y a 80 milliards d'enregistrements dans le tableau, avec une indexation appropriée, ceux qui vous intéressent réellement à un moment donné seront regroupés physiquement sur le disque. En raison de la façon dont les données sont organisées dans SQL Server, les données divisées par limites d'index peuvent également se trouver dans une autre table car elles n'ont pas à lire la table entière pour obtenir ce dont elles ont besoin.

Si vous choisissez également de partitionner la table, vous pouvez améliorer le temps d'accès et l'insertion du temps.


la source
4

Je normaliserais. Nous avons fait une comptabilité analytique pour la rentabilité des comptes clients dans une banque et nous avons généré plus de 250 millions de lignes de coûts individuels en utilisant des centaines de pilotes qui ont été répartis par centre de coûts ou par grand livre ou par diverses autres techniques sur des millions de comptes chaque mois.

Par exemple, le coût total de l'entretien des distributeurs automatiques de billets a été réparti entre les comptes qui avaient utilisé des distributeurs automatiques de billets en fonction de la quantité relative d'utilisation. Donc, si 1 million de dollars a été dépensé pour l'entretien des guichets automatiques et que 5 clients l'ont utilisé une fois chacun et qu'un client l'a utilisé 5 fois, alors un client a coûté 0,5 million de dollars à la banque et les autres clients à la banque 0,1 million de dollars chacun. D'autres pilotes pourraient être beaucoup plus complexes.

En fin de compte, vous trouverez probablement que c'est rare - certains comptes ne reçoivent pas de coûts de certaines sources / pilotes - et certains comptes ne reçoivent rien. Dans un modèle normalisé, ces lignes n'existent pas. Dans le modèle dénormalisé, la ligne existe, avec quelques colonnes vides. En outre, dans un modèle normalisé clairsemé, vous devriez voir les performances s'améliorer, car l'existence d'une ligne est généralement plus rapide à vérifier (avec un index de couverture sur CostType) que la vérification de toutes les lignes avec non NULL dans un "compartiment" particulier (même avec index sur chaque colonne de montant - que vous pouvez voir commence à devenir très inutile).

Cade Roux
la source
SPARSE - C'est un très bon point qui fait toute la différence. S'il est rare, vous économisez de l'espace en normalisant. Sinon, non. Mais l'espace disque est bon marché, donc personnellement je vote pour une flexibilité maximale (normalisée).
3

Indépendamment de l'avantage de performance, je serais certainement en faveur de l'option 1. L'option 2 serait de voler Peter pour payer Paul, à mon avis.


la source
2

J'irais avec l'option 1, puis si la vitesse de génération de rapports devenait un problème sur la route, j'ajouterais également le tableau 2 et le remplirais dans une base de données de génération de rapports dans une sorte de processus automatisé de nuit / hors-pointe.

Vous pouvez également envisager de regrouper la structure quotidienne du tableau 2 en d'autres cumuls hebdomadaires, mensuels, trimestriels et annuels si cela est justifié.

Mais, comme je l'ai dit, je choisirais également de stocker les données «brutes» sous une forme appropriée (normalisée).

EJ Brennan
la source
0

Compte tenu des volumes que vous mentionnez, je choisirais la deuxième option, mais sans TotalCost. On pourrait dire que c'est encore normalisé.


Modifier: comme alternative, et en fonction de vos besoins et de la taille du AccountId, vous pouvez également envisager les éléments suivants:

AccountDate
-----------
AccountId  
Date  
AcDtID (surrogate key)

Costs
-------
AcDtID
CostTypeId  
Amount  

Avec cette conception, vous pouvez toujours ajouter un TotalCost dénormalisé à la première table et le faire recalculer tous les soirs, ce qui permet d'exécuter certains rapports uniquement sur la première table.

Patrick Honorez
la source
J'en ai TotalCostlà parce que la majorité des rapports sont résumés, et j'ai pensé qu'il serait plus rapide d'interroger une seule valeur que d'ajouter 13 valeurs différentes.
Probablement, mais alors vous introduisez vraiment une dépendance transitive. Ces enregistrements seront-ils jamais mis à jour? ou simplement écrit et ensuite lu?
Les enregistrements seront mis à jour chaque fois qu'un nouveau coût est appliqué à cette plage de dates. Après environ un mois, il est peu probable que le coût total soit mis à jour, mais c'est toujours possible en raison de choses comme les frais de support annuels.
Ensuite, chaque mise à jour nécessiterait 2 mises à jour et le champ TotalCost ajoute un risque d'incohérence.
Dépendance transitoire, mais pas nécessairement un risque d'incohérence - une contrainte CHECK () peut garantir que TotalCost est toujours la somme des coûts.
Mike Sherrill 'Cat Recall'
0

vous devez en fait diviser la première table en deux tables afin de pouvoir utiliser une sous-requête et sélectionner la deuxième ligne comme colonne ou plusieurs colonnes. il est plus flexible de cette façon et par là, vous pouvez obtenir un résultat comme le second plus facilement.

Uğur Gümüşhan
la source