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 OrderDateA tento dotaz:
vyberte Datum objednávky, ID objednávky z Sales.SalesOrderHeader kde Datum objednávky>=convert(datetimeoffset,'20110601 00:00 -04:00') a OrderDateV 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;40EN BET40EN14400 před>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:00Toto 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…