sql >> Database >  >> RDS >> Sqlserver

AT TIME ZONE – een nieuwe favoriete functie in SQL Server 2016


Afbeelding © Mark Boyle | Australia Day Council of NSW.
Afbeeldingen eigendom van respectievelijke artiest(en). Alle rechten voorbehouden.

AT TIME ZONE is zo'n coole functie en het was me tot voor kort niet opgevallen, ook al heeft Microsoft er sinds december een pagina over.

Ik woon in Adelaide in Australië. En net als meer dan een miljard andere mensen in de wereld, hebben Adelaide-mensen te maken met een tijdzone van een half uur. In de winter zijn we UTC+9:30 en in de zomer zijn we UTC+10:30. Behalve dat als je dit op het noordelijk halfrond leest, je dat met 'winter' moet onthouden, ik bedoel april tot oktober. De zomertijd is van oktober tot april, en de kerstman zit op het strand met een koud drankje, zwetend door zijn dikke rode pak en baard. Tenzij hij levens gaat redden natuurlijk.

Binnen Australië hebben we drie hoofdtijdzones (West, Centraal en Oost), maar dit loopt uit tot vijf in de zomer, aangezien de drie staten die zich uitstrekken tot het noordelijke uiteinde van Australië (WA, Qld en NT) niet probeer om het even welk daglicht te bewaren. Ze zijn dicht genoeg bij de evenaar om er niets om te geven, of iets dergelijks. Het is heel leuk voor de Gold Coast-luchthaven, waarvan de landingsbaan de grens NSW-QLD overschrijdt.

Databaseservers draaien vaak in UTC, omdat het gewoon makkelijker is om niet te hoeven converteren tussen UTC en lokaal (en omgekeerd) in SQL Server. Vele jaren geleden herinner ik me dat ik een rapport moest repareren met een lijst van incidenten die zich hebben voorgedaan samen met reactietijden (sindsdien heb ik hierover geblogd). Het meten van SLA was vrij eenvoudig - ik kon zien dat er één incident plaatsvond tijdens de werkuren van de klant en dat ze binnen een uur reageerden. Ik zag dat er buiten werktijd nog een incident had plaatsgevonden en de reactie was binnen twee uur. Het probleem deed zich voor toen er een rapport werd opgesteld aan het einde van een periode waarin de tijdzone veranderde, waardoor een incident dat daadwerkelijk om 17.30 uur (buiten kantooruren) plaatsvond, werd vermeld alsof het om 16.30 uur (binnen kantooruren) had plaatsgevonden. . Het antwoord had ongeveer 90 minuten geduurd, wat goed was, maar het rapport toonde anders aan.

Dit alles is opgelost in SQL Server 2016.

Hoe AT TIME ZONE te gebruiken in SQL Server 2016

Nu kan ik met AT TIME ZONE, in plaats van te zeggen:'20160101 00:00 +10:30', beginnen met een datetime-waarde die geen tijdzoneverschuiving heeft, en AT TIME ZONE gebruiken om uit te leggen dat het in Adelaide is.

SELECTEER CONVERT (datumtijd,'20160101 00:00') BIJ TIJDZONE 'Cen. Australische standaardtijd'; -- 2016-01-01 00:00:00.000 +10:30

En dit kan worden omgezet naar de Amerikaanse tijd door AT TIME ZONE opnieuw toe te voegen.

SELECTEER CONVERT (datumtijd,'20160101 00:00') BIJ TIJDZONE 'Cen. Australia Standard Time' AT TIJDZONE 'US Eastern Standard Time'; -- 2015-12-31 08:30:00.000 -05:00

Nu weet ik dat dit veel meer langdradig is. En ik moet de string expliciet converteren naar datetime, om een ​​fout te voorkomen die zegt:

Argumentgegevenstype varchar is ongeldig voor argument 1 van de functie AT TIME ZONE.

Maar ondanks de langdradigheid ervan, ben ik er dol op, omdat ik op geen enkel moment hoefde uit te zoeken dat Adelaide in +10:30 was, of dat Eastern -5:00 was - ik moest gewoon de tijdzone bij naam weten. Uitzoeken of zomertijd wel of niet van toepassing zou zijn, werd voor mij afgehandeld, en ik hoefde geen conversie van lokaal naar UTC uit te voeren om een ​​basislijn vast te stellen.

Het werkt met behulp van het Windows-register, dat al die informatie bevat, maar helaas is het niet perfect als je terugkijkt in de tijd. Australië heeft de data in 2008 gewijzigd en de VS hebben de data in 2005 gewijzigd - beide landen hebben een groot deel van het jaar daglicht bewaard. AT TIME ZONE begrijpt dit. Maar het lijkt niet te beseffen dat in Australië in het jaar 2000, dankzij de Olympische Spelen in Sydney, Australië ongeveer twee maanden eerder met de zomertijd begon. Dit is een beetje frustrerend, maar het is niet de schuld van SQL - daar moeten we Windows de schuld van geven. Ik denk dat het Windows-register de hotfix van dat jaar niet meer herinnert. (Opmerking voor mezelf:misschien moet ik iemand in het Windows-team vragen om dat op te lossen...)

Het nut gaat echter gewoon door!

Die tijdzonenaam hoeft niet eens een constante te zijn. Ik kan variabelen doorgeven en zelfs kolommen gebruiken:

MET PeopleAndTZs AS( SELECT * FROM (VALUES ('Rob', 'Cen. Australia Standard Time'), ('Paul', 'New Zealand Standard Time'), ('Aaron', 'US Eastern Standard Time' ) ) t (persoon, tz))SELECTEER tz.persoon, SYSDATETIMEOFFSET() OP TIJDZONE tz.tz VAN 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*/

(Omdat ik dat net voor 18.30 uur hier in Adelaide heb uitgevoerd, wat toevallig bijna 21.00 uur is in Nieuw-Zeeland, waar Paul is, en bijna 05.00 uur vanmorgen in het oostelijke deel van Amerika, waar Aaron is.)

Hierdoor zou ik gemakkelijk kunnen zien hoe laat het is voor mensen, waar ook ter wereld, en wie het beste kan reageren op een probleem, zonder handmatige datetime-conversies uit te voeren. En meer nog, ik zou het in het verleden voor mensen kunnen doen. Ik zou een rapport kunnen hebben dat analyseert in welke tijdzones het grootste aantal gebeurtenissen zou plaatsvinden tijdens kantooruren.

Die tijdzones staan ​​vermeld in sys.time_zone_info , samen met wat de huidige offset is en of zomertijd momenteel wordt toegepast.

naam

current_utc_offset

is_currently_dst
Singapore-standaardtijd

+08:00

0
W. Australische standaardtijd

+08:00

0
Taipei standaardtijd

+08:00

0
Ulaanbaatar standaardtijd

+09:00

1
Noord-Koreaanse standaardtijd

+08:30

0
Aus Central W. Standaardtijd

+08:45

0
Transbaikal-standaardtijd

+09:00

0
Tokio standaardtijd

+09:00

0

Sample van rijen uit sys.time_zone_info

Ik ben alleen echt geïnteresseerd in wat de naam is, maar toch. En het is interessant om te zien dat er een tijdzone is genaamd "Aus Central W. Standard Time", die op het kwartier valt. Ga figuur. Het is ook vermeldenswaard dat naar plaatsen wordt verwezen met hun standaardtijdnaam, zelfs als ze momenteel DST in acht nemen. Zoals Ulaanbaatar in die lijst hierboven, die niet wordt vermeld als Ulaanbaatar Daylight Time. Dit kan mensen in de war brengen wanneer ze AT TIME ZONE gaan gebruiken.

Kan AT TIME ZONE prestatieproblemen veroorzaken?

Nu vraag je je vast af wat de impact van het gebruik van AT TIME ZONE kan zijn op indexeren.

Wat de vorm van het plan betreft, is het niet anders dan omgaan met datetimeoffset in het algemeen. Als ik datetime-waarden heb, zoals in de AdventureWorks-kolom Sales.SalesOrderHeader.OrderDate (waarop ik een index heb gemaakt met de naam rf_IXOD), voer dan beide deze query uit:

selecteer OrderDate, SalesOrderID van Sales.SalesOrderHeader waarbij OrderDate>=convert(datetime,'20110601 00:00') in tijdzone 'US Eastern Standard Time' en OrderDate  

En deze vraag:

selecteer OrderDate, SalesOrderID van Sales.SalesOrderHeader waarbij OrderDate>=convert(datetimeoffset,'20110601 00:00 -04:00') en OrderDate  

In beide gevallen krijg je plannen die er als volgt uitzien:

Maar als we wat nader onderzoeken, is er een probleem.

Degene die AT TIME ZONE gebruikt, gebruikt de statistieken niet zo goed. Het denkt dat er 5.170 rijen uit die Index Seek komen, terwijl er eigenlijk maar 217 zijn. Waarom 5.170? Welnu, Aarons recente post, "Aandacht besteden aan schattingen", legt het uit door te verwijzen naar de post "Kadinaliteitsschatting voor meerdere predikaten" van Paul. 5.170 is 31.465 (rijen in de tabel) * 0.3 * sqrt(0.3).

De tweede vraag doet het goed, met een schatting van 217. Er zijn geen functies bij betrokken, zie je.

Dit is waarschijnlijk eerlijk genoeg. Ik bedoel - op het moment dat het het plan produceert, heeft het het register niet om de informatie gevraagd die het nodig heeft, dus het weet echt niet hoeveel het moet schatten. Maar er is een kans dat het een probleem wordt.

Als ik extra predikaten toevoeg waarvan ik weet dat dit geen probleem kan zijn, dalen mijn schattingen zelfs nog verder - tot 89,9 rijen.

selecteer OrderDate, SalesOrderID van Sales.SalesOrderHeader waarbij OrderDate>=convert(datetime,'20110601 00:00') in tijdzone 'US Eastern Standard Time' en OrderDate =convert(datetimeoffset,'20110601 00:00 +14:00') en OrderDate  

Het schatten van te veel rijen betekent dat er te veel geheugen wordt toegewezen, maar het schatten van te weinig kan leiden tot te weinig geheugen, met mogelijk een lekkage om het probleem te verhelpen (wat vaak rampzalig kan zijn vanuit prestatieperspectief). Lees Aarons post voor meer informatie over hoe slechte schattingen slecht kunnen zijn.

Als ik bedenk hoe ik om moet gaan met het weergeven van waarden voor die mensen van vroeger, kan ik query's als deze gebruiken:

MET 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 BIJ TIJDZONE 'UTC' BIJ TIJDZONE tz.tzFROM PeopleAndTZs tzCROSS JOIN Sales.SalesOrderHeader oWHERE o.SalesOrderID TUSSEN 44001 EN 44010; pre> 

En krijg dit plan:

... die dergelijke zorgen niet heeft - de meest rechtse Compute Scalar converteert de datetime OrderDate naar datetimeoffset voor UTC, en de meest linkse Compute Scalar converteert deze naar de juiste tijdzone voor de persoon. De waarschuwing is omdat ik een CROSS JOIN doe, en dat was volledig opzettelijk.

Voor- en nadelen van andere tijdconversiemethoden

Vóór AT TIME ZONE was een van mijn favoriete, maar vaak niet gewaardeerde kenmerken van SQL 2008 het datatype datetimeoffset. Hierdoor kunnen datum-/tijdgegevens ook met de tijdzone worden opgeslagen, zoals '20160101 00:00 +10:30', wanneer we dit jaar nieuwjaar in Adelaide hebben gevierd. Om te zien wanneer dat in US Eastern was, kan ik de functie SWITCHOFFSET gebruiken.

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

Dit is hetzelfde moment in de tijd, maar in een ander deel van de wereld. Als ik aan de telefoon zou zijn met iemand in North Carolina of New York en hen een gelukkig nieuwjaar wenste omdat het net na middernacht was in Adelaide, zouden ze zeggen:"Wat bedoel je? Het is hier nog steeds ontbijttijd op oudejaarsavond!”

Het probleem is dat ik hiervoor moet weten dat Adelaide in januari +10:30 is en US Eastern -5:00 uur. En dat is vaak vervelend. Vooral als ik vraag naar eind maart, begin april, oktober, begin november – die tijd van het jaar waarin mensen niet zeker weten in welke tijdzone mensen in andere landen zich bevonden omdat ze een uur veranderen voor zomertijd, en ze allemaal volgens verschillende regels. Mijn computer vertelt me ​​in welke tijdzone mensen zich nu bevinden, maar het is veel moeilijker om te bepalen in welke tijdzone ze zich op andere momenten van het jaar zullen bevinden.

Zie de volgende links voor wat andere informatie over het converteren tussen tijdzones en hoe mensen zoals Aaron met zomertijd zijn omgegaan:

  • AT TIME ZONE gebruiken om een ​​oud rapport te herstellen (dat ben ik!)
  • Verwerk conversie tussen tijdzones in SQL Server – deel 1
  • Omgaan met conversie tussen tijdzones in SQL Server – deel 2
  • Omgaan met conversie tussen tijdzones in SQL Server – deel 3

En wat officiële Microsoft-documentatie:

  • OP TIJDZONE (Transact-SQL)
  • sys.time_zone_info (Transact-SQL)
  • Help en ondersteuning voor zomertijd

Moet je AT TIME ZONE gebruiken?

AT TIME ZONE is niet perfect. Maar het is echt nuttig - ongelooflijk dus. Het is flexibel genoeg om kolommen en variabelen als invoer te accepteren, en ik zie er enorm veel potentieel voor. Maar als het ervoor zorgt dat mijn schattingen niet kloppen, dan moet ik voorzichtig zijn. Voor weergavedoeleinden zou dit er echter helemaal niet toe doen, en dat is waar ik kan zien dat het het meest nuttig is. Het is een grote overwinning om het gemakkelijker te maken om een ​​weergavewaarde van of naar UTC (of van of naar een lokale tijd) te converteren, zonder dat iemand zijn hersens hoeft te breken over offsets en zomertijd.

Dit is echt een van mijn favoriete functies van SQL Server 2016. Ik schreeuw al heel lang om zoiets.

Oh, en de meeste van die miljard mensen in de tijdzone van een half uur zijn in India. Maar dat wist je waarschijnlijk al…


  1. Hoe wijzig ik velden in het nieuwe PostgreSQL JSON-gegevenstype?

  2. PostgreSQL-anonimisering op aanvraag

  3. Hoe EXCEPT werkt in SQL Server

  4. Opstartomgeving configureren in SQL Server Management Studio (SSMS) - SQL Server / TSQL-zelfstudie, deel 7