Wir versuchen, ein Data Warehouse-Design zu optimieren, das die Berichterstellung für Daten für viele Zeitzonen unterstützt. Beispielsweise haben wir möglicherweise einen Bericht über die Aktivität eines Monats (Millionen von Zeilen), in dem die Aktivität nach Tagesstunden gruppiert angezeigt werden muss. Und natürlich muss diese Stunde des Tages die "lokale" Stunde für die gegebene Zeitzone sein.
Wir hatten ein Design, das gut funktionierte, als wir nur UTC und eine Ortszeit unterstützten. Das Standarddesign der Datums- und Zeitdimensionen für UTC und Ortszeit finden Sie in den Faktentabellen. Dieser Ansatz scheint jedoch nicht skalierbar zu sein, wenn wir die Berichterstellung für mehr als 100 Zeitzonen unterstützen müssen.
Unsere Faktentabellen würden sehr breit werden. Außerdem müssten wir das Syntaxproblem in SQL lösen, indem wir angeben, welche Datums- und Uhrzeit-IDs für die Gruppierung in einem bestimmten Lauf des Berichts verwendet werden sollen. Vielleicht eine sehr große CASE-Anweisung?
Ich habe einige Vorschläge gesehen, um alle Daten nach dem von Ihnen abgedeckten UTC-Zeitbereich abzurufen und sie dann an die Präsentationsschicht zurückzugeben, um sie dort in lokale und aggregierte Daten zu konvertieren. Eingeschränkte Tests mit SSRS deuten jedoch darauf hin, dass dies extrem langsam sein wird.
Ich habe auch einige Bücher zu diesem Thema konsultiert, und alle scheinen zu sagen, dass nur UTC und Konvertierung angezeigt werden oder dass UTC und ein Lokal vorhanden sind. Würde mich über Gedanken und Vorschläge freuen.
Hinweis: Diese Frage ähnelt der Behandlung von Zeitzonen in Data Mart / Warehouse , aber ich kann diese Frage nicht kommentieren, daher war dies eine eigene Frage verdient.
Update: Ich habe Aarons Antwort ausgewählt, nachdem er einige wichtige Updates vorgenommen und Beispielcode und Diagramme veröffentlicht hatte. Meine früheren Kommentare zu seiner Antwort machen keinen Sinn mehr, da sie sich auf die ursprüngliche Bearbeitung der Antwort beziehen. Ich werde versuchen, dies erneut zu aktualisieren, wenn dies gerechtfertigt ist
Antworten:
Ich habe dieses Problem durch eine sehr einfache Kalendertabelle gelöst - jedes Jahr gibt es eine Zeile pro unterstützter Zeitzone mit dem Standardversatz und der Start- / Endzeit / Endzeit der Sommerzeit und deren Versatz (sofern diese Zeitzone dies unterstützt). Dann eine Inline-Funktion mit Schema-Bindung und Tabellenwert, die die Quellzeit (natürlich in UTC) benötigt und den Offset addiert / subtrahiert.
Dies wird offensichtlich nie besonders gut funktionieren, wenn Sie über einen großen Teil der Daten berichten. Die Partitionierung scheint zu helfen, aber es gibt immer noch Fälle, in denen die letzten Stunden in einem Jahr oder die ersten Stunden im nächsten Jahr tatsächlich zu einem anderen Jahr gehören, wenn sie in eine bestimmte Zeitzone konvertiert werden - sodass Sie niemals eine echte Partition erhalten können Isolation, außer wenn Ihr Berichtsbereich den 31. Dezember oder den 1. Januar nicht umfasst.
Es gibt ein paar seltsame Randfälle, die Sie berücksichtigen müssen:
2014-11-02 05:30 UTC und 2014-11-02 06:30 UTC konvertieren beide zum Beispiel in der östlichen Zeitzone auf 01:30 Uhr (eine zum ersten Mal wurde 01:30 lokal getroffen und dann eine zum zweiten Mal, als die Uhren von 2:00 Uhr auf 1:00 Uhr zurückgingen und eine weitere halbe Stunde verstrichen war). Sie müssen also entscheiden, wie mit dieser Stunde der Berichterstellung umgegangen werden soll. Laut UTC sollten Sie den doppelten Datenverkehr oder das doppelte Volumen von allem, was Sie messen, sehen, sobald diese zwei Stunden einer einzelnen Stunde in einer Zeitzone zugeordnet sind, in der die Sommerzeit eingehalten wird. Dies kann auch lustige Spiele mit Sequenzierung von Ereignissen spielen, da etwas, das logischerweise passieren musste, nachdem etwas anderes erscheinen konntebevor es passiert, sobald das Timing auf eine Stunde anstatt auf zwei eingestellt ist. Ein extremes Beispiel ist eine Seitenansicht, die um 05:59 UTC erfolgte, und ein Klick, der um 06:00 UTC erfolgte. In der UTC-Zeit passierten diese im Abstand von einer Minute, aber bei der Umrechnung in die östliche Zeit erfolgte die Ansicht um 01:59 Uhr, und das Klicken erfolgte eine Stunde zuvor.
2014-03-09 02:30 kommt in den USA nie vor. Dies liegt daran, dass wir um 2:00 Uhr die Uhr auf 3:00 Uhr vorwärts rollen. Daher möchten Sie wahrscheinlich einen Fehler auslösen, wenn der Benutzer eine solche Zeit eingibt und Sie auffordert, diese in UTC zu konvertieren, oder Ihr Formular so zu gestalten, dass Benutzer eine solche Zeit nicht auswählen können.
Selbst unter Berücksichtigung dieser Randfälle denke ich immer noch, dass Sie den richtigen Ansatz haben: Speichern Sie die Daten in UTC. Es ist viel einfacher, Daten von UTC auf andere Zeitzonen abzubilden als von einer Zeitzone auf eine andere Zeitzone, insbesondere wenn verschiedene Zeitzonen die Sommerzeit an verschiedenen Daten beginnen / beenden und sogar dieselbe Zeitzone in verschiedenen Jahren nach unterschiedlichen Regeln wechseln kann ( Zum Beispiel haben die USA die Regeln vor ungefähr 6 Jahren geändert.
Sie werden für all dies eine Kalendertabelle verwenden wollen, keinen gigantischen
CASE
Ausdruck (keine Aussage ). Ich habe gerade eine dreiteilige Serie für MSSQLTips.com darüber geschrieben. Ich denke, der 3. Teil wird für Sie am nützlichsten sein:http://www.mssqltips.com/sqlservertip/3173/handle-conversion-between-time-zones-in-sql-server--part-1/
http://www.mssqltips.com/sqlservertip/3174/handle-conversion-between-time-zones-in-sql-server--part-2/
http://www.mssqltips.com/sqlservertip/3175/handle-conversion-between-time-zones-in-sql-server--part-3/
In der Zwischenzeit ein echtes Live-Beispiel
Angenommen, Sie haben eine sehr einfache Faktentabelle. Die einzige Tatsache, die mir in diesem Fall wichtig ist, ist die Ereigniszeit, aber ich werde eine bedeutungslose GUID hinzufügen, um die Tabelle breit genug zu machen, um mich darum zu kümmern. Um genau zu sein, speichert die Faktentabelle Ereignisse nur in UTC-Zeit und UTC-Zeit. Ich habe die Spalte sogar mit einem Suffix versehen,
_UTC
damit keine Verwirrung entsteht.Laden wir nun unsere Faktentabelle mit 10.000.000 Zeilen, die alle 3 Sekunden (1.200 Zeilen pro Stunde) vom 30.12.2013 um Mitternacht UTC bis kurz nach 5 Uhr UTC am 12.12.2014 darstellen. Dies stellt sicher, dass die Daten eine Jahresgrenze überschreiten sowie die Sommerzeit für mehrere Zeitzonen vorwärts und rückwärts. Das sieht wirklich beängstigend aus, hat aber auf meinem System ~ 9 Sekunden gedauert. Die Tabelle sollte ungefähr 325 MB groß sein.
Und nur um zu zeigen, wie eine typische Suchabfrage für diese 10-MM-Zeilentabelle aussehen wird, wenn ich diese Abfrage ausführe:
Ich erhalte diesen Plan und er kehrt in 25 Millisekunden * mit 358 Lesevorgängen zurück, um 72 Stundensummen zurückzugeben:
* Dauer gemessen mit unserem kostenlosen SQL Sentry Plan Explorer , der die Ergebnisse verwirft, sodass die Netzwerkübertragungszeit der Daten, das Rendern usw. nicht enthalten sind. Als zusätzlichen Haftungsausschluss arbeite ich für SQL Sentry.
Es dauert natürlich etwas länger, wenn ich meine Reichweite zu groß mache - ein Monat Daten dauert 258 ms, zwei Monate mehr als 500 ms und so weiter. Parallelität kann eintreten:
Hier fangen Sie an, über andere, bessere Lösungen nachzudenken, um Berichtsanfragen zu beantworten, und es hat nichts damit zu tun, in welcher Zeitzone Ihre Ausgabe angezeigt wird. Ich werde nicht darauf eingehen, ich möchte nur zeigen, dass die Zeitzonenkonvertierung Ihre Berichtsabfragen nicht wirklich viel mehr zum Kotzen bringt, und sie können bereits zum Kotzen werden, wenn Sie große Bereiche erhalten, die nicht von den richtigen unterstützt werden Indizes. Ich werde mich an kleine Datumsbereiche halten, um zu zeigen, dass die Logik korrekt ist, und Sie müssen sich darum kümmern, dass Ihre bereichsbezogenen Berichtsabfragen mit oder ohne Zeitzonenkonvertierungen eine angemessene Leistung erbringen.
Okay, jetzt brauchen wir Tabellen zum Speichern unserer Zeitzonen (mit Offsets in Minuten, da nicht jeder sogar Stunden von UTC entfernt ist) und DST-Änderungsdaten für jedes unterstützte Jahr. Der Einfachheit halber werde ich nur einige Zeitzonen und ein einziges Jahr eingeben, um den obigen Daten zu entsprechen.
Enthält einige Zeitzonen für Abwechslung, einige mit halbstündigen Offsets, andere ohne Sommerzeit. Beachten Sie, dass Australien, in der südlichen Hemisphäre beobachtet DST während unseres Winters, so dass ihre Uhren gehen zurück im April und vorwärts im Oktober. (In der obigen Tabelle werden die Namen umgedreht, aber ich bin mir nicht sicher, wie ich dies für Zeitzonen der südlichen Hemisphäre weniger verwirrend machen soll.)
Nun eine Kalendertabelle, um zu wissen, wann sich TZs ändern. Ich werde nur interessierende Zeilen einfügen (jede Zeitzone oben und nur Sommerzeitänderungen für 2014). Zur Vereinfachung der Berechnungen speichere ich sowohl den Moment in UTC, in dem sich eine Zeitzone ändert, als auch den gleichen Moment in der Ortszeit. Für Zeitzonen, in denen die Sommerzeit nicht eingehalten wird, ist sie das ganze Jahr über Standard, und die Sommerzeit "beginnt" am 1. Januar.
Sie können dies definitiv mit Algorithmen füllen (und die kommende Tipp-Serie verwendet einige clevere satzbasierte Techniken, wenn ich es selbst sage), anstatt manuell zu schleifen, was Sie haben. Für diese Antwort habe ich beschlossen, nur ein Jahr für die fünf Zeitzonen manuell auszufüllen, und ich werde mich nicht um ausgefallene Tricks kümmern.
Okay, wir haben also unsere Faktendaten und unsere "Dimension" -Tabellen (ich erschrecke, wenn ich das sage). Was ist also die Logik? Nun, ich gehe davon aus, dass Benutzer ihre Zeitzone auswählen und den Datumsbereich für die Abfrage eingeben müssen. Ich gehe auch davon aus, dass der Datumsbereich volle Tage in ihrer eigenen Zeitzone sein wird. Keine Teiltage, egal Teilstunden. Sie übergeben also ein Startdatum, ein Enddatum und eine TimeZoneID. Von dort aus werden wir eine Skalarfunktion verwenden, um das Start- / Enddatum von dieser Zeitzone in UTC umzuwandeln, wodurch wir die Daten basierend auf dem UTC-Bereich filtern können. Sobald wir dies getan und unsere Aggregationen daran durchgeführt haben, können wir die Konvertierung der gruppierten Zeiten zurück in die Quellzeitzone anwenden, bevor sie dem Benutzer angezeigt werden.
Die skalare UDF:
Und die tabellenwertige Funktion:
Und eine Prozedur, die es verwendet ( bearbeiten : aktualisiert, um die 30-Minuten-Offset-Gruppierung zu handhaben):
(Möglicherweise möchten Sie dort einen Kurzschluss oder eine separate gespeicherte Prozedur ausprobieren, falls der Benutzer in UTC Bericht erstatten möchte. Die Übersetzung von und nach UTC ist offensichtlich eine verschwenderische Arbeit.)
Beispielanruf:
Gibt in 41 ms * zurück und generiert diesen Plan:
* Wieder mit verworfenen Ergebnissen.
Für 2 Monate kehrt es in 507 ms zurück, und der Plan ist bis auf die Anzahl der Zeilen identisch:
Obwohl dies etwas komplexer ist und die Laufzeit ein wenig erhöht, bin ich ziemlich zuversichtlich, dass diese Art von Ansatz viel, viel besser funktionieren wird als der Bridge-Table-Ansatz. Und dies ist ein Beispiel für eine dba.se-Antwort. Ich bin sicher, meine Logik und Effizienz könnten von Leuten verbessert werden, die viel schlauer sind als ich.
Sie können die Daten lesen, um die Randfälle zu sehen, über die ich spreche - keine Ausgabezeile für die Stunde, in der die Uhren vorwärts rollen, zwei Zeilen für die Stunde, in der sie zurückgerollt sind (und diese Stunde zweimal passiert ist). Sie können auch mit schlechten Werten spielen. Wenn Sie beispielsweise 20140309 02:30 Eastern Time vergehen, wird es nicht so gut funktionieren.
Ich habe möglicherweise nicht alle Annahmen richtig darüber, wie Ihre Berichterstattung funktionieren wird, daher müssen Sie möglicherweise einige Anpassungen vornehmen. Aber ich denke, das deckt die Grundlagen ab.
quelle
Können Sie die Transformation in einem gespeicherten Prozess oder einer parametrisierten Ansicht anstelle einer Präsentationsebene durchführen? Eine andere Möglichkeit besteht darin, einen Würfel zu erstellen und die Berechnungen im Würfel zu haben.
Erklärung aus den Kommentaren:
quelle