Hlavní navigace

Názory k článku Datové typy v PostgreSQL pro práci s datem a časem

Článek je starý, nové názory již nelze přidávat.

  • 28. 2. 2019 0:18

    Miroslav Šilhavý

    Když se podíváte na následující tabulku, tak doufám, že se mnou souhlasíte, že jsou nadimenzovány více než dostatečně. (Time čas 8/12 bajtů 00:00:00 .. 24:00:00).

    Jen pro zajímavost, nevíte, jak se tento datový typ vyrovná s přestunými sekundami, které se někdy před Novým rokem přidávají? Protože now() navrátí např. 2018-12-31 24:00:01, ale pak dopadla špatně extrakce času z timestampu..?

  • 28. 2. 2019 0:31

    Miroslav Šilhavý

    Beru dotaz zpět, dohledal jsem (https://www.postgresql.org/docs/11/functions-datetime.html):

    timezone
    The time zone offset from UTC, measured in seconds. Positive values correspond to time zones east of UTC, negative values to zones west of UTC. (Technically, PostgreSQL does not use UTC because leap seconds are not handled.)

    :))) nicméně tedy nesouhlasím s tvrzením, že datové typy jsou nadimenzovány více než dostatečně :)... Na tisíciletí nám to sedí, falíruje nám tam jen ta sekunda :).

  • 28. 2. 2019 0:46

    Miroslav Šilhavý

    V praxi to může mít své důsledky. Např. dotaz:

    SELECT '2017-01-01 00:00:00'::ti­mestamp - '2016-12-31 23:59:59'::ti­mestamp;

    by měl správně navrátit '00:00:02', ale v Postgresu navrátí '00:00:01'.

  • 28. 2. 2019 5:00

    kvr kvr

    A co teprve oblíbené dotazy typu SELECT * FROM table WHERE eventDate BETWEEN 'xyz 00:00:00' AND 'xyz 23:59:59' ?

    Je to samozřejmě důsledkem špatného návrhu, kdy se k definici intervalů používají z obou stran inclusivní hranice, na druhé straně SQL k tomu přímo svádí absencí inclusive-exclusive-between varianty.

  • 28. 2. 2019 0:45

    pinnocchio

    UTC != GMT

    GMT je časová zóna určující polohu Slunce z nultýho poledníku.

    UTC není časová zóna, ale standard pro určení času, založenej na přesnejch hodinách. Ke všemu "přesnějších" než rotace Země.

    Z toho důvodu se může čas v UTC a v GMT lišit až o jednu sekundu (GMT se semtam dorovnává přestupnou sekundou).

  • 28. 2. 2019 7:40

    Miroslav Šilhavý

    No to je právě to. Operační systémy dnes pracují v UTC, tj. ve skutečnosti, v OS, ta přestupná sekunda nastane, uběhne. Některé OS přestupnou sekundu rozpouštějí (jestli se nepletu, tak 24 hodin dopředu), některé OS ji "poctivě" reportují. A tam vůbec netuším, jak se PostgreSQL zachová.

  • 28. 2. 2019 8:24

    Filip Jirsák

    Je to opačně, přestupnou sekundou (nebo teoreticky vynecháním, sekundy) se dorovnává UTC, aby byla minimální odchylka od UT1, který je založen na GMT. Proto může být v UTC čas 23:59:60.

  • 28. 2. 2019 8:37

    Miroslav Šilhavý

    To už je jedno z jaké strany to nazvete. Podstata věci je v tom, že se naše Koule naotáčí během roku o něco víc nebo míň, než co odpovídá přesným (tj. atomovým) hodinám. Hodiny naměří 365 dní přesně, zatímco slunce nad greenwichským poledníkem (abyste mě nebral za slovo, tak nad všemi poledníky :)) projde o sekundu dřív, nebo později.

    V ten moment je potřeba čas, který je odpočítán přesně z počtu kmitů atomu cesia (a je přenášen všude, včetně PC), posunout právě o tu sekundu. V ten okamžik všichni (včetně PC) musí někde nějak tu sekundu přidat, nebo ubrat. Mohou ji rozpustit den předtím. Nebo, některé systémy prostě tu přestupnou sekundu zobrazí v UTC.

  • 28. 2. 2019 9:20

    Filip Jirsák

    Nejde o zobrazení v UTC, ta přestupná sekunda je tam opravdu vložená. A UTC (jak plyne i z názvu) slouží k tomu, aby všichni na světě (no, aspoň na Zemi, s tím U si dost fandíme…) měli stejný čas, protože často je stejný čas potřeba. Ne vždy je to potřeba s přesností na sekundy, ale u elektronické komunikace je to čím dál častější – i u vás může záležet na tom, zda jste daňové přiznání nebo přihlášku do výběrového řízení poslal sekundu před půlnocí nebo až o půlnoci.

    Takže to rozpuštění přestupné sekundy do celého dne je řešení pro interní systém, ale musím pak počítat s tím, že v tu chvíli nemám stejný čas, jako ostatní. To, že třeba Google přestupnou sekundu rozpouští, je podle mne docela problém, protože je zároveň velkým provozovatelem cloudových služeb, takže pak spousta služeb po celém internetu má špatný čas, a nemusí o tom ani vědět.

  • 28. 2. 2019 13:02

    bez přezdívky

    A člověka ještě víc zmate, když zjistí, že jiné databáze to mají jinak. Tak kupříkladu Oracle:

    • datový typ DATE obsahuje datum i čas s přesností na sekundy. Má 7 bytů a interní ID 12
    • jiný datový typ, ovšem také pojmenovaný DATE, má 8 bytů. Interní ID 13
    • samostatné datové typy pro datum a čas neexistují. Pokud je chce emulovat pomocí DATE, tak bude kód zamořený funkcí TRUNC. V praxi je lepší je neemulovat a prostě se bez nich odejít. Případně použít jiný typ jako NUMBER nebo VARCHAR2 a ošéfovat si to ručně. Má osobní zkušenost je, že datum a čas odděleně jsou potřeba jen na exotiku typu "spustit každý den ve tři ráno"
    • datový typ TIMESTAMP obsahuje datum i čas. Oproti DATE umí i zlomky sekundy a to na 0 až 9 míst, takže teoreticky s přesností nanosekund. Přesnost určuje programátor a databáze ji "ráda ztrácí" při implicitních konverzích (protože když nezadáte přenost, tak je default 6)
    • zrada je ve funkcích typu SYSTIMESTAMP. Ta vrací čas z OS. Takže i když nastavíte přesnost na nanosekundy, tak skutečná přesnost bude jinde. V případě OS Windows vrací OS jen mikrosekundy. Oracle Linux vrací nanosekundy

    Rozdíl mezi DATE a DATE je v tom, že ten první je interní datový formát, zatímco ten druhý je hodnota vypočítaná nějakou funkcí. Takže sloupec typu DATE má 7 bytů a je to typ 12, zatímco výsledek funkce SYSDATE má 8 bytů a je to typ 13. Ten extra byte je "unused". Normální smrtelník nikdy nezjistí, že to jsou dva různé datové typy, Oracle je implicitně konvertuje. Jenže pokud potřebujete řešit věci kolem správy metadat, tak dříve nebo později narazíte na nesrovnalosti mezi verzemi Oracle způsobené tím, že na 12.1 se ve view ukáže typ 12, zatímco 12.2 reportuje typ 13, navíc s rozdílnou velikostí (7 vs 8 bytů).

    Na Oracle se dá datový typ zkoumat třebas takhle: select dump(sysdate) from dual

  • 28. 2. 2019 20:47

    jviki

    Ze zkušenosti musím naprosto nesouhlasit s upřednostňováním timestamp with time zone před timestamp without time zone.

    Pokud máte např. 3 kooperující systémy, v mém případě nějaký sběrný bod, ze kterého chodí data, dále serverovou aplikaci, která komunikuje s databází, a samotný databázový systém, tak musíte zajistit, že všechny systémy mají shodné nastavení časových zón, anebo musíte zajistit explicitní přepočet časové zóny na rozhraních.

    Nelze očekávat, že všichni klienti komunikující s databází mají nastavenou stejnou zónu (hrozí tu mimochodem race condition právě např. na rozhraní letní / zimní čas).

    Timestamp with time zone dále komplikuje ladění, protože mi databáze dává interpretovaná data a nikoliv uložená data. Daleko hůře se odhalují chyby spojené právě s datem a časem.

    Osobně zastávám názor, že časové značky by měly být na aplikační úrovni (a níže, tedy v DB) vždy v UTC. Konkrétní časové zóny jsou totiž závislé na kontextu - tedy koncovém uživateli.

    Používáním timestamp with time zone jsem se už dost spálil. Takže implicitní chování timestamp mi připadá v pořádku. Výhrady, které uvádíte mi často připadají přesně opačné. Např. je dobře, že timestamp není ovlivněn přechody mezi letním a zimním časem.

  • 28. 2. 2019 21:31

    Filip Jirsák

    Připadá mi, že jste to napsal přesně opačně. Když ukládáte čas i s časovou zónou, nemusíte řešit, kdo jakou časovou zónu má, protože to vždy dokážete převést na libovolnou jinou časovou zónu. Když máte čas bez časové zóny, musíte naopak mít údaj o časové zóně uložený někde externě (např. implicitně, že se vždy používá „naše“ časová zóna, lokální, v případě ČR tedy Europe/Prague). Pak právě nastávají třeba ty problémy se změnou času, protože když vám přijde čas 27. 10. 2019 02:30:00, tak nevíte, jestli je to ještě SELČ nebo už SEČ.

  • 28. 2. 2019 22:40

    kvr kvr

    Pak právě nastávají třeba ty problémy se změnou času, protože když vám přijde čas 27. 10. 2019 02:30:00, tak nevíte, jestli je to ještě SELČ nebo už SEČ.

    Eh, proto právě jviki psal, že je třeba používat timezone, která přechodem letní/zimní čas netrpí. Použiju-li běžnou timezone, tak naopak ten záznam není jednu hodinu v roce unikátní a ještě k tomu matematicky rostoucí.

    +1 na předchozí příspěvek, v dnešním globalizovaném světě jsou timezone v DB silně kontraproduktivní, v podstatě je typicky nevhodné je řešit i na aplikačním serveru (backend) a celou konverzi dělat až na úrovni browseru. Samozřejmě tam, kde to jde (moderní single page aplikace). Výjimkou jsou různé účetní reporty (nebo cokoliv, co má návaznost na legislativní pravidla), ale tam se stejně udržuje uměle vytvořené razítko spíš s datem než s časem. Je-li tam čas, tak je stejně timezone natvrdo zadrátovaná na úrovni vlastnické entity než k záznamu samotnému.

  • 1. 3. 2019 7:20

    Filip Jirsák

    On je v těch časových zónách trochu zmatek, existují dva typy časových zón – „občanské“, jako třeba Europe/Prague, které přechodem na zimní čas a zpět trpí. A pak geografické, jako UTC, SEČ a SELČ, které tím přechodem netrpí. PostgreSQL používá jenom ten druhý typ zón.

    I pokud použijete pro celou databázi jednu časovou zónu, třeba UTC, pořád je vhodné data do databáze ukládat i s tou časovou zónou, protože pak máte tu informaci přímo v databázi a nemusíte ji přidávat externě. Když použijete datový typ bez časové zóny, interpretuje databáze časovou zónu na základě konfigurace, což je obvykle lokální zóna, u nás tedy typicky Praha. Za prvé tam máte zase problém přechodem z letního času, ale hlavně riskujete, že budete chtít server přesunout do USA nebo tam udělat repliku, nastavíte lokální čas třeba na východní pobřeží, a všechny časy se vám o několik hodin posunou, protože jste změnil tu implicitní časovou zónu.

    Ne, zahazovat údaje, které znám (např. tu časovou zónu) není nikdy dobrý nápad. A v globalizovaném světě je používání času bez časové zóny o to horší nápad.

  • 1. 3. 2019 7:33

    Pavel Stěhule

    Bacha na to - V postgresu jsou k dispozici oba způsoby určení zóny (plus další) - na venek - interně jede v UTC

    postgres=# show timezone;
    ┌───────────────┐
    │   TimeZone    │
    ╞═══════════════╡
    │ Europe/Prague │
    └───────────────┘
    (1 row)
  • 1. 3. 2019 7:35

    Miroslav Šilhavý

    A pak geografické, jako UTC, SEČ a SELČ, které tím přechodem netrpí. PostgreSQL používá jenom ten druhý typ zón.

    Zde matete lidi. Jsou dvě různé věci:
    1. V datovém typu TIMESTAMP WITH TIME ZONE ukládá postgres časovou informaci v univerzálním formátu (zjednodušeně řečeno, interně je čas uložen na nultém poledníku) + k tomu uloží ofset v hodinách od nultého poledníku (tj. např. +1 v případě, že v době uložení platí SEČ, +2 v případě, že v době uložení platí SELČ).

    2. Naopak databáze samotná pracuje s těmi "občanskými" timezonami. V nastavení postgresql.conf (ale je to konfigurovatelné i za běhu pomocí SET timezone='Euro­pe/Prague'; To nemění nic na databázovém serveru, ale jen klientské nastavení. To, kde se nachází ten, kdo s databází pracuje. Díky tomuto nastavení pak postgres ví, jak má čas uložit do databáze.

    Příklad:
    a) pokud mám nastaveno timezone='Euro­pe/Prague' a dám příkaz
    SELECT CAST('2019-01-01 00:00:00' AS TIMESTAMP WITH TIME ZONE);
    dostanu: '2019-01-01 00:00:00+01'

    b) pokud mám nastaveno timezone='Euro­pe/Moscow' a dám příkaz
    SELECT CAST('2019-01-01 00:00:00' AS TIMESTAMP WITH TIME ZONE);
    dostanu: '2019-01-01 00:00:00+03'

    No a nakonec, pokud tyto dvě "novoroční půlnoci" od sebe odečtu:
    SELECT CAST('2019-01-01 00:00:00+03' AS TIMESTAMP WITH TIME ZONE) - CAST('2019-01-01 00:00:00+01' AS TIMESTAMP WITH TIME ZONE);
    dostanu: '-02:00:00' - tedy přesně dvouhodinový rozdíl mezi pražským a moskevským Novým rokem.

  • 1. 3. 2019 7:59

    Pavel Stěhule

    Tak trochu mi přijde, že oba dva tu tvrdíte jinými slovy totéž :-).

    Postgres sám pro svůj provoz časovou zónu nepoužívá - její definice se použije pro přepočítávání hodnot TimestampTz, TimeTz - a jinak asi nikde jinde. Starší POSIX systémy vracely aktuální čas a zónu - a jednoduše se přepočetlo do UTC. Dnešní Linux vrací typicky UTC a 0 ofset.

    Časovou zónu mohu specifikovat jak Europe/Prague, tak SEČ nebo SELČ, případně ji mohu specifikovat intervalem. Podle toho, co potřebuji. Může být trochu divné použít SEČ, když platí SELČ, ale také to může mít svůj smysl. U některých zón může docházet ke změně ofsetu, jiné jsou perzistentní. Na druhou stranu - v některých zemích se časová zóna aktualizovala několikrát, a bez historické databáze, pokud bych zobrazoval časy jen v UTC, nebo s přihlédnutím k aktuální hodnotě časové zóny by uživatel mohl dostávat dost nečekané výsledky.

  • 1. 3. 2019 11:00

    Filip Jirsák

    Psal jsem o tom uložení, kde PostgreSQL datové typy s TZ ukládá v UTC, tj. časovou zónu u nich vlastně uloženou nemá, je součástí typu. Pak je ta časová zóna session, což je zóna, ve které PostgreSQL časové údaje zobrazuje – a tam se obvykle používají ty „občanské“ časové zóny, protože v ČR obvykle budu chtít zobrazovat čas v Europe/Prague, tedy automaticky přepínat SEČ a SELČ podle toho, kam daný čas zrovna spadá.

    Ale tím, že je typ definovaný i s časovou zónou, nic nebrání aplikaci přepnout si session do UTC a převod do lokální časové zóny dělat v aplikaci. Ona si to tedy aplikace může převádět i tehdy, když používá pro čtení dat z databáze lokální časovou zónu, nebo libovolnou jinou – když mám u času uvedenou časovou zónu, můžu si to libovolně převádět.

    Jak tady někdo argumentoval, že radši použije datový typ bez TZ a bude to ukládat v UTC – pak ale s touhle konvencí musí počítat všechny aplikace, které s tou databází pracují, včetně různých ad-hoc skriptů. Přitom tahle konvence je přímo zapečená v datovém typu s TZ.

    Ono to pojmenování „with timezone“ není úplně šťastné, spíš by se to mělo jmenovat třeba „timestamp in UTC“. Ale SQL databáze už jsou holt dost staré a občas si tedy nesou historickou zátěž.

  • 1. 3. 2019 13:13

    Rudolf Dvořáček (neregistrovaný) 213.123.54.---

    Presne z tohoto duvodu zapisuji data (je jedno jestli do databaze, do souboru nebo cernou kridou do komina) vzdy v UTC, pokud zrovna nepotrebuji informaci o casove zone, v jake byla data vytvorena.

  • 28. 2. 2019 21:33

    Pavel Stěhule

    Pokud si v aplikaci zajistíte převod do UTC, tak pak TimestampTz nepotřebujete. Pokud ale ne, tak TimestampTz je nutný, právě třeba kvůli přechodům standardní, letním časem. Hodně také záleží, odkud berete čas. Pokud používám transakční čas (z db serveru), tak k žádným problémům nemůže dojít. Pokud používám klientské časy, tak se musím spolehnout (ve všech případech), že mi poskytne správný čas (případně správnou zónu).

    K nastavení zón - právě u TimestampTz vůbec nikdo nečeká, že mají klienti nastavené STEJNÉ zóny. Jediným požadavkem je, aby měli nastavené zóny SPRÁVNĚ. Pokud správně nastavené nebudou, tak to, samozřejmě, fungovat nemůže. Pokud pracujete čistě s UTC, tak ok, ale pak veškerou práci s převody času si musíte ošetřit sám.

  • 1. 3. 2019 0:40

    VS

    U "timestamp with time zone" jsme se u jedné aplikace trochu spálili, nebo jsme spíš narazili na ne zcela jednoznačnou dokumentaci. Potřebovali jsme ukládat časové značky, které dodávala klientská strana, a zároveň ke každé značce timezone, kterou také dodal klient. Z dokumentace postgresu se nám tehdy zdálo, že přesně toto "timestamp with time zone" - tedy že _ukládá_ timestamp vč. informace o původní TZ, se kterou byl vložen. Ale skončili jsme s tím, že tomu tak není. Jediný rozdíl proti "timestamp without time zone" je, že při výstupu je hodnota přepočtena na TZ nastavenou pro danou session. Nebo je to jinak?

  • 1. 3. 2019 7:48

    Filip Jirsák

    VS: PostgreSQL ukládá typ s časovou zónou vždy v UTC. Informace o tom, která časová zóna byla na vstupu, se tedy ztrácí – pokud chcete vědět, zda daný časový okamžik uživatel zadal jako 1:00 SELČ nebo 0:00 UTC, musíte si tu uživatelem zadanou zónu poznamenat jinak. Ale díky tomu, že u uloženého času známe časovou zónu, může se kdykoli čas převést do jakékoli jiné časové zóny. Naproti tomu typy bez timezone se interpretují tak, že jde o čas v lokální časové zóně dané session. Takže přesunete server jinam a nastavíte místní časovou zónu, a hned máte všechny časy jinak.

    Z hlediska dat uložených v záznamech mezi těmi tedy není rozdíl, rozdíl je v tom typu – jeden říká, že uložený čas je i s časovou zónou a při načtení se jen rozhodne, do které časové zóny ho chcete konvertovat. Druhý typ je uložený bez časové zóny a při načítání se nerozhoduje, kam se konvertuje (tj. přepočítá), ale do které časové zóny se vloží – tj. čas se nezmění, akorát se dodatečně rozhodneme, zda je to čas v Praze, v Londýně nebo v New Yorku. Je to podobné, jako kdybyste ukládal měnu – a buď byste jí vždy ukládal v USD a při vstupu a výstupu přepočítával na jiné měny správným kurzem (akorát by všechny kurzy musely být neměnné), nebo byste měnu ukládal jenom jako číslo, a teprve při načtení byste se rozhodl, zda to budete považovat za USD, CZK nebo ITL.

  • 1. 3. 2019 8:07

    Pavel Stěhule

    Analogie s měnami mne nenapadla, ale je dobrá - je to hodně podobné. Když bych použil tuto analogii, tak Posgres obsahuje databázi kurzů, která se průběžně aktualizuje.

  • 1. 3. 2019 3:17

    Miroslav Šilhavý

    To narážíte na další častý nešvar programátorů. Nepoužívají transakce a ani nevědí o neměnnosti now() v rámci transakce. V důsledku toho programátor používá místní čas aplikačního serveru a cpe timestampy z aplikace do sekvence příkazů. Tím si zajistí neměnnost timestampu, bohužel je to pak ale závislé na tom, aby místní časy byly nastaveny stejně a správně. Bohužel, oblíbené databázové abstrakční frameworky programátora vysloveně vedou k tomu, aby nepoužíval transakce a k tomu, že mu "jednoduchým" způsobem a hlavně bez přemýšlení časové údaje předžvýkají.

  • 1. 3. 2019 4:59

    kvr kvr

    Kdy použít transakce a kam vůbec data ukládat, nechám na programátorech, kteří lépe rozumí business logice dané funkcionality.

    S tím now() - jaký je rozdíl mezi databázovým now() a aplikačním serverem? Transakce se může zablokovat v různé fázi a stejně nemám žádnou garanci, který timestamp se kdy použije, klidně to může skončit vložením dvou řádků s klesajícím timestamp. Používat now(), ať už z databáze nebo aplikačního serveru k něčemu kritickému je v principu nesmysl. Je-li to nějaký klíčový identifikátor, stejně pochází obvykle zcela odjinud, například z koncového IoT zařízení, tedy nejen mimo transakci, ale zcela asynchronní vstup z hlediska aplikačního serveru i databáze.

    Nevím, které databázové abstrakční frameworky programátora vedou k nepoužívání transakce, EJB, Spring i Hibernate při zápisu transakce přímo vyžadují.

  • 1. 3. 2019 11:11

    Filip Jirsák

    Každá rozumná databáze vrací v now() čas začátku transakce, tj. v průběhu transakce se ta hodnota nemění a všechny záznamy vložené/aktua­lizované během té transakce budou mít stejný časový údaj.

  • 1. 3. 2019 18:09

    kvr kvr

    To je v pořádku, ale nic to nemění na tom, že když do timestamp nacpu now() z databáze nebo now() z aplikačního serveru, význam bude úplně stejný. Stejně jako nemám garantovaný přesný čas napříč clusterem aplikace, tak nemám ani garantován stejný čas napříč clusterem databáze. Dokonce ani na jedné mašině nemám garantováno, že transakce A, která startovala před transakcí B, zapíše (pokusí se) data do tabulky před transakcí B. Takže nějaká přidaná hodnota v DB rozhodně není.

    Podstatné je, že má-li ten timestamp nějaký business význam, je nevhodné jej nastavovat na DB úrovni. V případě migrace na jinou platformu to pak znamená práci navíc. Aplikační logika by měla být postavená jak nad databázi, tak nad DAO vrstvou. Doba, kdy databáze byla jádro vývojařské platformy, je už 20 let pryč.

    PS: Ohledně "každou rozumnou dabází, která vrací v now()...". Funkce now() není ani SQL standardem a není zdaleka všude podporována. To, že je SQL standard 20-30 let za praktickýma implementacema, jen podtrhuje výše uvedené.

  • 1. 3. 2019 20:55

    Pavel Stěhule

    Na Postgresu a skoro všude lze použít CURRENT_TIMESTAMP, které je ANSI.

    Drtivá většina (netvrdím, že všechny aplikace) nad relační db běží v režimu single server nebo master/slave. Vůči jedné db můžete mít postaveno více klientů, aplikačních serverů (tam je poměrně běžné horizontální škálování). Tudíž db je poslední bod, a její čas mohu použít jako autorativní.

    Další výhodou je transakční chování - a konec konců konzistence funkčnosti v rámci db.

  • 1. 3. 2019 5:24

    Pavel Stěhule

    Občas znalosti programátorů jsou tragický - software se nebrání, a většinou není vidět, jak je špatně naprogramovaný. Teď jsem potkal kluky, kteří po letech programování zjistili, že v databázi jsou race condition ošetřené, a že IF NOT EXISTS vůbec nic neřeší.

  • 1. 3. 2019 5:41

    Miroslav Šilhavý

    Bohužel dobrá výuka programování na středních školách chybí, ale to je právě ta doba, kdy se programátoři "rodí". Ti jsou pak odkázaní na samonauku. A přiznejme si, co najdou: příklady v PHP a MySQL, a pokud někde narazí na BEGIN, není pro ně důležité ani načíst si, co to to znamená.

    Pak si také myslím, že svoji roli hraje úroveň dokumentace. MySQL má dokumentaci pochopitelně kvalitní a kompletní, ale málo edukativní. PostgreSQL má dokumentaci mnohem lepší. Bohužel, začínající programátoři se k postgresu nedostanou, a ve chvíli, kdy se dostanou, mívají už většinou zkreslené představy o tom, co mají od databáze očekávat.

    Kapitolou samou o sobě je naučit programátory vnímání doby trvání příkazů a transakcí. Programátorovi přijde půl sekundy jako vynikající výsledek, pár sekund jako přijatelný. Vysvětlit mu, že ale s nárustem dat a s nárustem provozu a paralelních požadavků najednou latence naskáčou i na desítky sekund, je nadlidská práce. Mají svoji hlavu a "zkušenost" s provozem zájmových stránek sběratelů korkových zátek, a nevymluvíte jim, že může existovat i trochu jiný typ provozu a zátěže.

    Smutné je, že namísto toho, aby se programátoři zaměřovali na databáze, které jsou právě určeny k nejlepšímu zpracování dat, tráví se spousta času na vymýšlení různých aplikačních cache. Podívejte se do pár nejběžnějších frameworků. Databázové třídy jsou univerzální od SQLite, přes MySQL a Postgres, až po MS SQL a Oracle. V takovém případě je jakákoliv optimalizace opravdu prakticky nemožná a nezbývá, než data zpracovat v aplikaci. Programátor pak přijde k hotovému, a v podstatě už ani nemá možnost se práci s databází naučit.

  • 1. 3. 2019 6:35

    Pavel Stěhule

    Programátor má hlavně dost práce se naučit ten framework - docela bych si tipnul, že naučit se Java API, které maskuje SQL a databáze, je výrazně náročnější než se naučit databáze.

    V korporátu si skoro nejde nevšimnout, že zatímco admini jsou proškolení od pat až po špičku nosu, tak programátoři bývají netknutí znalostmi ohledně databází. Průser je, že se špatně implementovanou aplikací, se špatným návrhem sebelepší admin neudělá vůbec nic.

  • 1. 3. 2019 6:57

    Miroslav Šilhavý

    V korporátu si skoro nejde nevšimnout, že zatímco admini jsou proškolení od pat až po špičku nosu, tak programátoři bývají netknutí znalostmi ohledně databází. Průser je, že se špatně implementovanou aplikací, se špatným návrhem sebelepší admin neudělá vůbec nic.

    Přesně tak! S tím bojuju co chvíli. Vedení (tím myslím i vedoucí programátorů) razí tezi, že je potřeba rychle něco udělat a na optimalizace bude čas později. A nedá se jim vysvětlit, že když navrhnou špatně architekturu, nejde už zoptimalizovat vůbec nic a musí se začít od začátku.

    Ale zkuste si v praxi prosadit projekt, kde se nejdřív navrhne databáze a datový model, a terpve pak to ostatní.

    Také vidím to, že admini jsou v podstatě lepší databázoví programátoři, než programátoři samotní. Ale řekněte mi, jaké to má řešení v praxi?

  • 1. 3. 2019 7:28

    Pavel Stěhule

    Pokud není kompetentní management, tak se toho moc dělat nedá. Na to, abych věděl, že něco nevím, tak už toho musím vědět docela hodně. Něco se dá zvládnout interní osvětou - pokud člověk nenarazí na úplný blbce, tak většinou lidi mají chuť dělat věci dobře - ale trvá to, a je to nikdy nekončící proces.

    U těch adminů je to jednoduché - zkuste něco nastavit na Oracle bez důkladnějšího školení - navíc vyjma začátečníků si žádný admin nelajsne na Oracle udělat něco, na co nedostal školení.

    Oproti tomu připojit se k Oracle skrz JDBC, nebo skrz Hibernate serializovat objekt je jednoduché - a dokud máte dost RAM, tak se to moc nebrání. Výkonnostní problémy se objevují tak po dvou-třech letech provozu, a tou dobou, už je všechno tak zafixované, a nic se nedá změnit, takže se vše bere jako nutné zlo, případně se problémy utlučou hw, nebo také neutlučou.

    Je to taková tragikomedie - jak dobře navrhovat db aplikace se ví tak 20-30 let - a žádný velký výsledky to nemá.

  • 1. 3. 2019 13:03

    jviki

    Škoda, že se diskuse zvrhla k stěžování si na ducha doby a neschopnost programátorů... NOW() je taky poněkud mimo téma, byť zrádná věc. Zpět k tématu, někde se opakuji, ale popsal jsem to trochu konkrétněji.

    Jde mi o to, že timestamp with time zone dává nečekané výsledky, což právě poněkud rozporuji s článkem. Podle názvu toho typu by někdo jako já čekal, že se spolu s časovou značkou uloží také časová zóna. Ale ono ne. Naopak, dojde k neočekávané konverzi při INSERTu a potom k neočekávané konverzi při SELECTu. Takto mohu dostat až 3 různé časy. Na to jsem alespoň narazil a trápil se s tím mnoho hodin. A i když si přečtete dokumentaci, tak je tu stále jakýsi nejistý prostor pro skryté bugy.

    Proto upřednostňuji timestamp without time zone - tedy v zásadě normalizovanou podobu časového údaje (v UTC). Pokud si potřebuji pamatovat i původní zónu, musím si ji stejně uložit zvlášť (timestamp with time zone mi nepomůže).

    Pokud ladím, tak vím, že všechny značky jsou v UTC. Tj deterministické a poměrně pohodlné pro srovnávání. Všechny SELECTy i INSERTy se *vždy* chovají přesně stejně.

    Mimochodem, pokud budu provádět INSERT v okamžiku změny času letní/zimní a použiju timestamp with time zone, co když aplikace uvidí ještě letní čas, ale databáze už zimní? Může se stát něco dosti podivného... (Možná se ale mýlím, rád bych.) Možná také stačí, aby databázový server měl jiný popis časových zón (tzdata) a už něco takového může také hrozit. Se značkami v UTC je to bezpečné. Srovnal bych to s používáním globálních proměnných v aplikacích, prostě eliminace side efektů.

    Pokud aplikace provádí SELECT, tak u timestamp without time zone přesně ví, že výsledek je v UTC. Nemusím řešit nic jako session time zone atp. Aplikační server ve smyslu obsluhy klienta stejně nic jako časovou zónu nemá (fyzicky může být na libovolném konci světa, ale to je podle mě irelevantní), jakou by tedy měl mít session time zone? (Ne všechny operace probíhají v kontextu konkrétního klienta.)

    Pokud chci čas i v původní zóně (tady nemám zkušenost s žádným use case a žádný mě ani nenapadá), explicitně přepočítám podle sloupce, kde mám původní zónu uloženou zvlášť (timestamp with time zone mi opět nepomůže, spíš trochu zkomplikuje).

    Až v okamžiku zobrazení výsledku uživateli, kdy znám jeho lokální zónu, tak teprve provádím konverzi, jak jsem už psal.

  • 1. 3. 2019 13:38

    Filip Jirsák

    jviki: Popisujete to, že údaje do databáze ukládáte v UTC. Což je přesně to, co dělá timestamp with time zone. Jediný rozdíl v databázi je v tom, že vy to musíte říct každému, kdo s těmi daty chce pracovat („pozor, všechny časy jsou v UTC“), zatímco datový typ timestamp with time zone tuhle informaci nese už sám v sobě. Druhý rozdíl je pak v aplikacích, které s daty pracují – ve vašem případě musí všechny počítat s tím, že data jsou v UTC. Při použití TZ se aplikace může rozhodnout, zda bude používat UTC nebo libovolnou jinou časovou zónu.

    Mimochodem, tenhle přístup „časovým zónám moc nerozumíme, tak je radši nebudeme používat“, vede k problémům mezi letním a standardním časem (když se podíváte na zimní data v létě, máte je o hodinu posunutá), případně v říjnu přijdete o hodinu dat, protože pokud používáte lokální čas bez časové zóny a používáte lokální zónu se změnou času (např. Europe/Prague), ten interval jedné hodiny v říjnu prostě může být SEČ i SELČ a aplikace si vybere jedno z toho. Když jako lokální zónu použijete UTC, těmhle problémům se sice vyhnete, ale problém, že s časem pracují lidé, kteří tomu nerozumí, tudíž to nejspíš dělají špatně, přetrvává.

    Mimochodem, pokud budu provádět INSERT v okamžiku změny času letní/zimní a použiju timestamp with time zone, co když aplikace uvidí ještě letní čas, ale databáze už zimní? Může se stát něco dosti podivného... (Možná se ale mýlím, rád bych.)
    Ano, mýlíte se. Neexistuje žádné „vidět zimní/letní čas“. Čas máte v nějaké časové zóně, která je definovaná posunem od UTC. Když se používá „občanská“ zóna, třeba Europe/Prague, podle toho časového údaje se rozhodne, zda je to SELČ nebo SEČ – ale tohle rozhodnutí se provádí na vstupu, např. když ten údaj zadá uživatel, dál už aplikace pracuje s tím SELČ/SEČ. Pokud někdo pracuje bez časových zón a doplňuje je tam až dodatečně, způsobuje mu to samozřejmě ty problémy, které popisujete – např. proto, že ta jedna hodina v říjnu může znamenat dvě různé časové zóny.

    Pokud aplikace provádí SELECT, tak u timestamp without time zone přesně ví, že výsledek je v UTC.
    To je právě ten příklad postranního efektu – aplikace musí někde z venku získat informaci, že údaje v databázi jsou v UTC. Když použijete typ TZ, aplikace dostane časy v takové zóně, v jaké si řekne. Klidně to může být UTC.

    Nemusím řešit nic jako session time zone atp.
    Vy to neřešíte prostředky, které jsou k tomu určené. Místo toho to vyřešíte tím, že to napíšete do nějaké dokumentace, a každý, kdo s tou databází pracuje, na to pak musí myslet.

    Aplikační server ve smyslu obsluhy klienta stejně nic jako časovou zónu nemá, jakou by tedy měl mít session time zone?
    Třeba UTC, když chce pracovat v UTC. Vtip je v tom, že si o tom rozhodne ten server a vždycky dostane správná data, a nemusí vůbec řešit, jakou časovou zónu používají jiné aplikace přistupující k té stejné databázi.

    Pokud si potřebuji pamatovat i původní zónu, musím si ji stejně uložit zvlášť (timestamp with time zone mi nepomůže).
    Ano, to je pravda, nicméně takový požadavek bude velmi výjimečný. Lidé obvykle chtějí vidět datum a čas v jedné časové zóně, kterou si zvolí.

  • 1. 3. 2019 13:47

    Pavel Stěhule

    Jednak běžný timestamp vůbec nebere v potaz časovou zónu, jak při zápisu, tak při zobrazení

    postgres=# select '20190202 10:10:10+10'::timestamp;
    ┌─────────────────────┐
    │      timestamp      │
    ╞═════════════════════╡
    │ 2019-02-02 10:10:10 │
    └─────────────────────┘
    (1 row)

    Takže with time zone je ve smyslu zpracování, nikoliv uložení. Jde jen o to správně nastavit timezone.Interně jsou data uložená v UTC, což je totéž co děláte vy. Pokud chcete zobrazit uložená data do některé zóny, tak stačí nastavit timezone, případně použít konverzní operátor AT TIME ZONE.

    Je tam jeden základní předpoklad - správné nastavení timezone. Pak všechno funguje automaticky a o nic se nemusíte starat.

    Je tam vidět historie. Perfektně to sedí pro tlusté klienty, kde timezone nastavím v connection stringu, případně se vytáhne default z klienta. U tenkého klienta už to musím dělat explicitně příkazem SET.

    Dovedu si představit, že vývojáři, kteří nemají jinou zkušenost než s programováním tenkého klienta mohou mít problém to pochopit. Vše se ale točí kolem korektního nastavení proměnné timezone.

  • 11. 3. 2019 22:16

    bez přezdívky

    Zcela souhlasim s jviki.
    Vsechno ulozit jako UTC BEZ timezone.
    (pokud potrebuji znat timezone, je to v dalsim sloupecku napr. "Europe/London" ale to je potreba jen velice vyjimecne)

    Vyskledkem je pracovni prostredi, ve kterem je minimum problemu:
    - vzdy do DB ukladam v UTC (konverze nutna na urovni aplikace pred zapisem do DB)
    - vzdy z DB ctu v UTC (konverze vetsinou nutna nma urovni aplikace po cteni z DB)
    - jednotlive DB jsou jednoduse propojitelne
    - kazda aplikace je nucena ke konverzi timezone pred zapisem a po precteni

    Ano, da se pracovat i s hodnotami "with timezone" jenze je to podle me v realnem svete k nicemu. Proc?
    Protoze v realnem svete je to trochu jinak.

    Budto timezone vubec k nicemu neni (to je 99% pripadu) takze ukladat den_vydani_clanku v Nemecku v Europe/Berlin a v anglii v Europe/London je vam k nicemu. Proc? Protoze to je jedna jasne urcena hodnota v UTC - tak to proste stejne ulozite v UTC. A pokud to chcete zobrazit japoncum, zkonvertujete z UTC do Asia/Tokyo a je to hotovo.

    Jsou situace, kdy CHCETE znat v jake casove zone ma to datum byt zobrazovano. Napr. u letenek se zobrazuje cas odletu / priletu vzdy podle polohy letiste. Potom je dobre mit ulozeny cas v UTC a zaroven budto nejakym slozitejsim kusem kodu odvodit co je na tom kterem miste za casovou zonu (coz by slo) anebo se proste udela dalsi sloupecek a casova zona se da tam. Aplikace potom musi precist oba sloupce a formatovat podle toho kde je info o casove zone.

    Pro pokrocilejsi use case si obcas pomaham pomocnymi tabulkami. Nekdy se hodi tabulky s cisly tydnu (mnoho zemi ma mnoho ruznych cislovani tech standardu je snad 8), nekdy se hodi pomocne tabulky i pro groupovani po dnech nebo tydnech (napr. kvuli statistikam v ruznych zemich).