Proč by se měl každý řádek přepočítávat. V dotazu je nějaký časový údaj, který se případně jednorázově převede na UTC. Vše pak běží v UTC. Jestli chcete výsledek v místním čase, tak se převede až výsledek.
Možná je podstatou vašich dotazů, že když je v uloženém údaji i informace o časovém pásmu, tak s tím údajem musíte DB při vyhledávání nějak složitě počítat. Ale to je jen o vhodném uložení těchto dat, což je interní záležitost DB a její programátoři zrovna takovéto věci optimalizují. Jestli to má na výsledný čas dotazů nějaký vliv, tak zcela marginální i pro big data.
Správné řešení je opravdu ukládat UTC s informací o časovém pásmu. I když jde o big data. Jak si to s tím lokálním časem představujete, když je dnes vše celosvětově propojené a lezou do toho data z různých částí světa? To si budete ukládat místní čas, aniž byste věděl čí místní zrovna je?
MS má tohle obecně zprasené. I Windows si ve výchozím stavu vynucují, aby hardwarové hodiny jely v místním čase místo v UTC.
Nenestesi jsem mel pohotovost tenhle viked. Takze muzu rict a ani Oracle to nema vyresene nejlepe. Sice podporuje datovy typ "TIMESTAMP WITH TIMEZONE", ale k tomu nejde jednoduse pricist datovy typ INTERVAL. Takze se to musi konvertovat tam a nazpatek a programatorum to jaksi nevyslo naporve.
PS: Dalsi problem byl, ze cron taky nepodporuje casove zony a prechod na letni cas nedetekuje.
Cela ta problematika je mnohem slozitejsi nez by se na prvni pohled mohlo zdat, protoze hodiny se uci deti uz ve skolce a vsichni si mysli, ze tomu rozumi. Pak se ale napr. sami rebootuji servery kvuli prestupne vterine, protoze na to nikdo nemysel dopredu. Anebo autorum standartu nedojde, ze je to cele nesmyslne navrzene. Napr ISO SQL rika, ze scitanim casu a intervalu muze vzniknout neplatne datum a je vzdy na programatorovi aby si overil vysledek.(Viz proprietarni fce Oracle: add_months)
Datový typ timestamptz
je datum a čas s časovou zónou. Uložené je to tedy s časovou zónou, konkrétně s časovou zónou, která byla uvedena u té konkrétní hodnoty při vkládání. Ostatně, můžete to vidět sám, v tom výpisu je ten sloupec ts
vypsaný a vidíte tam dva řádky s časovou zónou +01
a dva s +02
.
Údaje jsem vkládal s časovou zónou Europe/Prague
, jak je z výpisu vidět. Tento zápis se v okamžiku konverze řetězce na datum a čas s časovou zónou převede na odpovídající časovou zónu +01
nebo +02
, podle toho, kam ta která hodnota spadá.
Když použiju časovou zónu Europe/Prague
, můžu sedět klidně na Měsíci a počítač, ze kterého jsou data zadaná, může být třeba na Marsu, a pořád to bude časová zóna označující lokální čas v ČR.
Když mám index podle UTC, je mi samozřejmě k ničemu, když chci hledat podle lokálního času. A opačně, když mám index podle lokálního času, je mi k ničemu, když chci hledat podle UTC. To ale není problém střídání letního a hvězdného času, ale toho, že chcete hledat podle dvou různých kritérií. Ten problém se vyřeší úplně stejně zrušením letního času jako zrušením vyhledávání podle UTC.
Jinak to, že jsou ty indexy navzájem „k ničemu“ není tak docela pravda, ony by se daly využít i navzájem, akorát by se vyhledávaný interval rozšířil o hodinu výsledná sada záznamu by se následně profiltrovala podle časové zóny. Bylo by to sice méně efektivní, než přímé využití indexu, ale pořád ještě efektivnější, než fullscan. Ale pochybuju, že by na tohle přišel nějaký optimalizátor „sám od sebe“, bylo by nutné ten dotaz odpovídajícím způsobem přeformulovat.
Ne, neni. Pro cas a casovou zonu si ho muzete zkonstruovat hned nekolika zpusoby.
A jinak samozrejme ze muzu klidne vyhledavat podle lokalniho casu s naindexovanym UTC. Staci mi znat posun. V opripade stridani letniho/zimniho casu to samozrejme efektivne nejde, jak uz jsme si rekli. Ale o tom celou dobu mluvime. Letni a zimni cas je pro vyhledavani podle casu proste problemem.
Mate jeste nejake napady, co vsechno by se dalo udelat aby se tento problem alespon castecne omezil?
Čas zpracování pro sekvenci příkazů tak, jak jsem je napsal (bez indexů) je pro oba dotazy až na statisticky nevýznamnou odchylku shodný a trvá to dlouho, protože se musí provést fullscan celé tabulky.
Čas zpracování po přidání indexů na lokální čas a na UTC čas je pro oba dotazy až na statisticky nevýznamnou odchylku shodný a dotazy jsou provedeny velmi rychle, protože se vyberou řádky splňující podmínku podle indexu.
Konkrétní časy nemá smysl sem psát, protože záleží na tom, jak výkonný je počítač, na kterém databáze běží, na její konfiguraci i na tom, jestli počítač není zatížen i něčím jiným.
V komentáři jsem vám napsal SQL příkaz pro definici tabulky, kde jsou uvedené datové typy, příkazy, kterými byla data vložena (včetně hodnot), a dotazy včetně jejich výstupů, kterými jsem data získal. Zaznamenal jste doufám, že redakční systém Lupy ty příkazy vypisuje do malého okna a je potřeba si odrolovat dolů, abyste je viděl celé.
Je na těch triviálních SQL příkazech něco, co vám mohu dovysvětlit? Mám nějak upřesňovat, že zápis '2016-03-28 15:30:00 Europe/Prague' znamená čas 15:30 dne 28. 3. 2016 v lokální časové zóně ČR?
Aha, takže „index pro čas a časovou zónu“ není určení, jak má index vypadat.
Podstata mé odpovědi je také úplně někde jinde, než konstrukce indexu. Podstatou je to, že když chcete v databázi něco efektivně vyhledávat, musíte k tomu mít vhodně zkonstruovaný index. Přičemž pokud chcete vyhledávat podle lokálního času, potřebujete index nad lokálním časem – bez ohledu na to, jakým způsobem jsou data uložena. Výhodou některých databází pak je to, že takový index vytvoříte použitím jedné vestavěné funkce.
OK, tak ještě jednou.
postgres=#CREATE TEMPORARY TABLE log (id int PRIMARY KEY, ts timestamptz); CREATE TABLE postgres=# INSERT INTO log (id, ts) VALUES (1, '2016-03-26 15:30:00 Europe/Prague'), (2, '2016-03-26 16:30:00 Europe/Prague'), (3, '2016-03-28 15:30:00 Europe/Prague'), (4, '2016-03-28 16:30:00 Europe/Prague'); INSERT 0 4 postgres=# SELECT *, ts AT TIME ZONE 'UTC' AS ts_utc FROM log; id | ts | ts_utc ----+------------------------+--------------------- 1 | 2016-03-26 15:30:00+01 | 2016-03-26 14:30:00 2 | 2016-03-26 16:30:00+01 | 2016-03-26 15:30:00 3 | 2016-03-28 15:30:00+02 | 2016-03-28 13:30:00 4 | 2016-03-28 16:30:00+02 | 2016-03-28 14:30:00 (4 rows) postgres=# SELECT *, ts AT TIME ZONE 'UTC' AS ts_utc FROM log WHERE ts::time BETWEEN '16:00:00' AND '17:00:00'; id | ts | ts_utc ----+------------------------+--------------------- 2 | 2016-03-26 16:30:00+01 | 2016-03-26 15:30:00 4 | 2016-03-28 16:30:00+02 | 2016-03-28 14:30:00 (2 rows)
Čemu na tom nerozumíte?
Akorát předvádíte, že netušíte, o co jde. Třeba bod 3 – když budete mít vše jenom podle UTC a budete potřebovat najít všechny záznamy, které mají čas mezi 16:00 UTC a 17:00 UTC – budete pro efektivní hledání potřebovat index pouze s časem. Protože hodiny, které vás zajímají, jsou uprostřed údaje datum+čas, takže index na datum-čas by vám byl k ničemu. Bez ohledu na časové zóny.
1. nikoliv, od sameho zacatku resime velke mnozstvi dat a s tim souvisejici problem vykonu. Nepohodli je samozrejme neprijemne, ale budiz, rekneme ze to bude holt trosku drazsi. Nikoliv vsak nemozne.
2. vy nevite v jake casove zone ten uzivatel je? No holt jestli k tomu mate nejaky dalsi zahadny duvod, tak si to ulozte jako offset, na to casovou zonu nepotrebujete. Kazdopadne informace o tom v jake casove zone byl uzivatel kdysi driv problem zimniho/letniho casu v zone kde zrovna jsme nijak neresi.
3. takze si vzdycky nejdriv vytvorite specialni index? Pro cas a danou zonu? Jak to chcete udelat pri milarde zaznamu? Prijde mi to malinko komplikovanejsi nez zjistit vysledek podle UTC, vam ne?
4. Tak jestli chcete blbnout, tak vas muzu ubezpecit ze UTC rozhodne casova zona neni. To uz spis kdybyste rekl ze to je cas.. ale zona? Ne.
5. Myslim ze clovek nemusi byt velky psycholog aby dokazal rozlisit jestli se bavite nebo vztekate ;-)
Uložené mám datum a čas s časovou zónou, vždy podle lokální zóny používané v okamžiku toho daného času – údaje „od listopadu do března“ tedy budou uložené s CET, údaje „od dubna do října“ s CEST. Jak to interně ukládá databáze mne nemusí zajímat. Konverze je to ::time
, to je přetypování na čas bez časové zóny, tedy '17:00:00 CET'::time = '17:00:00'
, '17:00:00 CEST'::time = '17:00:00'
. Vaše podezření je špatné, PostgreSQL umí s časem počítat správně.
Vy jste vubec nepochopil v cem je problem..
Já bych to trochu upřesnil. Já nechápu, proč vy děláte problém z něčeho, co vůbec problém není. Ptal jste se na ten hrozně problémový dotaz, tak jsem vám ho o pár komentářů níže napsal – po pravdě řečeno, neexistuje mnoho dotazů, které by byly jednodušší, než tento.
Mam li datum-cas v UTC, tak casovou zonu ukladat nepotrebuji. K cemu by mi byla kdyz ji znam?
Pro případ, když chcete uživateli zobrazit datum a čas ve stejné časové zóně, ve které je zadal. Třeba když uživatel v ČR zadá začátek události jako 26. 3. 2016 02:00:00 GST, asi to bude chtít příště zobrazit jako 26. 3. 2016 02:00:00 GST, a ne jako 25. 3. 2016 22:00:00 UTC, ani jako 25. 3. 2016 23:00:00 CET.
K cemu je vam cas interne prevedeny do UTC, kdyz chcete najit vsechny udalosti ke kterym doslo mezi 17 a 18 hodinou libovolneho dne a hlavne, mistniho casu?
Asi jste myslel datum a čas převedený do UTC. Je mi dost k ničemu, stejně jako mi bude k ničemu i v případě, že budu hledat údaje mezi 17 a 18 hodinou UTC daného dne. Když potřebuju vyhledávat podle času, musím mít v indexu čas – když budu vyhledávat podle času v UTC, budu tam mít čas v UTC, když budu vyhledávat podle lokálního času, budu tam mít lokální čas.
Nikoliv, datum-cas ukladaji v datetime ve formatu UTC. Zadnou casovou zonu.
UTC není formát. UTC je časová zóna.
Snazite se buhviproc obhajit neobhajitelne a pomalu u toho zacinate pusobit trosku srandovnim dojmem,:-)
U psaní toho triviálního SQL dotazu jsem se opravdu bavil.
SELECT * FROM log WHERE log.timestamp::time BETWEEN '17:00:00' AND '18:00:00'
Protože chcete prohledávat podle času, potřebujete mít na čas index (a s časovými zónami to nijak nesouvisí, stejné by to bylo, kdyby neexistovalo nic jiného, než UTC ¨je to jako kdybyste chtěl vyhledávat třeba podle textového řetězce od pátého znaku dál). Takže si v tomto případě uděláte index na log.timestamp::time
a výše uvedený dotaz se vyhodnotí prostě jako range podle idnexu.
Tak to bude dlouha debata :-D
1. nejde o pohodli ale o vykon. Vy jste vubec nepochopil v cem je problem..
2. mezi 'time zone' a 'time of a day that has time zone awareness' je proste rozdil. Kdyz nebudete nabalovat do vlakna dalsi silenosti, klidne se k tomu rozepisu ;-)
3. Mam li datum-cas v UTC, tak casovou zonu ukladat nepotrebuji. K cemu by mi byla kdyz ji znam?
4. Nikoliv, datum-cas ukladaji v datetime ve formatu UTC. Zadnou casovou zonu. Cemu na tom nerozumite?
5. K cemu je vam cas interne prevedeny do UTC, kdyz chcete najit vsechny udalosti ke kterym doslo mezi 17 a 18 hodinou libovolneho dne a hlavne, mistniho casu?
Snazite se buhviproc obhajit neobhajitelne a pomalu u toho zacinate pusobit trosku srandovnim dojmem,:-)
Jinak MSSQL je nejcaste pouzivany DB server v enterprise kategorii.
Což ještě nutně neznamená, že se v něm pohodlně pracuje s datem a časem.
Nevim jestli ma vyznam abych vysvetloval ze offset opravdu neni casova zona a ze se pouziva k necemu malinko jinemu.
Asi to význam nemá, když samotný Microsoft v dokumentaci uvádí: „Defines a date that is combined with a time of a day that has time zone awareness and is based on a 24-hour clock.“
Kazdopadne by bylo dobre mit na zreteli, ze se pro ukladani casu celkem logicky pouziva k tomu urceny datetime.
Pokud si vy ukládáte datum a čas bez časové zóny, nemůžete se pak divit, že ji uloženou nemáte. Hlavně to nezačněte ukládat i bez času, pak byste začal tvrdit, že čas v rámci dne je nesmysl, protože vy s tím máte problémy v databázi, a měl by stačit jen kalendář.
Treba Dynamics ktery tam sklada cas v UTC formatu a prevod na localtime provadi az na vystupu.
Aha, takže ukládají čas i s časovou zónou. Informace „vše je v UTC“ je také způsob uložení časové zóny.
Je to tak proto, ze konverze casu je proste draha operace, zejmena kdyz do veci vnasi hokej stridani zimniho a letniho casu.
Právě proto DB interně pracuje jen s časem v UTC, a když jí v dotazu zadáte jinou časovou zónu, nejprve si to interně převede do UTC. Což je levná operace, je to jeden dotaz do interní tabulky a provede se jednou pro celý dotaz.
Pokud máte možnost zvolit správný datový typ a zvolíte špatný, je to problém váš. Pokud používáte špatnou databázi, je to problém té databáze a také toho, kdo ji zvolil. Ale těžko to může být obecný problém práce s datem a časem, když existují databáze, které s tím umí normálně pracovat. Např. v PostgreSQL můžete porovnávat datum a čas v různých časových zónách, můžete převádět datum a čas z jedné časové zóny do jiné, není s tím žádný problém. A upřímně řečeno, ono na tom není nic složitého, i do toho MS SQL byste si to mohl snadno doprogramovat. Prostě stačí vše převádět do UTC, a vedle uložené hodnoty v UTC si ještě uložit časovou zónu, pro případ, že bude uživatele zajímat, v jaké časové zóně si ten údaj uložil.
Ale opravdu by me zajimalo jak byste napsal dotaz, ktery na data ulozena ve formatu UTC + casova zona, najde vsechny zaznamy ktere nastaly mezi 17 a 18 hodinou (at uz zimniho nebo letniho casu). Aniz by musel pro kazdy radek udelat prepocet. Naznacite?
Ja vam pak kdyztak ukazu stejny dotaz pro casove pasmo kde se na zimni/letni cas nehraje a muzeme debatovat nad tim, jak dlouho by prohledavani miliardy zaznamu v prvnim a druhem pripade trvalo ;-)
Jinak MSSQL je nejcaste pouzivany DB server v enterprise kategorii.
Nevim jestli ma vyznam abych vysvetloval ze offset opravdu neni casova zona a ze se pouziva k necemu malinko jinemu. Kazdopadne by bylo dobre mit na zreteli, ze se pro ukladani casu celkem logicky pouziva k tomu urceny datetime.
A tim 'pouzivaji' chci rict, ze v nem cas ukladaji i standardni MS produkty. Treba Dynamics ktery tam sklada cas v UTC formatu a prevod na localtime provadi az na vystupu. Jak taky jinak, ze.
Je to tak proto, ze konverze casu je proste draha operace, zejmena kdyz do veci vnasi hokej stridani zimniho a letniho casu.
Ano, MS SQL je specifická databáze. Nicméně datetimeoffset je právě datový typ, který umožňuje ukládat datum a čas s časovým pásmem.
To, co vy složitě popisujete jako jakési podivné posunování času, je ve skutečnosti to, co jsem napsal já – změna časového pásma, které se používá pro lokální čas. V zimě používáme pro lokální čas časové pásmo SEČ (středoevropský čas, mezinárodní zkratka CET), v létě používáme časové pásmo SELČ (středoevropský letní čas, mezinárodní zkratka CEST).
Pokud si čas ukládáte i s časovým pásmem, žádný problém nenastává. Protože SEČ na UTC převedete jednoduše tak, že odečtete jednu hodinu, SELČ na UTC převedete tak, že odečtete dvě hodiny. Špatná kombinace nastat nemůže. Problém máte jedině tehdy, když ukládáte datum a čas bez časové zóny, což je váš problém.
Pokud vaše databáze nemá vestavěné funkce pro rozumnou práci s datem a časem, je to problém té databáze. Třeba PostgreSQL umí s datem a časem s časovým pásmem normálně pracovat. Záznamy v databázi můžete mít uložené klidně v UTC nebo v CET/CEST. Když si vytvoříte SQL dotaz, kde použijete čas 17–18 hodin v časové zóně Europe/Prague, v době kompilace dotazu se to převede na UTC a pak se to v databázi vyhledá normálně podle indexu.
Tak to ale neni. Napr v MSSQL (a to opravdu neni okrajova DB) neni datovy typ, ktery by umoznil ukladani casove zony. Castecne by to snad slo eliminovat offsetem u neprilis casto pouzivaneho datoveho typu datetimeoffset, za predpokladu ze se budeme ochotni vzdat informaci o case na pocitacich v ruznych casovych pasmech.
Krom toho strilite trochu vedle. Problem neni s casovym pasmem, to je zalezitost geograficka a my celkem dobre vime v jakem casovem pasmu jsme a kdy plati letni a kdy zimni cas. Jenom holt mame data obcas o hodinu posoupnuta.
Problem pritom zustava at si je ulozime v UTC nebo v localtime, porad se muze sejit spatna kombinace. A je potiz dohledat casove intervaly ktere jsou u nekterych zaznamu napr 16-17 hodin a u jinych 17-18 kdyz mame ulozeno v localtime, jenom proto, ze se cas mezitim posunul. Nebo obracene, mit ulozeno v UTC a dohledavat cas 17-18, ktery je jednou +1 a jindy +2 hodiny.
A i kdyz si pomoci funkci MSSQL cas nakrasno dynamicky budeme prevadet, tak jednak UTC pro localtime nenaindexujeme (kdyz uz pominu tu zrudnost preindexovavat sloupec v UTC pro localtime nebo obracene) a druhak nam prohledavani zabere o nekolik radu vic casu. Coz je u big data zatracene znat...
Když píšete o indexu na sloupci, předpokládám, že myslíte nějakou databázi. Sloupec v té databázi ale má datový typ, asi datum a čas s časovou zónou. Ten převod pak zařídí databáze a použije index, protože převod mezi časovými zónami je jednoznačně dán, a kterékoli dva časové údaje i v různých časových zónách dokážete jednoznačně seřadit.
Pokud někdo ukládá datum a čas bez časové zóny, je to jeho problém.
No, zkus potom udelat podminku na porovnani casu. Tj. treba agregovat nejakou chybu v zaznamu z databazoveho logu, v rozmezi nejakeho konkretniho casu, treba mezi 16 a 17 hodinou kdyz ti to z niceho nic dvakrat do roka preskoci. A kdyz tech zaznamu mas treba miliardu. To je najednou prevod pro kazdy zaznam slusnou komplikaci. Pritom by normalne stacila podminka typu od-do a s indexem na danem sloupci by mohlo byt hotovo za par minut :-)
Čas se „posouvá zpět“ pouze v případě, že neuvádíte kompletní časový údaj (vynecháte časovou zónu). Jinak s tím problém není, protože časové údaje s uvedením časové zóny jsou jednoznačné a jsou na sebe navzájem bez problémů převoditelné. 02:30:00 SEČ = 03:30:00 SELČ = 01:30:00 UTC. Celý „problém“ pak spočívá v tom, že např. v EU se v období do 27. 3. 2016 00:59:59 UTC používá SEČ, od 27. 3. 2016 01:00:00 UTC do 30. 10. 2016 00:59:59 UTC se používá SELČ.