sql >> Databáze >  >> RDS >> Sqlserver

AT TIME ZONE – nová oblíbená funkce v SQL Server 2016


Obrázek © Mark Boyle | Australia Day Council of NSW.
Vlastnictví obrázků příslušných umělců. Všechna práva vyhrazena.

AT TIME ZONE je skvělá funkce a donedávna jsem si jí nevšiml, i když Microsoft má o ní stránku od prosince.

Bydlím v Adelaide v Austrálii. A stejně jako více než miliarda dalších lidí na světě se lidé z Adelaide musí vyrovnat s tím, že jsou v půlhodinovém časovém pásmu. V zimním čase máme UTC+9:30 a v letním UTC+10:30. Až na to, že pokud to čtete na severní polokouli, budete si muset pamatovat, že „zimou“, mám na mysli duben až říjen. Letní čas je říjen až duben a Santa Claus sedí na pláži se studeným nápojem a potí se přes hustý červený oblek a vousy. Pokud ovšem nezachraňuje životy.

V Austrálii máme tři hlavní časová pásma (západní, střední a východní), ale toto se v létě rozšiřuje na pět, protože tři státy, které se rozprostírají na severním konci Austrálie (WA, Qld a NT), nemají pokusit se zachránit denní světlo. Jsou dost blízko rovníku, aby jim to bylo jedno, nebo něco takového. Pro letiště Gold Coast, jehož ranvej překračuje hranici NSW-QLD, je to spousta zábavy.

Databázové servery často běží v UTC, protože je jednoduše snazší neřešit převod mezi UTC a místním (a naopak) v SQL Server. Před mnoha lety si pamatuji, že jsem musel opravit zprávu, která uváděla incidenty, ke kterým došlo, spolu s dobou odezvy (od té doby jsem o tom psal na blogu). Měření SLA bylo docela přímé – viděl jsem, že k jednomu incidentu došlo během pracovní doby zákazníka a že zákazník odpověděl do jedné hodiny. Viděl jsem, že k dalšímu incidentu došlo mimo pracovní dobu, a odpověď byla do dvou hodin. Problém nastal, když bylo hlášení vytvořeno na konci období, kdy se změnilo časové pásmo, což způsobilo, že incident, který se skutečně stal v 17:30 (mimo hodiny), byl uveden, jako by se stal v 16:30 (uvnitř hodin). . Odpověď trvala asi 90 minut, což bylo v pořádku, ale zpráva ukazovala něco jiného.

To vše je opraveno v SQL Server 2016.

Jak používat AT TIME ZONE v SQL Server 2016

Nyní, s AT TIME ZONE, místo toho, abych řekl:'20160101 00:00 +10:30', mohu začít s hodnotou data a času, která nemá posun v časovém pásmu, a použít AT TIME ZONE k vysvětlení, že je v Adelaide.

SELECT CONVERT(datetime,'20160101 00:00') AT TIME ZONE 'Cen. australský standardní čas“; -- 2016-01-01 00:00:00.000 +10:30

A to lze převést na americký čas opětovným připojením AT TIME ZONE.

SELECT CONVERT(datetime,'20160101 00:00') AT TIME ZONE 'Cen. Australský standardní čas' V ČASOVÉ ZÓNĚ 'Východní standardní čas USA'; -- 2015-12-31 08:30:00.000 -05:00

Teď vím, že je to mnohem zdlouhavější. A musím explicitně převést řetězec na datum a čas, abych se vyhnul chybě:

Datový typ argumentu varchar je neplatný pro argument 1 funkce AT TIME ZONE.

Ale i přes zdlouhavost toho mám rád, protože jsem v žádném okamžiku nemusel zjišťovat, že Adelaide je v +10:30 nebo že Eastern je -5:00 – prostě jsem potřeboval znát časové pásmo jménem. Zjišťování, zda má platit letní čas nebo ne, bylo řešeno za mě a nemusel jsem provádět žádnou konverzi z místního na UTC, abych vytvořil nějakou základní linii.

Funguje to pomocí registru systému Windows, který obsahuje všechny tyto informace, ale bohužel to není dokonalé, když se podíváte zpět v čase. Austrálie změnila data v roce 2008 a USA změnily svá data v roce 2005 – obě země šetří denní světlo po větší část roku. AT TIME ZONE tomu rozumí. Ale nezdá se, že by si uvědomoval, že v Austrálii v roce 2000, díky olympijským hrám v Sydney, Austrálie zahájila letní čas asi o dva měsíce dříve. To je trochu frustrující, ale není to chyba SQL – musíme za to vinit Windows. Myslím, že registr Windows si nepamatuje opravu hotfix, která se toho roku objevila. (Poznámka pro sebe:Možná budu muset požádat někoho z týmu Windows, aby to opravil…)

Užitečnost však pokračuje!

Název časového pásma ani nemusí být konstantní. Mohu předávat proměnné a dokonce používat sloupce:

WITH PeopleAndTZs AS( SELECT * FROM (VALUES ('Rob', 'Standardní čas Cen. Austrálie'), ('Paul', 'Standardní čas Nového Zélandu'), ('Aaron', 'Východní čas USA' ) ) t (osoba, 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:54/9251.09 /před> 

(Protože jsem to spustil těsně před 18:30 tady v Adelaide, což je shodou okolností skoro 21:00 na Novém Zélandu, kde je Paul, a dnes skoro 5:00 ve východní části Ameriky, kde je Aaron.)

To by mi umožnilo snadno zjistit, jaký je čas pro lidi, ať jsou kdekoli na světě, a zjistit, kdo by byl nejlepší reagovat na nějaký problém, aniž bych musel provádět jakékoli ruční převody data a času. A ještě víc by mi to umožnilo udělat to pro lidi v minulosti. Mohl bych mít zprávu, která analyzuje, která časová pásma by umožnila, aby se během pracovní doby odehrál největší počet událostí.

Tato časová pásma jsou uvedena v sys.time_zone_info , spolu s tím, jaký je aktuální posun a zda se aktuálně používá letní čas.

name

current_utc_offset

is_currently_dst
Singapurský standardní čas

+08:00

0
W. Australský standardní čas

+08:00

0
Tchajpejský standardní čas

+08:00

0
Ulánbátarský standardní čas

+09:00

1
Severokorejský standardní čas

+08:30

0
Aus Central W. Standardní čas

+08:45

0
Transbajkalský standardní čas

+09:00

0
Tokijský standardní čas

+09:00

0

Vzorkování řádků ze sys.time_zone_info

Opravdu mě zajímá jen to, jak se jmenuje, ale stejně. A je zajímavé vidět, že existuje časové pásmo zvané „Aus Central W. Standardní čas“, které je na čtvrthodinu. Jdi zjistit. Za zmínku také stojí, že místa jsou označována pomocí názvu standardního času, i když právě sledují letní čas. Jako Ulánbátar v tomto seznamu výše, který není uveden jako Ulánbátarský letní čas. Když začnou používat AT TIME ZONE, může to způsobit smyčku.

Může AT TIME ZONE způsobit problémy s výkonem?

Nyní vás jistě zajímá, jaký vliv může mít používání AT TIME ZONE na indexování.

Pokud jde o tvar plánu, neliší se to obecně od zacházení s datem a časem. Pokud mám hodnoty datetime, například ve sloupci AdventureWorks Sales.SalesOrderHeader.OrderDate (na základě kterého jsem vytvořil index nazvaný rf_IXOD), spustím oba tento dotaz:

vyberte OrderDate, SalesOrderID z Sales.SalesOrderHeader kde OrderDate>=convert(datetime,'20110601 00:00') v časovém pásmu 'US Eastern Standard Time' a OrderDate  

A tento dotaz:

vyberte Datum objednávky, ID objednávky z Sales.SalesOrderHeader kde Datum objednávky>=convert(datetimeoffset,'20110601 00:00 -04:00') a OrderDate  

V obou případech získáte plány, které vypadají takto:

Ale když to prozkoumáme trochu blíže, je tu problém.

Ten, který používá AT TIME ZONE, nevyužívá statistiky příliš dobře. Myslí si, že z tohoto hledání indexu vyjde 5 170 řádků, i když ve skutečnosti je jich pouze 217. Proč 5 170? Aaronův nedávný příspěvek „Paying Attention To Estimates“ to vysvětluje odkazem na příspěvek „Odhad mohutnosti pro více predikátů“ od Paula. 5 170 je 31 465 (řádky v tabulce) * 0,3 * sqrt(0,3).

Druhým dotazem je to správně, odhadem 217. Žádné funkce, vidíte.

Tohle je asi dost fér. Chci říct – v okamžiku, kdy vytváří plán, nebude žádat registr o informace, které potřebuje, takže opravdu neví, kolik jich odhadnout. Ale existuje potenciál, že to bude problém.

Pokud přidám další predikáty, o kterých vím, že to nemůže být problém, mé odhady ve skutečnosti klesnou ještě dále – až na 89,9 řádků.

vyberte OrderDate, SalesOrderID z Sales.SalesOrderHeader kde OrderDate>=convert(datetime,'20110601 00:00') v časovém pásmu 'US Eastern Standard Time' a OrderDate =convert(datetimeoffset,'20110601 00:00 +14:00') a OrderDate  

Odhadování příliš mnoha řádků znamená alokaci příliš velkého množství paměti, ale odhad příliš malého počtu může způsobit příliš málo paměti, přičemž k nápravě problému může být zapotřebí únik (což může být z hlediska výkonu často katastrofální). Přečtěte si Aaronův příspěvek, kde najdete další informace o tom, jak špatné mohou být špatné odhady.

Když zvážím, jak zacházet se zobrazováním hodnot pro ty lidi z dřívějška, mohu použít dotazy jako tento:

WITH PeopleAndTZs AS( SELECT * FROM (VALUES ('Rob', 'Standardní čas Cen. Austrálie'), ('Paul', 'Standardní čas Nového Zélandu'), ('Aaron', 'Východní čas USA' ) ) t (osoba, 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; 

A získejte tento plán:

…což nemá žádné takové starosti – výpočetní skalár nejvíce vpravo převádí datum a čas Datum objednávky na datum a čas offset pro UTC a výpočetní skalár nejvíce vlevo jej převádí na příslušné časové pásmo pro danou osobu. Varování je proto, že dělám CROSS JOIN, a to bylo zcela záměrné.

Výhody a nevýhody jiných metod převodu času

Před AT TIME ZONE byl jednou z mých oblíbených, ale často nedoceněných funkcí SQL 2008 datový typ datetimeoffset. To umožňuje ukládat data/čas také s časovým pásmem, jako je „20160101 00:00 +10:30“, kdy jsme letos slavili Nový rok v Adelaide. Abych viděl, kdy to bylo ve východních USA, mohu použít funkci SWITCHOFFSET.

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

Toto je stejný okamžik v čase, ale v jiné části světa. Kdybych telefonoval někomu v Severní Karolíně nebo New Yorku a přál mu šťastný nový rok, protože v Adelaide právě uplynula půlnoc, řekl by:„Co tím myslíš? Tady je na Silvestra pořád ještě snídaně!“

Problém je v tom, že abych to mohl udělat, potřebuji vědět, že v lednu je v Adelaide +10:30 a východě USA -5:00. A to je často bolest. Zvláště pokud se ptám na konec března, začátek dubna, říjen, začátek listopadu – ta období roku, kdy si lidé nemohou být jisti, v jakém časovém pásmu se lidé v jiných zemích nacházeli, protože se kvůli letnímu času mění o hodinu. všichni tak činí podle jiných pravidel. Můj počítač mi říká, v jakém časovém pásmu se lidé nyní nacházejí, ale je mnohem těžší určit, v jakém časovém pásmu se budou nacházet v jiných obdobích roku.

Další informace o převodu mezi časovými pásmy a o tom, jak se lidé jako Aaron vypořádali s letním časem, naleznete na následujících odkazech:

  • Pomocí AT TIME ZONE opravit starý přehled (to jsem já!)
  • Řešení převodu mezi časovými pásmy na serveru SQL Server – část 1
  • Řešení převodu mezi časovými pásmy na serveru SQL Server – část 2
  • Řešení převodu mezi časovými pásmy na serveru SQL Server – část 3

A některá oficiální dokumentace společnosti Microsoft:

  • V ČASOVÉ ZÓNĚ (Transact-SQL)
  • sys.time_zone_info (Transact-SQL)
  • Nápověda a podpora pro letní čas

Měli byste použít AT TIME ZONE?

AT TIME ZONE není dokonalé. Ale je to opravdu užitečné – neuvěřitelně užitečné. Je dostatečně flexibilní, aby akceptoval sloupce a proměnné jako vstup, a vidím pro něj obrovský potenciál. Ale pokud to způsobí, že mé odhady budou venku, pak budu muset být opatrný. Pro účely zobrazení by to však nemělo vůbec záležet, a právě tam to vidím jako nejužitečnější. Usnadnění převodu zobrazované hodnoty do nebo z UTC (nebo do nebo z místního času), aniž by si někdo musel lámat hlavu nad offsety a DST, je velká výhra.

Toto je opravdu jedna z mých oblíbených funkcí SQL Serveru 2016. Po něčem takovém jsem křičel už velmi dlouho.

A většina z těch miliard lidí v půlhodinovém časovém pásmu je v Indii. Ale to už jste asi věděli…


  1. Jak ve WiX otestuji existenci klíče registru (nikoli hodnoty) pro Oracle ODP.Net

  2. Spouštěč s názvem dynamického pole

  3. Zvládnutí použití stoplistů pomocí SQL Server Full-Text Search (FTS)

  4. 3 způsoby, jak vrátit časové pásmo z hodnoty Datetime v Oracle