Bild © Mark Boyle | Australia Day Council of NSW.
Bilder tillhör respektive artist(er). Alla rättigheter reserverade.
AT TIME ZONE är en så cool funktion och jag hade inte märkt det förrän nyligen, även om Microsoft har haft en sida om det sedan december.
Jag bor i Adelaide i Australien. Och som över en miljard andra människor i världen, måste Adelaide människor klara av att vara i en halvtimmes tidszon. På vintertid är vi UTC+9:30 och på sommartid är vi UTC+10:30. Förutom att om du läser det här på norra halvklotet måste du komma ihåg att med "vinter" menar jag april till oktober. Sommartid är oktober till april, och jultomten sitter på stranden med en kall drink och svettas genom sin tjocka röda kostym och skägg. Såvida han inte är ute och räddar liv, förstås.
Inom Australien har vi tre huvudsakliga tidszoner (västra, centrala och östra), men detta sträcker sig till fem på sommaren, eftersom de tre staterna som sträcker sig till norra änden av Australien (WA, Qld och NT) inte gör det. försök att spara dagsljus. De är tillräckligt nära ekvatorn för att inte bry sig, eller något liknande. Det är mycket roligt för Gold Coast-flygplatsen, vars landningsbana korsar gränsen mellan NSW och QLD.
Databasservrar körs ofta i UTC, eftersom det helt enkelt är lättare att slippa konvertera mellan UTC och lokal (och tvärtom) i SQL Server. För många år sedan minns jag att jag behövde fixa en rapport som listade incidenter som inträffade tillsammans med svarstider (jag har bloggat om detta sedan dess). Att mäta SLA var ganska enkelt – jag kunde se att en incident inträffade under kundens arbetstid och att de svarade inom en timme. Jag kunde se att ytterligare en incident inträffade utanför arbetstid, och svaret var inom två timmar. Problemet kom när en rapport producerades i slutet av en period då tidszonen ändrades, vilket gjorde att en incident som faktiskt inträffade klockan 17:30 (utanför timmar) listades som om den hade inträffat klockan 16:30 (inom timmarna) . Svaret hade tagit cirka 90 minuter, vilket var okej, men rapporten visade något annat.
Allt detta är fixat i SQL Server 2016.
Hur man använder AT TIME ZONE i SQL Server 2016
Nu, med AT TIME ZONE, istället för att säga:'20160101 00:00 +10:30', kan jag börja med ett datetime-värde som inte har en tidszonförskjutning, och använda AT TIME ZONE för att förklara att det är i Adelaide.
VÄLJ KONVERTERA(datumtid,'20160101 00:00') VID TIDZON 'Cen. Australien standardtid'; -- 2016-01-01 00:00:00.000 +10:30
Och detta kan konverteras till amerikansk tid genom att lägga till AT TIME ZONE igen.
VÄLJ KONVERTERA(datumtid,'20160101 00:00') VID TIDZON 'Cen. Australien Standard Time' PÅ TIDZONEN 'US Eastern Standard Time'; -- 2015-12-31 08:30:00.000 -05:00
Nu vet jag att det här är mycket mer långrandigt. Och jag måste uttryckligen konvertera strängen till datetime, för att undvika ett fel som säger:
Argumentdatatypen varchar är ogiltig för argument 1 i AT TIME ZONE-funktionen.Men trots det långa i det, älskar jag det, för jag behövde inte vid något tillfälle komma på att Adelaide var +10:30, eller att Eastern var -5:00 – jag behövde helt enkelt veta tidszonen efter namn. Att ta reda på om sommartid skulle gälla eller inte hanterades för mig, och jag behövde inte göra någon konvertering från lokal till UTC för att fastställa någon baslinje.
Det fungerar genom att använda Windows-registret, som har all information i sig, men tyvärr är det inte perfekt när man ser tillbaka i tiden. Australien ändrade datumen 2008 och USA ändrade datumen 2005 – båda länderna sparar dagsljus under mer av året. AT TIME ZONE förstår detta. Men det verkar inte uppskatta att i Australien år 2000, tack vare OS i Sydney, började Australien sommartid ungefär två månader tidigare. Det här är lite frustrerande, men det är inte SQLs fel – vi måste skylla på Windows för det. Jag antar att Windows-registret inte kommer ihåg snabbkorrigeringen som gick runt det året. (Note to self:Jag kan behöva be någon i Windows-teamet att fixa det...)
Användbarheten fortsätter dock!
Det tidszonnamnet behöver inte ens vara konstant. Jag kan skicka variabler i och till och med använda kolumner:
WITH PeopleAndTZs AS( SELECT * FROM (VÄRDEN ('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:19507*04:59:19507*
(Eftersom jag körde det strax före 18.30 här i Adelaide, vilket råkar vara nästan 21.00 i Nya Zeeland där Paul är, och nästan 05.00 i morse i den östra delen av Amerika där Aaron är.)
Detta skulle låta mig enkelt se vad klockan är för människor var de än är i världen, och för att se vem som skulle vara bäst att svara på något problem, utan att behöva utföra några manuella datum- och tidkonverteringar. Och ännu mer, det skulle låta mig göra det för människor i det förflutna. Jag skulle kunna ha en rapport som analyserar vilka tidszoner som skulle tillåta att det största antalet händelser inträffar under kontorstid.
Dessa tidszoner listas i sys.time_zone_info
, tillsammans med vad den aktuella offseten är och om sommartid för närvarande tillämpas.
namn | current_utc_offset | is_currently_dst |
---|---|---|
Singapore standardtid | +08:00 | 0 |
W. Australiens standardtid | +08:00 | 0 |
Taipei standardtid | +08:00 | 0 |
Ulaanbaatar standardtid | +09:00 | 1 |
Nordkorea standardtid | +08:30 | 0 |
Aus Central W. Standard Time | +08:45 | 0 |
Transbaikal standardtid | +09:00 | 0 |
Tokyo standardtid | +09:00 | 0 |
Sampling av rader från sys.time_zone_info
Jag är egentligen bara intresserad av vad namnet är, men ändå. Och det är intressant att se att det finns en tidszon som heter "Aus Central W. Standard Time" som är på kvart. Gissa. Det är också värt att notera att platser hänvisas till med deras standardtidsnamn, även om de för närvarande observerar sommartid. Som Ulaanbaatar i listan ovan, som inte är listad som Ulaanbaatar Daylight Time. Detta kan få folk att bli en slinga när de börjar använda AT TIME ZONE.
Kan AT TIME ZONE orsaka prestandaproblem?
Nu är jag säker på att du undrar vad effekten av att använda AT TIME ZONE kan ha på indexering.
När det gäller formen på planen är det inte annorlunda än att hantera datumtidsförskjutning i allmänhet. Om jag har datetime-värden, t.ex. i AdventureWorks-kolumnen Sales.SalesOrderHeader.OrderDate (då jag skapade ett index som heter rf_IXOD), kör jag båda denna fråga:
välj OrderDate, SalesOrderID från Sales.SalesOrderHeader där OrderDate>=convert(datetime,'20110601 00:00') vid tidszonen 'US Eastern Standard Time' och OrderDateOch den här frågan:
välj OrderDate, SalesOrderID från Sales.SalesOrderHeader där OrderDate>=convert(datetimeoffset,'20110601 00:00 -04:00') och OrderDateI båda fallen får du planer som ser ut så här:
Men om vi utforskar lite närmare finns det ett problem.
Den som använder AT TIME ZONE använder inte statistiken särskilt bra. Den tror att den kommer att se 5 170 rader komma ut ur den där Indexsökningen, när det faktiskt bara finns 217. Varför 5 170? Tja, Aarons senaste inlägg, "Paying Attention To Estimates", förklarar det genom att hänvisa till inlägget "Cardinality Estimation for Multiple Predicates" från Paul. 5 170 är 31 465 (rader i tabellen) * 0,3 * sqrt(0,3).
Den andra frågan får det rätt och uppskattar 217. Inga funktioner inblandade, förstår du.
Detta är förmodligen rättvist nog. Jag menar - vid den tidpunkt då den producerar planen kommer den inte att ha bett registret om den information den behöver, så den vet verkligen inte hur många som ska uppskattas. Men det finns potential att det blir ett problem.
Om jag lägger till extra predikat som jag vet inte kan vara ett problem, sjunker faktiskt mina uppskattningar ännu mer – ner till 89,9 rader.
välj OrderDate, SalesOrderID från Sales.SalesOrderHeader där OrderDate>=convert(datetime,'20110601 00:00') vid tidszonen 'US Eastern Standard Time' och OrderDate=convert(datetimeoffset,'20110601 00:00 +14:00') and OrderDate Att uppskatta för många rader innebär att för mycket minne tilldelas, men att uppskatta för få kan orsaka för lite minne, vilket kan behöva spilla för att rätta till problemet (vilket ofta kan vara katastrofalt ur ett prestationsperspektiv). Läs Aarons inlägg för mer information om hur dåliga uppskattningar kan vara dåliga.
När jag funderar på hur jag ska hantera att visa värden för dessa personer från tidigare, kan jag använda frågor som denna:
WITH PeopleAndTZs AS( SELECT * FROM (VÄRDEN ('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.tzFROM PeopleAndTZs tzCROSS JOIN Sales.SalesOrderHeader oWHERE o.SalesOrderID MELLAN 440010;44010; före>Och skaffa den här planen:
…som inte har några sådana bekymmer – Compute Scalar längst till höger konverterar datetime OrderDate till datetimeoffset för UTC, och Compute Scalar längst till vänster konverterar den till lämplig tidszon för personen. Varningen beror på att jag gör en CROSS JOIN, och det var helt avsiktligt.
Fördelar och nackdelar med andra tidsomvandlingsmetoder
Innan AT TIME ZONE var en av mina favorit- men ofta ouppskattade funktioner i SQL 2008 datatypen datetimeoffset. Detta gör att datum/tidsdata kan lagras med tidszonen också, såsom '20160101 00:00 +10:30', vilket är när vi firade nyår i Adelaide i år. För att se när det var i US Eastern kan jag använda funktionen SWITCHOFFSET.
SELECT SWITCHOFFSET('20160101 00:00 +10:30', '-05:00'); -- 2015-12-31 08:30:00.0000000 -05:00Detta är samma ögonblick i tiden, men i en annan del av världen. Om jag var i telefon med någon i North Carolina eller New York och önskade dem ett gott nytt år eftersom det var precis över midnatt i Adelaide, skulle de säga "Vad menar du? Det är fortfarande frukost här på nyårsafton!”
Problemet är att för att göra detta måste jag veta att i januari är Adelaide +10:30 och US Eastern är -5:00. Och det är ofta jobbigt. Speciellt om jag frågar om slutet av mars, början av april, oktober, början av november – de tider på året då folk inte kan vara säkra på vilken tidszon människor i andra länder befann sig i eftersom de ändras med en timme för sommartid, och de alla gör det enligt olika regler. Min dator talar om för mig vilken tidszon människor befinner sig i nu, men det är mycket svårare att avgöra vilken tidszon de kommer att befinna sig i under andra tider på året.
För lite annan information om konvertering mellan tidszoner och hur folk som Aaron har hanterat sommartid, se följande länkar:
- Använder AT TIME ZONE för att fixa en gammal rapport (det är jag!)
- Hantera konvertering mellan tidszoner i SQL Server – del 1
- Hantera konvertering mellan tidszoner i SQL Server – del 2
- Hantera konvertering mellan tidszoner i SQL Server – del 3
Och lite officiell Microsoft-dokumentation:
- VID TIDSZON (Transact-SQL)
- sys.time_zone_info (Transact-SQL)
- Hjälp och support sommartid
Ska du använda AT TIME ZONE?
AT TIME ZONE är inte perfekt. Men det är verkligen användbart – otroligt så. Det är tillräckligt flexibelt för att acceptera kolumner och variabler som input, och jag kan se en enorm potential för det. Men om det kommer att få mina uppskattningar att vara ute, då måste jag vara försiktig. För visningsändamål bör detta dock inte spela någon roll, och det är där jag kan se att det är mest användbart. Att göra det enklare att konvertera ett visningsvärde till eller från UTC (eller till eller från en lokal tid), utan att någon behöver tänka på offset och sommartid, är en stor vinst.
Det här är verkligen en av mina favoritfunktioner i SQL Server 2016. Jag har längtat efter något sånt här väldigt länge.
Åh, och de flesta av dessa miljarder människor i en halvtimmes tidszon är i Indien. Men det visste du förmodligen redan...