Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

AT TIME ZONE – nowa ulubiona funkcja w SQL Server 2016


Obraz © Mark Boyle | Australia Day Council of NSW.
Zdjęcia są własnością odpowiednich artystów. Wszelkie prawa zastrzeżone.

AT TIME ZONE to taka fajna funkcja i nie zauważyłem jej do niedawna, mimo że Microsoft ma stronę na ten temat od grudnia.

Mieszkam w Adelajdzie w Australii. I podobnie jak ponad miliard innych ludzi na świecie, mieszkańcy Adelaide muszą radzić sobie z przebywaniem w półgodzinnej strefie czasowej. Zimą jest UTC+9:30, a latem UTC+10:30. Z wyjątkiem tego, że jeśli czytasz to na półkuli północnej, musisz pamiętać, że przez „zimę” mam na myśli od kwietnia do października. Lato trwa od października do kwietnia, a Święty Mikołaj siedzi na plaży z zimnym napojem, pocąc się przez gruby czerwony garnitur i brodę. Chyba że ratuje życie, oczywiście.

W Australii mamy trzy główne strefy czasowe (zachodnią, środkową i wschodnią), ale latem ta strefa rozciąga się do pięciu, ponieważ trzy stany rozciągające się na północny kraniec Australii (WA, Qld i NT) nie staraj się oszczędzać światło dzienne. Są wystarczająco blisko równika, żeby się tym nie przejmować, czy coś w tym stylu. To świetna zabawa dla lotniska Gold Coast, którego pas startowy przecina granicę NSW-QLD.

Serwery baz danych często działają w UTC, ponieważ po prostu łatwiej jest nie zajmować się konwersją między UTC a lokalnym (i odwrotnie) w SQL Server. Wiele lat temu pamiętam, że musiałem naprawić raport, który wymieniał incydenty, które miały miejsce, wraz z czasami reakcji (od tego czasu piszę o tym na blogu). Pomiar SLA był dość prosty – widziałem, że jeden incydent miał miejsce w godzinach pracy klienta i że odpowiedział w ciągu godziny. Widziałem, że kolejny incydent miał miejsce poza godzinami pracy, a odpowiedź nastąpiła w ciągu dwóch godzin. Problem pojawił się, gdy raport został wygenerowany pod koniec okresu, w którym zmieniła się strefa czasowa, powodując, że zdarzenie, które faktycznie wydarzyło się o 17:30 (poza godzinami) zostało wymienione tak, jakby miało miejsce o 16:30 (w godzinach wewnętrznych) . Odpowiedź zajęła około 90 minut, co było w porządku, ale raport pokazywał inaczej.

Wszystko to zostało naprawione w SQL Server 2016.

Jak korzystać z AT TIME ZONE w SQL Server 2016

Teraz, używając AT TIME ZONE, zamiast mówić:„20160101 00:00 +10:30”, mogę zacząć od wartości daty i godziny, która nie ma przesunięcia strefy czasowej, i użyć AT TIME ZONE, aby wyjaśnić, że jest w Adelajdzie.

SELECT CONVERT(datetime,'20160101 00:00') 
    AT TIME ZONE 'Cen. Australia Standard Time';
 
-- 2016-01-01 00:00:00.000 +10:30

I można to przeliczyć na czas amerykański, dodając ponownie AT TIME ZONE.

SELECT CONVERT(datetime,'20160101 00:00') 
    AT TIME ZONE 'Cen. Australia Standard Time' 
    AT TIME ZONE 'US Eastern Standard Time';
 
-- 2015-12-31 08:30:00.000 -05:00

Teraz wiem, że jest to o wiele bardziej rozwlekłe. I muszę jawnie przekonwertować ciąg na datę i godzinę, aby uniknąć błędu mówiącego:

Typ danych argumentu varchar jest nieprawidłowy dla argumentu 1 funkcji AT TIME ZONE.

Ale pomimo tego, że jest to rozwlekłe, uwielbiam to, ponieważ w żadnym momencie nie musiałem domyślać się, że w Adelajdzie jest +10:30 lub że na Wschodzie jest -5:00 – po prostu musiałem znać strefę czasową z nazwy. Ustaliłem, czy czas letni powinien mieć zastosowanie, czy nie, i nie musiałem dokonywać żadnej konwersji z lokalnego na UTC, aby ustalić jakiś punkt odniesienia.

Działa przy użyciu rejestru systemu Windows, który zawiera wszystkie te informacje, ale niestety nie jest idealny, gdy patrzymy wstecz. Australia zmieniła daty w 2008 r., a Stany Zjednoczone zmieniły daty w 2005 r. – oba kraje oszczędzają światło dzienne przez większą część roku. AT TIME ZONE to rozumie. Ale wydaje się, że nie docenia tego, że w Australii w 2000 roku, dzięki Igrzyskom Olimpijskim w Sydney, Australia zaczęła oszczędzać czas około dwa miesiące wcześniej. To trochę frustrujące, ale to nie wina SQL – musimy winić za to Windows. Sądzę, że rejestr systemu Windows nie pamięta poprawki, która pojawiła się w tym roku. (Uwaga dla siebie:być może będę musiał poprosić kogoś z zespołu Windows, aby to naprawił…)

Przydatność jednak trwa nadal!

Ta nazwa strefy czasowej nie musi nawet być stałą. Mogę przekazywać zmienne, a nawet używać kolumn:

WITH PeopleAndTZs AS
(
  SELECT * FROM (VALUES 
    ('Rob',   'Cen. Australia Standard Time'),
    ('Paul',  'New Zealand Standard Time'),
    ('Aaron', 'US Eastern Standard Time')
  ) t (person, tz)
)
SELECT tz.person, SYSDATETIMEOFFSET() AT TIME ZONE tz.tz
  FROM PeopleAndTZs tz;
 
/*
  Rob      2016-07-18 18:29:11.9749952 +09:30
  Paul     2016-07-18 20:59:11.9749952 +12:00
  Aaron    2016-07-18 04:59:11.9749952 -04:00
*/

(Ponieważ prowadziłem to tuż przed 18:30 tutaj w Adelajdzie, czyli prawie 21:00 w Nowej Zelandii, gdzie jest Paul, i prawie 5 rano dziś rano we wschodniej części Ameryki, gdzie jest Aaron).

To pozwoliłoby mi łatwo zobaczyć, która jest godzina dla ludzi z dowolnego miejsca na świecie i zobaczyć, kto najlepiej zareaguje na jakiś problem, bez konieczności wykonywania ręcznych konwersji daty i godziny. A co więcej, pozwoliłoby mi to robić dla ludzi w przeszłości. Mógłbym mieć raport, który analizuje, które strefy czasowe pozwolą na wystąpienie największej liczby zdarzeń w godzinach pracy.

Te strefy czasowe są wymienione w sys.time_zone_info , wraz z bieżącym przesunięciem i informacją, czy jest obecnie stosowany czas letni.

nazwa

current_utc_offset

is_currently_dst
Singapur czas standardowy

+08:00

0
W. Australia czas standardowy

+08:00

0
Tajpej czas standardowy

+08:00

0
Czas standardowy Ułan Bator

+09:00

1
Czas standardowy Korei Północnej

+08:30

0
Aus Central W. Czas standardowy

+08:45

0
Przezbaikalski czas standardowy

+09:00

0
Tokio czas standardowy

+09:00

0

Próbkowanie wierszy z sys.time_zone_info

Interesuje mnie tylko nazwa, ale i tak. Interesujące jest to, że istnieje strefa czasowa o nazwie „Aus Central W. Standard Time”, która jest kwadransem. Domyśl. Warto również zauważyć, że miejsca są określane za pomocą nazwy czasu standardowego, nawet jeśli obecnie obserwują czas letni. Takich jak Ułan Bator na powyższej liście, który nie jest wymieniony jako czas letni w Ułan Bator. Może to zaskoczyć ludzi, którzy zaczną używać AT TIME ZONE.

Czy AT TIME ZONE może powodować problemy z wydajnością?

Teraz jestem pewien, że zastanawiasz się, jaki wpływ może mieć używanie AT TIME ZONE na indeksowanie.

Pod względem kształtu planu nie różni się to od ogólnego zajmowania się przesunięciem daty i czasu. Jeśli mam wartości daty i godziny, takie jak w kolumnie AdventureWorks Sales.SalesOrderHeader.OrderDate (na której utworzyłem indeks o nazwie rf_IXOD), to uruchamiam oba te zapytania:

select OrderDate, SalesOrderID
  from Sales.SalesOrderHeader
  where OrderDate >= convert(datetime,'20110601 00:00') at time zone 'US Eastern Standard Time' 
    and OrderDate <  convert(datetime,'20110701 00:00') at time zone 'US Eastern Standard Time' ;

I to zapytanie:

select OrderDate, SalesOrderID
  from Sales.SalesOrderHeader
  where OrderDate >= convert(datetimeoffset,'20110601 00:00 -04:00')
    and OrderDate <  convert(datetimeoffset,'20110701 00:00 -04:00');

W obu przypadkach otrzymujesz plany, które wyglądają tak:

Ale jeśli przyjrzymy się trochę bliżej, pojawia się problem.

Ten, który korzysta z AT TIME ZONE, niezbyt dobrze wykorzystuje statystyki. Sądzi, że zobaczy 5170 wierszy z wyszukiwania indeksu, podczas gdy w rzeczywistości jest tylko 217. Dlaczego 5170? Cóż, niedawny post Aarona „Paying Attention To Estimates” wyjaśnia to, odwołując się do posta Paula „Oszacowanie kardynalizacji dla wielu predykatów”. 5170 to 31 465 (wiersze w tabeli) * 0,3 * sqrt(0,3).

Drugie zapytanie daje odpowiedź, oszacowując 217. Widzisz, nie ma żadnych funkcji.

To chyba wystarczy. To znaczy – w momencie tworzenia planu nie zapyta rejestru o potrzebne informacje, więc tak naprawdę nie wie, ile oszacować. Ale może to stanowić problem.

Jeśli dodam dodatkowe predykaty, o których wiem, że nie mogą stanowić problemu, moje szacunki w rzeczywistości spadają jeszcze bardziej – do 89,9 wierszy.

select OrderDate, SalesOrderID
  from Sales.SalesOrderHeader
  where OrderDate >= convert(datetime,'20110601 00:00') at time zone 'US Eastern Standard Time' 
  and OrderDate   <  convert(datetime,'20110701 00:00') at time zone 'US Eastern Standard Time'
  and OrderDate   >= convert(datetimeoffset,'20110601 00:00 +14:00')
  and OrderDate   <  convert(datetimeoffset,'20110701 00:00 -12:00');

Oszacowanie zbyt dużej liczby wierszy oznacza przydzielenie zbyt dużej ilości pamięci, ale oszacowanie zbyt małej ilości może spowodować zbyt małą ilość pamięci, co potencjalnie może wymagać rozlania w celu rozwiązania problemu (co często może być katastrofalne z punktu widzenia wydajności). Przeczytaj post Aarona, aby uzyskać więcej informacji o tym, jak złe szacunki mogą być złe.

Kiedy zastanawiam się, jak radzić sobie z wyświetlaniem wartości dla tych osób z przeszłości, mogę użyć zapytań takich jak:

WITH PeopleAndTZs AS
(
  SELECT * FROM (VALUES 
    ('Rob',   'Cen. Australia Standard Time'),
    ('Paul',  'New Zealand Standard Time'),
    ('Aaron', 'US Eastern Standard Time')
  ) t (person, tz)
)
SELECT tz.person, o.SalesOrderID, o.OrderDate AT TIME ZONE 'UTC' AT TIME ZONE tz.tz
FROM PeopleAndTZs tz
CROSS JOIN Sales.SalesOrderHeader o
WHERE o.SalesOrderID BETWEEN 44001 AND 44010;

I zdobądź ten plan:

…co nie ma takich obaw – skalar obliczeniowy z prawej strony konwertuje datę i godzinę OrderDate na przesunięcie daty i godziny dla czasu UTC, a skalar z lewej strony konwertuje ją na odpowiednią strefę czasową dla danej osoby. Ostrzeżenie jest takie, że robię CROSS JOIN i było to w pełni zamierzone.

Zalety i wady innych metod konwersji czasu

Przed AT TIME ZONE jedną z moich ulubionych, ale często niedocenianych funkcji SQL 2008 był typ danych datetimeoffset. Dzięki temu dane daty/godziny mogą być również przechowywane ze strefą czasową, na przykład „20160101 00:00 +10:30”, kiedy w tym roku świętowaliśmy Nowy Rok w Adelajdzie. Aby zobaczyć, kiedy to było we wschodnich stanach USA, mogę użyć funkcji PRZEŁĄCZ PRZESUNIĘCIE.

SELECT SWITCHOFFSET('20160101 00:00 +10:30', '-05:00');
 
-- 2015-12-31 08:30:00.0000000 -05:00

To ten sam moment w czasie, ale w innej części świata. Gdybym rozmawiał przez telefon z kimś w Północnej Karolinie lub Nowym Jorku, życząc mu szczęśliwego Nowego Roku, ponieważ w Adelajdzie było tuż po północy, powiedziałby:„Co masz na myśli? W Sylwestra wciąż jest czas na śniadanie!”

Problem w tym, że aby to zrobić, muszę wiedzieć, że w styczniu Adelaide jest +10:30, a US Eastern jest –5:00. A to często jest uciążliwe. Zwłaszcza jeśli pytam o koniec marca, początek kwietnia, październik, początek listopada – te pory roku, kiedy ludzie nie mogą być pewni, w której strefie czasowej przebywali ludzie w innych krajach, bo zmieniają się o godzinę na czas letni, a oni wszyscy robią to według innych zasad. Mój komputer mówi mi, w jakiej strefie czasowej przebywają teraz ludzie, ale o wiele trudniej jest określić, w jakiej strefie czasowej będą przebywać o innych porach roku.

Aby uzyskać więcej informacji na temat konwersji między strefami czasowymi i tego, jak ludzie tacy jak Aaron radzili sobie z czasem letnim, zobacz następujące linki:

  • Korzystanie z AT TIME ZONE do naprawy starego raportu (to ja!)
  • Obsługa konwersji między strefami czasowymi w SQL Server – część 1
  • Obsługa konwersji między strefami czasowymi w SQL Server – część 2
  • Obsługa konwersji między strefami czasowymi w SQL Server – część 3

I trochę oficjalnej dokumentacji Microsoft:

  • W STREFIE CZASOWEJ (Transact-SQL)
  • sys.time_zone_info (języka Transact-SQL)
  • Pomoc i wsparcie dotyczące czasu letniego

Czy należy używać AT TIME STREFA?

AT TIME ZONE nie jest idealny. Ale to jest naprawdę przydatne – niewiarygodnie. Jest na tyle elastyczny, że akceptuje kolumny i zmienne jako dane wejściowe i widzę w tym ogromny potencjał. Ale jeśli to spowoduje, że moje szacunki zostaną ujawnione, będę musiał być ostrożny. Do celów wyświetlania nie powinno to jednak mieć znaczenia i właśnie tam widzę, że jest najbardziej przydatne. Ułatwienie konwersji wyświetlanej wartości do lub z czasu UTC (lub do lub z czasu lokalnego), bez konieczności zawracania sobie głowy przesunięciami i czasem letnim, to wielka wygrana.

To naprawdę jedna z moich ulubionych funkcji SQL Server 2016. Od bardzo dawna błagam o coś takiego.

Aha, a większość z tych miliardów ludzi w półgodzinnej strefie czasowej jest w Indiach. Ale prawdopodobnie już wiedziałeś, że…


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Wybór narzędzia do monitorowania serwera SQL dopasowanego do Twoich potrzeb

  2. Jak skopiować bazę danych SQL Azure na mój lokalny serwer programistyczny?

  3. Nie można zmniejszyć bazy danych „tylko do odczytu” | Zmniejsz dziennik transakcji podczas korzystania z grupy dostępności AlwaysOn

  4. Procedura składowana lub funkcja oczekuje parametru, który nie jest dostarczany

  5. Jak wygenerować skrypty upuszczania unikalnych ograniczeń w bazie danych SQL Server — samouczek SQL Server / TSQL część 99