sql >> Databáze >  >> RDS >> PostgreSQL

Reprezentace dat, časů a intervalů v PostgreSQL

PostgreSQL přichází s řadou vestavěných datových typů souvisejících s datem a časem. Proč byste je měli používat přes řetězce nebo celá čísla? Na co si dát při jejich používání pozor? Přečtěte si další informace o tom, jak efektivně pracovat s těmito datovými typy v Postgresu.

Celá spousta typů

Standard SQL, norma ISO 8601, vestavěný katalog PostgreSQL a zpětná kompatibilita společně definují nepřeberné množství překrývajících se, přizpůsobitelných datových typů a konvencí souvisejících s datem/časem, které jsou přinejlepším matoucí. Tento zmatek se obvykle přelévá do kódu ovladače databáze, kódu aplikace, rutin SQL a vede k jemným chybám, které se obtížně ladí.

Na druhou stranu použití nativních vestavěných typů zjednodušuje příkazy SQL a usnadňuje jejich čtení a zápis, a v důsledku toho je méně náchylný k chybám. Použití, řekněme celých čísel (počet sekund od epochy) k vyjádření času, vede k nepraktickým výrazům SQL a další kód aplikace.

Díky výhodám nativních typů se vyplatí definovat sadu nepříliš bolestivých pravidel a vynutit je v celé aplikační a operační základně kódu. Zde je jedna taková sada, která by měla poskytovat rozumné výchozí hodnoty a rozumný výchozí bod pro další přizpůsobení, pokud je to nutné.

Typy

Používejte pouze následující 3 typy (ačkoli jich je k dispozici mnoho):

  • datum - konkrétní datum, bez času
  • časové razítko - konkrétní datum a čas s mikrosekundovým rozlišením
  • interval - časový interval s mikrosekundovým rozlišením

Tyto tři typy dohromady by měly podporovat většinu případů použití aplikací. Pokud nemáte specifické potřeby (jako je úspora úložiště), důrazně se doporučuje držet se pouze těchto typů.

Datum představuje datum bez času a je v praxi docela užitečný (viz příklady níže). Typ časového razítka je varianta, která obsahuje informace o časovém pásmu – bez informací o časovém pásmu je prostě příliš mnoho proměnných, které mohou ovlivnit interpretaci a extrakci hodnoty. Konečně interval představuje časové intervaly od mikrosekund až po miliony let.

Doslovné řetězce

Používejte pouze následující doslovné reprezentace a použijte operátor cast ke snížení výřečnosti, aniž byste obětovali čitelnost:

  • '2012-12-25'::date - ISO 8601
  • '2012-12-25 13:04:05.123-08:00'::timestamptz - ISO 8601
  • '1 month 3 days'::interval - Tradiční formát Postgres pro intervalový vstup

Vynechání časového pásma vás ponechá na milost a nemilost nastavení časového pásma serveru Postgres, konfigurace časového pásma, kterou lze nastavit na úrovni databáze, relace, na úrovni role nebo v připojovacím řetězci, nastavení časového pásma klientského počítače a více takových faktorů.

Při dotazování z kódu aplikace převeďte typy intervalů na vhodnou jednotku (jako jsou dny nebo sekundy) pomocí extract funkci a načíst hodnotu jako celé číslo nebo skutečnou hodnotu.

Konfigurace a další nastavení

  • Neměňte výchozí nastavení konfigurace GUC DateStyle ,TimeZone a lc_time .
  • Nenastavujte ani nepoužívejte proměnné prostředí PGDATESTYLE a PGTZ .
  • Nepoužívejte SET [SESSION|LOCAL] TIME ZONE ... .
  • Pokud můžete, nastavte systémové časové pásmo na UTC na počítači, na kterém běží server Postgres, a také na všech počítačích s kódem aplikace, které se k němu připojují.
  • Ověřte, že se váš databázový ovladač (jako konektor JDBC nebo ovladač Godatabase/sql) chová rozumně, když klient běží v jedné časové zóně a server v jiné. Ujistěte se, že funguje správně, když je TimeZone platné bez UTC parametr je součástí připojovacího řetězce.

Na závěr si uvědomte, že toto vše jsou pouze pokyny a lze je upravit tak, aby vyhovovaly vašim potřebám – ale ujistěte se, že nejprve prozkoumáte důsledky toho.

Nativní typy a operátory

Jak tedy přesně pomáhá používání nativních typů při zjednodušení kódu SQL? Zde je několik příkladů.

Typ data

Hodnoty datum typ lze odečíst a získat interval mezi nimi. Můžete také přidat celé číslo dní k datu částic nebo přidat interval k datu, abyste získali timestamptz :

-- 10 days from now (outputs 2020-07-26)
SELECT now()::date + 10;
 
-- 10 days from now (outputs 2020-07-26 04:44:30.568847+00)
SELECT now() + '10 days'::interval;

-- days till christmas (outputs 161 days 14:06:26.759466)
SELECT '2020-12-25'::date - now();

-- the 10 longest courses
  SELECT name, end_date - start_date AS duration
    FROM courses
ORDER BY end_date - start_date DESC
   LIMIT 10;

Hodnoty těchto typů jsou srovnatelné, a proto můžete poslední dotaz objednat do end_date - start_date , který má typ intervalu . Zde je další příklad:

-- certificates expiring within the next 7 days
SELECT name
  FROM certificates
 WHERE expiry_date BETWEEN now() AND now() + '7 days'::interval;

Typ časového razítka

Hodnoty typu timestamptz lze také odečíst (a získat interval ),přidáno (do intervalu dát další časové razítko ) a porovnali.

-- difference of timestamps gives an interval
SELECT password_last_modified - created_at AS password_age
  FROM users;

-- can also use the age() function
SELECT age(password_last_modified, created_at) AS password_age
  FROM users;

V tomto tématu si všimněte, že existují 3 různé vestavěné funkce, které vracejí různé hodnoty „aktuálního časového razítka“. Ve skutečnosti vracejí různé věci:

-- transaction_timestamp() returns the timestampsz of the start of current transaction
-- outputs 2020-07-16 05:09:32.677409+00
SELECT transaction_timestamp();

-- statement_timestamp() returns the timestamptz of the start of the current statement
SELECT statement_timestamp();

-- clock_timestamp() returns the timestamptz of the system clock
SELECT clock_timestamp();

Existují také aliasy pro tyto funkce:

-- now() actually returns the start of the current transaction, which means it
-- does not change during the transaction
SELECT now(), transaction_timestamp();

-- transaction timestamp is also returned by these keyword-style constructs
SELECT CURRENT_DATE, CURRENT_TIMESTAMP, transaction_timestamp();

Typy intervalů

Hodnoty typu intervalu lze použít jako datové typy sloupců, lze je vzájemně porovnávat a lze je přidávat (a odečítat od) časových razítek a dat. Zde je několik příkladů:

-- interval-typed values can be stored and compared 
  SELECT num
    FROM passports
   WHERE valid_for > '10 years'::interval
ORDER BY valid_for DESC;

-- you can multiply them by numbers (outputs 4 years)
SELECT 4 * '1 year'::interval;

-- you can divide them by numbers (outputs 3 mons)
SELECT '1 year'::interval / 4;

-- you can add and subtract them (outputs 1 year 1 mon 6 days)
SELECT '1 year'::interval + '1.2 months'::interval;

Další funkce a konstrukce

PostgreSQL také přichází s několika užitečnými funkcemi a konstrukcemi, které lze použít k manipulaci s hodnotami těchto typů.

Extrahovat

Funkci extraktu lze použít k načtení zadané části z dané hodnoty, jako je měsíc z data. Úplný seznam částí, které lze extrahovat, je zdokumentován zde. Zde je několik užitečných a nezřejmých příkladů:

-- years from an interval (outputs 2)
SELECT extract(YEARS FROM '1.5 years 6 months'::interval);

-- day of the week (0=Sun .. 6=Sat) from timestamp (outputs 4)
SELECT extract(DOW FROM now());

-- day of the week (1=Mon .. 7=Sun) from timestamp (outputs 4)
SELECT extract(ISODOW FROM now());

-- convert interval to seconds (outputs 86400)
SELECT extract(EPOCH FROM '1 day'::interval);

Poslední příklad je zvláště užitečný v dotazech prováděných aplikacemi, protože pro aplikace může být snazší zpracovat interval jako hodnotu s pohyblivou řádovou čárkou počtu sekund/minut/dní/atd.

Převod časového pásma

K dispozici je také užitečná funkce pro vyjádření timestamptz v jiném časovém pásmu. Typicky by to bylo provedeno v kódu aplikace – je snazší to otestovat a snižuje závislost na databázi časového pásma, na kterou bude Postgresserver odkazovat. Přesto to může být občas užitečné:

-- convert timestamps to a different time zone
SELECT timezone('Europe/Helsinki', now());

-- same as before, but this one is a SQL standard
SELECT now() AT TIME ZONE 'Europe/Helsinki';

Převod na text a z textu

Funkce to_char (docs) umí převádět data, časová razítka a intervaly na text na základě formátovacího řetězce – Postgres ekvivalent klasické C funkce strftime .

-- outputs Thu, 16th July
SELECT to_char(now(), 'Dy, DDth Month');

-- outputs 01 06 00 12 00 00
SELECT to_char('1.5 years'::interval, 'YY MM DD HH MI SS');

Pro převod z textu na data použijte to_date a pro převod textu na časová razítka použijte to_timestamp . Všimněte si, že pokud používáte formuláře, které byly uvedeny na začátku tohoto příspěvku, můžete místo toho použít operátory cast.

-- outputs 2000-12-25 15:42:50+00
SELECT to_timestamp('2000.12.25.15.42.50', 'YYYY.MM.DD.HH24.MI.SS');

-- outputs 2000-12-25
SELECT to_date('2000.12.25.15.42.50', 'YYYY.MM.DD');

Úplný seznam vzorů formátovacích řetězců naleznete v dokumentaci.

Pro jednoduché případy je nejlepší použít tyto funkce. Pro složitější analýzu nebo formátování je lepší spolehnout se na kód aplikace, který může být (pravděpodobně) lépe testován na jednotku.

Propojení s kódem aplikace

Někdy není vhodné předávat hodnoty data/časové razítko/interval do kódu aplikace az něj, zvláště když se používají vázané parametry. Například je obvykle vhodnější předat interval jako celé číslo dnů (nebo hodin nebo minut) než ve formátu řetězce. Je také snazší číst v intervalu jako celé číslo/počet dní s plovoucí desetinnou čárkou (nebo hodin, nebo minut atd.).

make_interval funkci lze použít k vytvoření intervalové hodnoty z celého počtu hodnot komponent (viz dokumenty zde). to_timestamp Funkce, kterou jsme viděli dříve, má jinou formu, která může vytvořit hodnotu atimestamptz z času unixové epochy.

-- pass the interval as number of days from the application code
SELECT name FROM courses WHERE duration <= make_interval(days => $1);

-- pass timestamptz as unix epoch (number of seconds from 1-Jan-1970)
SELECT id FROM events WHERE logged_at >= to_timestamp($1);

-- return interval as number of days (with a fractional part)
SELECT extract(EPOCH FROM duration) / 60 / 60 / 24;

  1. Instalace balíčků RODBC/ROracle na OS X Mavericks

  2. Úvod do ML{.NET}

  3. Výkon SQL Serveru TOP IO Query -2

  4. Jak mohu zjistit, zda je v Codeigniter úspěšný dotaz na vytvoření, aktualizaci nebo odstranění