Conception de Datawarehouse: dimension date / heure combinée par rapport aux dimensions et fuseaux horaires jour et heure séparés


10

Nous commençons tout juste la conception d'un nouvel entrepôt de données et nous essayons de concevoir le fonctionnement de nos dimensions de date et d'heure. Nous devons être en mesure de prendre en charge plusieurs fuseaux horaires (probablement au moins GMT, IST, PST et EST). Nous pensions au départ que nous aurions une large dimension de date et d'heure combinée jusqu'à une granularité de peut-être 15 minutes, de cette façon, nous avons une clé dans nos tables de faits et toutes les différentes données de date et heure pour tous les fuseaux horaires pris en charge sont dans une table de dimension. (c.-à-d. clé de date, date GMT, heure GMT, date IST, heure IST, etc.)

Kimball suggère d'avoir une dimension de jour distincte de la dimension d'heure pour éviter que la table ne devienne trop grande (la boîte à outils de l'entrepôt de données p. 240), ce qui semble bien, mais cela signifierait que nous avons deux clés dans nos tables de faits pour chaque fuseau horaire. nous devons prendre en charge (un pour la date et un pour l'heure).

Comme je suis très inexpérimenté dans ce domaine, j'espère que quelqu'un connaît les compromis entre les deux approches, c'est-à-dire les performances par rapport à la gestion de toutes les différentes clés de fuseau horaire. Il y a peut-être d'autres approches aussi, j'ai vu certaines personnes parler d'avoir une ligne distincte dans la table de faits par fuseau horaire, mais cela semble être un problème si vos tables de faits sont des millions de lignes, vous devez la quadrupler pour ajouter des fuseaux horaires .

Si nous faisons le grain de 15 minutes, nous aurons 131400 (24 * 15 * 365) lignes par an dans notre tableau de dimension date-heure, ce qui ne semble pas trop horrible pour les performances, mais nous ne le saurons pas avant d'en avoir testé requêtes de prototype. L'autre souci d'avoir des clés de fuseau horaire distinctes dans la table de faits est que la requête doit joindre la table de dimension à une colonne différente en fonction du fuseau horaire souhaité, c'est peut-être quelque chose que SSAS prend en charge pour vous, je ne suis pas sûr .

merci pour toutes vos pensées, -Matt


1
Cette question existe également dans Stack Overflow: stackoverflow.com/questions/2507289/… .
Jon of All Trades

Réponses:


5

La séparation de la date et de l'heure vous permettra de faire des agrégats par heure beaucoup plus facilement. par exemple: si vous souhaitez exécuter une requête pour trouver quelle période de la journée est la plus occupée. Cela est beaucoup plus facile à réaliser en utilisant une dimension temporelle distincte.

De plus, vous ne devriez avoir qu'une seule clé de temps. Décidez de l'heure GMT / EST, puis utilisez-la dans la table de faits. Si vous devez exécuter des rapports basés sur l'autre fuseau horaire, convertissez-le simplement dans votre application ou requête.


Ok, cela a du sens, les utilisateurs ne peuvent pas regrouper les données en fonction de leur fuseau horaire, mais c'est probablement quelque chose dont nous pourrions nous passer pour simplifier la conception.
Matt Palmerlee

@MattPalmerlee: les utilisateurs peuvent se regrouper par fuseau horaire si vous le leur donnez. Je l'incluais généralement dans le Geographytableau, mais si aucun ne s'applique, vous pouvez l'ajouter en tant qu'attribut de votre table de faits.
Jon of All Trades

5

Juste un suivi sur la façon dont nous avons décidé d'implémenter notre DataWarehouse pour prendre en charge plusieurs fuseaux horaires et être aussi efficace que possible: nous avons choisi de créer une table des fuseaux horaires (id, nom, etc ...) ainsi qu'un "fuseau horaire" pont "table qui ressemble à ceci:

time_zone_bridge
---------------
date_key_utc
time_key_utc
timezone_id
date_key_local
time_key_local

De cette façon, nous pouvons garder nos tables de dimension de date et d'heure normales petites, tous nos faits sont liés aux clés de date / heure UTC, puis si nous devons signaler / grouper par un fuseau horaire différent, nous devons simplement rejoindre via la table de pont de fuseau horaire et reliez les clés de date / heure locales aux tables de dimension de date et d'heure. Nous remplissons notre table de pont de fuseau horaire en utilisant du code C # invoqué depuis SSIS car cela était beaucoup moins compliqué que de faire des trucs TZ directement depuis SqlServer.


Je pense également que votre solution est probablement la plus logique sans entrer dans quelque chose de trop compliqué. Je teste mon DW en utilisant une table timeZone et TimeZoneBridge similaire au vôtre. Il possède également des tables TimeDimension et DateDimension. J'ai créé un index clusterisé sur date_key_local, time_key_local et timezone_id, afin que la traduction de l'heure locale en heure UTC à l'aide de TimeZoneBridge soit rapide.
dsum

1
Notre clé en cluster principale pour la table de pont est sur les colonnes date / heure utc + l'ID de fuseau horaire (si je me souviens bien), puisque toutes les clés de temps des tables de faits seront en utc, vous vous joindrez au pont via l'utc clés + tz id, il pourrait être préférable d'avoir l'index cluster sur ceux-ci. Faites ce qui convient à vos besoins. Je suis content que ma réponse ait aidé quelqu'un, je pense que c'est une bonne approche et d'après tous nos tests, elle est toujours raisonnablement rapide, soyez prudent quand il s'agit de la clause WHERE: filtrez les plages de dates que vous voulez dès que possible dans vos requêtes.
Matt Palmerlee

Est-ce que cela ne contient que des dates entières? Ou si vous avez 86000 valeurs de "clé de date / heure" dans votre table de faits, la table de pont aura 86000 lignes * n fuseaux horaires pris en charge, et c'est juste pour cela un jour?
Aaron Bertrand

1
vous pouvez peut-être ajouter la définition exacte du tableau que vous avez, afin que les lecteurs puissent voir les principales contraintes uniques.
ypercubeᵀᴹ

@AaronBertrand cela dépend du grain (ou de la granularité que vous choisissez) pour suivre vos données, dans notre cas, nous n'avions besoin que d'une granularité de 15 minutes dans nos tables de faits, c'est donc seulement 4 * 24 = 96 enregistrements par jour et par fuseau horaire que nous voulions prendre en charge, ce qui est tout à fait raisonnable.
Matt Palmerlee

2

J'ai vu l'idée d'un entrepôt utilisant une DateTimedimension combinée rejetée, mais je n'ai pas vu de raison vraiment claire pourquoi. Simplifiant légèrement, voici la table de faits que je construis en ce moment:

Transactions
(
...
CreatedDateTimeSK         INT NOT NULL,  -- Four bytes per date...
AuthorizedDateTimeSK      INT NOT NULL,
BatchSubmittedDateTimeSK  INT NOT NULL,
BatchApprovedDateTimeSK   INT NOT NULL,
SettlementDateTimeSK      INT NOT NULL,
LocalTimeZoneSK           TINYINT NOT NULL  -- ...plus one byte for the time zone
)

Les DateTimechamps se joignent à une table DateTime:

DateTimes
(
DateTimeSK   INT NOT NULL PRIMARY KEY,
SQLDate      DATE NOT NULL,
SQLDateTime  DATETIME2(0) NOT NULL,
Year         SMALLINT NOT NULL,
Month        TINYINT NOT NULL,
Day          TINYINT NOT NULL,
Hour         TINYINT NOT NULL,
Minute       TINYINT NOT NULL CHECK (Minute IN (0, 30)),
...
)

C'est à une résolution d'une demi-heure, donc il y a 48 enregistrements par jour, 350 400 en 20 ans - tout à fait gérable.

La date / l'heure de l'événement sont traduites en UTC lorsqu'elles sont stockées, mais avec le LocalTimeZoneSKchamp et une table de pont, nous pouvons facilement nous joindre pour obtenir l'heure locale:

TimeZoneBridge
(
DateTimeSK       INT NOT NULL,
TimeZoneSK       TINYINT NOT NULL,
PRIMARY KEY (DateTimeSK, TimeZoneSK),
LocalDateTimeSK  INT NOT NULL
)

Pour obtenir des transactions créées aujourd'hui, heure UTC:

SELECT COUNT(*)
FROM Transactions AS T
  INNER JOIN DateTimes AS CD ON T.CreatedDateTimeSK = CD.DateTimeSK
WHERE CD.SQLDate = '2014-08-22'

Pour obtenir les transactions créées aujourd'hui, à l'heure locale de la transaction:

SELECT COUNT(*)
FROM Transactions AS T
  INNER JOIN TimeZoneBridge AS TZB ON T.CreatedDateTimeSK = TZB.DateTimeSK AND T.TimeZoneSK = TZB.TimeZoneSK
  INNER JOIN DateTimes AS CD ON TZB.LocalDateTimeSK = CD.DateTimeSK
WHERE CD.SQLDate = '2014-08-22'

Vous pouvez être tenté de simplifier les choses en remplaçant le TimeZoneSKpar un REALdécalage (par exemple, -5,0 pour l'heure du centre des États-Unis), mais cela tombera en panne si certaines dates / heures pour un enregistrement de faits sont en heure d'été et d'autres non.

Si les événements d'un enregistrement de faits peuvent se produire dans différents fuseaux horaires, comme un envoi ou un vol, vous avez besoin d'un champ de fuseau horaire pour chaque date et vous avez jusqu'à cinq octets par date.


C'est une approche créative. Cependant, comme vous le dites, vous n'aurez que 350 400 lignes dans votre tableau de dim datetime combiné, si vous commencez à changer le grain en une résolution plus fine, vous entrerez rapidement dans les millions d'enregistrements. Si vous choisissez d'avoir une dimension de date distincte de la dimension de temps, vous n'avez que 48 lignes dans votre tableau de dimension de temps et seulement 365 lignes par an dans votre tableau de dimension de date (ou 7300 lignes en 20 ans). Votre table de faits a alors simplement une colonne pour date_key et time_key. Cela le rend également plus flexible si vous avez des tables de faits qui ne nécessitent qu'une granularité de date.
Matt Palmerlee

1
Un million de lignes dans une dimension ne me concerne pas - les données ne sont modifiées qu'une fois par décennie, et un index de couverture sur le PK et les deux ou trois champs les plus utilisés occuperont une quantité insignifiante de RAM de serveur. Cependant, ajouter une demi-douzaine de SMALLINTs à une table de faits d'un milliard de lignes représente 12 Go plus les frais généraux, et maintenant vous parlez d'argent réel. Pour les dates qui n'ont besoin que de stocker la date, vous pouvez bien sûr les pointer vers l'enregistrement "12:00 AM" pour la date appropriée.
Jon of All Trades,
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.