sql >> Database teknologi >  >> RDS >> Sqlserver

AT TIME ZONE – en ny favoritfunktion i SQL Server 2016


Billede © Mark Boyle | Australia Day Council of NSW.
Billeder ejendom tilhørende respektive kunstner(e). Alle rettigheder forbeholdes.

AT TIME ZONE er sådan en fed funktion, og jeg havde ikke lagt mærke til det før for nylig, selvom Microsoft har haft en side om det siden december.

Jeg bor i Adelaide i Australien. Og ligesom over en milliard andre mennesker i verden, må Adelaide-folk klare sig i en halv times tidszone. Om vinteren er vi UTC+9:30, og om sommeren er vi UTC+10:30. Bortset fra, at hvis du læser dette på den nordlige halvkugle, skal du huske, at med 'vinter' mener jeg april til oktober. Sommertid er oktober til april, og julemanden sidder på stranden med en kold drink og sveder gennem sit tykke røde jakkesæt og skæg. Medmindre han er ude og redde liv, selvfølgelig.

Inden for Australien har vi tre hovedtidszoner (vestlige, centrale og østlige), men dette strækker sig til fem om sommeren, da de tre stater, der strækker sig til den nordlige ende af Australien (WA, Qld og NT), ikke gør det. prøv at spare dagslys. De er tæt nok på ækvator til at være ligeglade, eller sådan noget. Det er masser af sjov for Gold Coast-lufthavnen, hvis landingsbane krydser NSW-QLD-grænsen.

Databaseservere kører ofte i UTC, fordi det simpelthen er nemmere at slippe for at skulle håndtere konvertering mellem UTC og lokal (og omvendt) i SQL Server. For mange år siden husker jeg, at jeg skulle rette en rapport, som oplistede hændelser, der opstod sammen med svartider (jeg har blogget om dette siden). Måling af SLA var ret ligetil – jeg kunne se, at én hændelse skete i kundens arbejdstid, og at de reagerede inden for en time. Jeg kunne se, at endnu en hændelse skete uden for arbejdstiden, og svaret var inden for to timer. Problemet opstod, da der blev udarbejdet en rapport i slutningen af ​​en periode, hvor tidszonen ændrede sig, hvilket medførte, at en hændelse, der faktisk skete kl. 17.30 (uden for åbningstiden), blev opført, som om den var sket kl. 16.30 (inden for timer). . Svaret havde taget omkring 90 minutter, hvilket var okay, men rapporten viste noget andet.

Alt dette er rettet i SQL Server 2016.

Sådan bruges AT TIME ZONE i SQL Server 2016

Nu, med AT TIME ZONE, i stedet for at sige:'20160101 00:00 +10:30', kan jeg starte med en datetime-værdi, som ikke har en tidszoneforskydning, og bruge AT TIME ZONE til at forklare, at den er i Adelaide.

VÆLG KONVERTER(datoklokkeslæt,'20160101 00:00') PÅ TIDSZONEN 'Cen. Australien standardtid'; -- 2016-01-01 00:00:00.000 +10:30

Og dette kan konverteres til amerikansk tid ved at tilføje AT TIME ZONE igen.

VÆLG KONVERTER(datoklokkeslæt,'20160101 00:00') PÅ TIDSZONEN 'Cen. Australien Standard Time' PÅ TIDSZONEN 'US Eastern Standard Time'; -- 2015-12-31 08:30:00.000 -05:00

Nu ved jeg, at det her er meget mere langhåret. Og jeg skal eksplicit konvertere strengen til datetime for at undgå en fejl, der siger:

Argumentdatatypen varchar er ugyldig for argument 1 i AT TIME ZONE-funktionen.

Men på trods af det langvarige i det, elsker jeg det, for på intet tidspunkt behøvede jeg at finde ud af, at Adelaide var i +10:30, eller at Eastern var -5:00 – jeg havde simpelthen brug for at kende tidszonen ved navn. At finde ud af, om sommertid skulle gælde eller ej, blev håndteret for mig, og jeg behøvede ikke at foretage nogen konvertering fra lokal til UTC for at etablere en baseline.

Det fungerer ved at bruge Windows-registreringsdatabasen, som har alle de oplysninger i sig, men desværre er det ikke perfekt, når man ser tilbage i tiden. Australien ændrede datoerne i 2008, og USA ændrede datoerne i 2005 – begge lande sparer dagslys mere af året. AT TIME ZONE forstår dette. Men det ser ikke ud til at værdsætte, at Australien i år 2000, takket være OL i Sydney, startede sommertid omkring to måneder tidligere. Dette er lidt frustrerende, men det er ikke SQLs skyld - det er vi nødt til at give Windows skylden for. Jeg gætter på, at Windows-registreringsdatabasen ikke husker det hotfix, der gik rundt det år. (Bemærkning til mig selv:Jeg skal muligvis bede en i Windows-teamet om at ordne det...)

Nytten fortsætter dog bare!

Det tidszonenavn behøver ikke engang at være konstant. Jeg kan sende variabler ind og endda bruge kolonner:

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:99507/

(Fordi jeg kørte det lige før 18:30 her i Adelaide, hvilket tilfældigvis er næsten 21:00 i New Zealand, hvor Paul er, og næsten 5:00 i morges i den østlige del af Amerika, hvor Aaron er.)

Dette ville give mig mulighed for nemt at se, hvad klokken er for folk, uanset hvor de er i verden, og for at se, hvem der ville være bedst til at reagere på et eller andet problem, uden at skulle udføre nogen manuelle konverteringer af dato og klokkeslæt. Og endnu mere, det ville lade mig gøre det for folk i fortiden. Jeg kunne have en rapport, der analyserer, hvilke tidszoner der ville tillade det største antal hændelser at forekomme i arbejdstiden.

Disse tidszoner er angivet i sys.time_zone_info , sammen med hvad den aktuelle offset er, og om der aktuelt anvendes sommertid.

navn

current_utc_offset

is_currently_dst
Singapore standardtid

+08:00

0
W. Australien 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 af rækker fra sys.time_zone_info

Jeg er kun rigtig interesseret i, hvad navnet er, men alligevel. Og det er interessant at se, at der er en tidszone kaldet "Aus Central W. Standard Time", som er på kvarteret. Gå figur. Også værd at bemærke, at steder henvises til ved at bruge deres standardtidsnavn, selvom de i øjeblikket holder sommertid. Såsom Ulaanbaatar på listen ovenfor, som ikke er angivet som Ulaanbaatar Daylight Time. Dette kan kaste folk til en sløjfe, når de begynder at bruge AT TIME ZONE.

Kan AT TIME ZONE forårsage ydeevneproblemer?

Nu er jeg sikker på, at du undrer dig over, hvad virkningen af ​​at bruge AT TIME ZONE kan være på indeksering.

Med hensyn til formen på planen er det ikke anderledes end at håndtere datetime offset generelt. Hvis jeg har datetime-værdier, som f.eks. i AdventureWorks-kolonnen Sales.SalesOrderHeader.OrderDate (hvorpå jeg oprettede et indeks kaldet rf_IXOD), så kører begge denne forespørgsel:

vælg OrderDate, SalesOrderID fra Sales.SalesOrderHeader hvor OrderDate>=convert(datetime,'20110601 00:00') i tidszonen 'US Eastern Standard Time' og OrderDate  

Og denne forespørgsel:

vælg OrderDate, SalesOrderID fra Sales.SalesOrderHeader hvor OrderDate>=convert(datetimeoffset,'20110601 00:00 -04:00') og OrderDate  

I begge tilfælde får du planer, der ser sådan ud:

Men hvis vi udforsker lidt nærmere, er der et problem.

Den, der bruger AT TIME ZONE, bruger ikke statistikken særlig godt. Den tror, ​​den kommer til at se 5.170 rækker komme ud af den indekssøgning, når der faktisk kun er 217. Hvorfor 5.170? Nå, Aarons seneste indlæg, "Paying Attention To Estimates," forklarer det ved at henvise til indlægget "Cardinality Estimation for Multiple Predicates" fra Paul. 5.170 er 31.465 (rækker i tabellen) * 0,3 * sqrt(0,3).

Den anden forespørgsel får det rigtigt, og estimerer 217. Ingen funktioner involveret, ser du.

Dette er nok fair nok. Jeg mener - på det tidspunkt, hvor den producerer planen, vil den ikke have bedt registreringsdatabasen om de oplysninger, den har brug for, så den ved virkelig ikke, hvor mange den skal estimere. Men der er potentiale for, at det bliver et problem.

Hvis jeg tilføjer ekstra prædikater, som jeg ved, ikke kan være et problem, falder mine estimater faktisk endnu mere – ned til 89,9 rækker.

vælg OrderDate, SalesOrderID fra Sales.SalesOrderHeader hvor OrderDate>=convert(datetime,'20110601 00:00') i tidszonen 'US Eastern Standard Time' og OrderDate =convert(datetimeoffset,'20110601 00:00 +14:00') and OrderDate  

At estimere for mange rækker betyder, at der tildeles for meget hukommelse, men estimering af for få kan forårsage for lidt hukommelse, hvilket potentielt kræver et spild for at rette op på problemet (hvilket ofte kan være katastrofalt set fra et præstationsperspektiv). Læs Aarons indlæg for mere information om, hvordan dårlige estimater kan være dårlige.

Når jeg overvejer, hvordan man håndterer visning af værdier for disse personer fra før, kan jeg bruge forespørgsler som denne:

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.tzFROM PeopleAndTZs tzCROSS JOIN Sales.SalesOrderHeader oWHERE o.SalesOrderID MELLEM 440010; 

Og få denne plan:

…hvilket ikke har nogen sådanne bekymringer – Compute Scalar længst til højre konverterer datetime OrderDate til datetime offset for UTC, og Compute Scalar længst til venstre konverterer den til den passende tidszone for personen. Advarslen er, fordi jeg laver en CROSS JOIN, og det var helt bevidst.

Fordele og ulemper ved andre tidskonverteringsmetoder

Før AT TIME ZONE var en af ​​mine foretrukne, men ofte ikke værdsatte funktioner i SQL 2008 datatypen datetimeoffset. Dette gør det muligt at gemme data/tidsdata med tidszonen, såsom '20160101 00:00 +10:30', hvilket er, da vi fejrede nytår i Adelaide i år. For at se hvornår det var i US Eastern, kan jeg bruge funktionen SWITCHOFFSET.

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

Dette er det samme øjeblik i tiden, men i en anden del af verden. Hvis jeg talte i telefon med nogen i North Carolina eller New York og ønskede dem et godt nytår, fordi det var lige over midnat i Adelaide, ville de sige "Hvad mener du? Det er stadig morgenmad her nytårsaften!”

Problemet er, at for at gøre dette, skal jeg vide, at i januar er Adelaide +10:30 og US Eastern -5:00. Og det er ofte en smerte. Især hvis jeg spørger om slutningen af ​​marts, begyndelsen af ​​april, oktober, begyndelsen af ​​november – de tidspunkter på året, hvor folk ikke kan være sikre på, hvilken tidszone folk i andre lande var i, fordi de ændrer sig med en time for sommertid, og de alle gør det efter forskellige regler. Min computer fortæller mig, hvilken tidszone folk befinder sig i nu, men det er meget sværere at sige, hvilken tidszone de vil være i på andre tidspunkter af året.

For nogle andre oplysninger om konvertering mellem tidszoner, og hvordan folk som Aaron har håndteret sommertid, se følgende links:

  • Brug af AT TIME ZONE til at rette en gammel rapport (det er mig!)
  • Håndter konvertering mellem tidszoner i SQL Server – del 1
  • Håndter konvertering mellem tidszoner i SQL Server – del 2
  • Håndter konvertering mellem tidszoner i SQL Server – del 3

Og noget officiel Microsoft-dokumentation:

  • TIDSZONE (Transact-SQL)
  • sys.time_zone_info (Transact-SQL)
  • Hjælp og support til sommertid

Skal du bruge AT TIME ZONE?

AT TIME ZONE er ikke perfekt. Men det er virkelig nyttigt – utroligt meget. Det er fleksibelt nok til at acceptere kolonner og variabler som input, og jeg kan se et enormt potentiale for det. Men hvis det får mine estimater til at være ude, så bliver jeg nødt til at være forsigtig. Til visningsformål burde dette dog ikke betyde noget, og det er her, jeg kan se, at det er mest nyttigt. At gøre det nemmere at konvertere en visningsværdi til eller fra UTC (eller til eller fra lokal tid), uden at nogen behøver at bryde deres hjerner om offsets og sommertid, er en stor gevinst.

Dette er virkelig en af ​​mine yndlingsfunktioner i SQL Server 2016. Jeg har længtes efter noget som dette i meget lang tid.

Åh, og de fleste af disse milliarder mennesker i den halve times tidszone er i Indien. Men det vidste du sikkert allerede...


  1. Hvorfor giver Oracles DECODE mig en anden værdi end NVL?

  2. Python kalder sql-server lagret procedure med tabelværdiparameter

  3. Sådan opretter du en unik begrænsning på kolonne for allerede eksisterende tabel - SQL Server / TSQL vejledning del 97

  4. hvordan man ændrer dato til tid i oracle 10g