SlideShare une entreprise Scribd logo
1  sur  7
Télécharger pour lire hors ligne
Publikováno pod licencí BSD © Pavel Stěhule 2012 CSPUG pavel.stehule@gmail.com http://www.cspug.cz http://www.postgres.cz http://groups.google.com/group/postgresql-cz?hl=cs


                                                                                       Export a import dat                                                                                    Mělo by platit6:
PostgreSQL ve verzi 9.2 a 9.3                                                                                                                                                                  shared_buffers + 2 * work_mem * max_connection <= 2/3 RAM
                                                                                                                                                                                               shared_buffers + 2 * maintenance_work_mem <= 1/2 RAM
    createdb jménodb
                                                                                       Příkaz COPY                                                                                             max_connections <= 10 * (počet_CPU)

   Vytvoří novou databázi                                                                 Pomocí příkazu COPY můžeme číst a zapisovat soubory na serveru (pouze superuser) nebo               checkpoint_segments udává počet 16MB segmentů transakčního logu po jejichž naplnění
                                                                                          číst ze stdin a zapisovat na stdout. Podobný příkaz copy v psql umožňuje číst                      dojde k tzv CHECKPONTu (databáze zapíše modifikované datové stránky (cache) na disk).
    dropdb jménodb                                                                        a zapisovat soubory na klientském počítači.
                                                                                                                                                                                               checkpoint_segments = 32
   odstraní existující databázi                                                           Export tabulky zaměstnanci do CSV souboru
                                                                                                                                                                                              Po CHECKPOINTu lze zahodit transakční logy vztažené k času před CHECKPOINTem. Za
    psql jménodb                                                                           COPY zamestnanci TO '/tmp/zam.csv'                                                                 optimální frekvenci CHECKPOINTů se považuje 5 – 15 min. Obvykle se zvyšuje na 32 až
                                                                                              CSV HEADER                                                                                      2567 (pro vytížené servery).
   spustí SQL konzole                                                                         DELIMITER ';' FORCE QUOTE *;
                                                                                                                                                                                               wal_buffers = 256kB
    pg_dump jménodb > jméno_souboru                                                       Import tabulky zaměstnanci z domovského adresáře uživatele (v konzoli)
                                                                                                                                                                                              Velikost bufferu transakčního logu by měl být větší než je velikost běžné transakce – pak se
   Vytvoří zálohu databáze                                                                 copy zamestnanci from ~/zamestnanci.dta
                                                                                                                                                                                              provede pouze jeden zápis do transakčního logu během transakce (256kB..1MB). Aktuálně
                                                                                                                                                                                              výchozí hodnotou je -1, což znamená zhruba 3% shared_buffers (max 16MB).
SQL konzole – psql                                                                     pg_dump – zajímavé parametry                                                                            listen_addresses = '*'
   Umožní zadání SQL příkazu a zobrazí jeho výsledek.                                     Příkaz pg_dump slouží k jednoduchému zálohování databáze 2.                                         A pro vzdálený přístup povolit TCP

                                                                                          -f                      specifikuje cílový soubor
Přehled důležitých příkazů                                                                -a                      exportuje pouze data                                                     SQL
                                                                                          -s                      exportuje pouze definice
   Každý příkaz začíná zpětným lomítkem “” a není ukončen středníkem.
                                                                                          -c                      odstraní objekty před jejich importem
                                                                                                                                                                                              Nejdůležitějším SQL příkazem je příkaz SELECT. Při zápisu je nutné dodržovat pořadí
   c jménodb                     přepnutí do jiné databáze                               -C                      vloží příkaz pro vytvoření nové databáze
                                                                                                                                                                                              jednotlivých klauzulí:
   l                             zobrazí seznamu databází                                -t                      exportuje pouze jmenovanou tabulku
   d objekt                      zobrazí popis objektu (tabulky, pohledu)                -T                      neexportuje uvedenou tabulku                                                 SELECT AVG(a.sloupec1), b.sloupec4
                                                                                          --disable-triggers                     během importu blokuje triggery                                   FROM tabulka1 a
   dt+                           zobrazí seznamu tabulek                                                                                                                                              JOIN tabulka2 b
   dv                            zobrazí seznam pohledů                                  --inserts               generuje příkazy INSERT místo COPY                                                   ON a.sloupec1 = b.sloupec2
   df *filtr*                    zobrazí seznam funkcí                                   --Fc      záloha je průběžně komprimovaná s dodatečnými meta informacemi 3                             WHERE b.sloupec3 = 'něco'
   sf funkce                     zobrazí zdrojový kód funkce                                                                                                                                    GROUP BY b.sloupec4
                                                                                                                                                                                                 HAVING AVG(a.sloupec1) > 100
   i                             importuje soubor                                     Základní konfigurace PostgreSQL                                                                           ORDER BY 1
   h SQL                         zobrazí syntaxi SQL příkazu                                                                                                                                    LIMIT 10
   ?                             zobrazí seznam psql příkazů                             Soubor postgresql.conf
   q                             ukončí konzolu
   x                             přepíná řádkové a sloupcové zobrazení                   Po instalaci PostgreSQL je nutné nastavit několik málo konfiguračních parametrů, které           Sjednocení, průnik, rozdíl relací
   timing on                     zapíná měření času zpracování dotazu                    ovlivňují využití operační paměti (výchozí nastavení je zbytečně úsporné).
                                                                                                                                                                                              Pro relace (tabulky) existují operace sjednocení ( UNION), průnik (INTERSECT) a rozdíl
                                                                                           shared_buffers4= 2GB                                                                               (EXCEPT). Častou operací je sjednocení relací – výsledků dvou příkazů SELECT – operace
Konfigurace konzole                                                                                                                                                                           sloučí řádky (a zároveň odstraní případné duplicitní řádky). Podmínkou je stejný počet
                                                                                          velikost paměti pro uložení datových stránek (1/5..1/3 RAM 5)
                                                                                                                                                                                              sloupců a konvertibilní datové typy slučovaných relací.
   Soubor .bashrc                                                                          work_mem = 10MB
    export PAGER=less                                                                                                                                                                         Vybere 10 nejstarších zaměstnanců bez ohledu zdali se jedná o interního nebo externího
    export LESS="-iMSx4 -RSFX -e"                                                         limit paměti pro běžnou manipulaci s daty (10..100MB)                                               zaměstnance:
                                                                                           maintenance_work_mem = 200MB                                                                        SELECT jmeno, prijmeni, vek
   Soubor .psqlrc
                                                                                                                                                                                                  FROM zamestnanci
    pset pager always                                                                    limit paměti pro údržbu (100MB ..)                                                                   UNION8
    pset linestyle unicode                                                                                                                                                                    SELECT jmeno, prijmeni, vek
                                                                                           effective_cache_size = 6GB                                                                              FROM externi_zamestnanci
    pset null 'NULL'
    set FETCH_COUNT 1000                                                                                                                                                                      ORDER BY vek DESC9
    set HISTSIZE 5000                                                                    odhad objemu dat cache (2/3 RAM)                                                                     LIMIT 10;
    timing
                                                                                           max_connections = 100
    set HISTFILE ~/.psql_history-:DBNAME
    set HISTCONTROL ignoredups                                                                                                                                                            CASE
    set PROMPT1 '(%n@%M:%>) [%/] > '
                                                                                          max počet přihlášených uživatelů (často zbytečně vysoké)
    set ON_ERROR_ROLLBACK on                                                                                                                                                                 Konstrukce CASE se používá pro transformace hodnot – zobrazení, bez nutnosti definovat
    set AUTOCOMMIT off1                                                                                                                                                                      vlastní funkce. Existují dva zápisy – první hledá konstantu, v druhém se hledá platný výraz:
                                                                                       2 Příkaz pg_dump nezálohuje uživatele. K tomuto účelu se používá příkaz pg_dumpall
                                                                                         s parametrem -r. Zálohování příkazem pg_dump je vhodné pro databáze do velikosti cca 50GB.
                                                                                         Pro větší databáze je praktičtější použít jiné metody zálohování.                                 6 Jedná se o orientační hodnoty určené pro počáteční konfiguraci “typického použití” databáze.
                                                                                       3 Pro obnovu je nutné použít pg_restore, obnovit lze i každou vybranou tabulku.                     7 Příliš vysoká hodnota může zvýšit dobu obnovy po havárii, kdy se kontroluje transakční log.
                                                                                       4 Nastavení shared_buffers nad 28MB typicky vyžaduje úpravu SHMMAX (na os. Linux)                   8 Při použití UNION ALL nedochází k odstranění duplicitních řádků – což může zrychlit vykonání
1 Doporučeno pro produkci – vynucuje potvrzení změn explicitním COMMITem. Po vypnutí   5 Doporučené hodnoty platí pro tzv dedikovaný server – tj počítač, který je vyhrazen primárně pro     dotazu.
  autocommitu se psql bude chovat podobně jako konzole Oracle.                           provoz databáze.                                                                                  9 Klauzule ORDER BY se aplikuje na výsledek algebraických operací
Publikováno pod licencí BSD © Pavel Stěhule 2012 CSPUG pavel.stehule@gmail.com http://www.cspug.cz http://www.postgres.cz http://groups.google.com/group/postgresql-cz?hl=cs

  SELECT CASE sloupec WHEN 0 THEN 'NE'                                                    Spojení relací10 JOIN                                                                                 SELECT *
                      WHEN 1 THEN 'ANO' END                                                                                                                                                        FROM a,
    FROM tabulka;                                                                                                                                                                                       LATERAL (SELECT *
                                                                                             Příkaz JOIN spojuje relace (tabulky) vedle sebe a to na základě stejných hodnot v jednom                               FROM b
  SELECT CASE WHEN sloupec = 0 THEN 'NE'                                                     nebo více atributu (sloupci). Každé spojení specifikuje dvě relace (spojkou je klíčové slovo                          WHERE a.a > 2 * b.b) x;
              WHEN sloupec = 1 THEN 'ANO' END                                                JOIN) a podmínku, která určuje, jak se tyto relace budou spojovat (zapsanou za klíčovým
     FROM tabulka;                                                                           slovem ON).                                                                                       Pro každou hodnotu vrátí součet všech kladných celých čísel menší rovno této hodnotě:
 V případě, že se nenajde hledaná konstanta a nebo že žádný výraz není pravdivý, tak je                                                                                                         SELECT a, sum(i)
 výsledkem hodnota za klíčovým slovem ELSE – nebo NULL, pokud chybí ELSE.                 Vnitřní spojení relací – INNER JOIN                                                                      FROM a,
                                                                                                                                                                                                        LATERAL generate_series(1, a) g(i)
                                                                                             Nejčastější varianta – do výsledku se zahrnou pouze řádky, které se podařilo dohledat                GROUP BY a
Agregační funkce s definovaným pořadím                                                       v obou relacích (stejné hodnota/hodnoty) se nalezly v obou tabulkách.
                                                                                                                                                                                                  ORDER BY 1;


 Výsledek novějších agregačních funkcí – string_agg, array_agg zavisí na pořadí ve           Zobrazí jméno dítěte a jméno rodiče (zaměstnance) – v případě, že má zaměstnanec více          Analytické (window) funkce
 kterém se zpravovávala agregovaná data. Proto je možné přímo v agregační funkci určit       dětí, tak jeho jméno bude uvedeno opakovaně:
 v jakém pořadí bude agragační funkce načítat hodnoty. Klauzule ORDER BY musí být za          SELECT d.jmeno, d.prijmeni, z.jmeno, z.prijmeni                                                  Analytické funkce se počítají pro každý prvek definované podmnožiny, např. pořadí prvku
 posledním argumentem agregační funkce.                                                          FROM deti d                                                                                   v podmnožině. Na rozdíl od agregačních funkcí se podmnožiny nedefinují klauzulí GROUP
                                                                                                      JOIN zamestnanci z                                                                       BY, ale klauzulí PARTITION hned za voláním analytické funkce (v závorce za klíčovým
 Vrátí seznam zaměstnanců v každém oddělení řazený podle příjmení:                                    ON d.zamestnanec_id = z.id                                                               slovem OVER). Mezi nejčastěji používané analytické funkce bude patřit funkce
  SELECT sekce_id, string_agg(prijmeni, ',' ORDER BY prijmeni)                                                                                                                                 row_number (číslo řádku) nebo ranking (pořadí hodnoty), případně dense_rank
     FROM zamestnanci                                                                     Vnější spojení relací – OUTER JOIN                                                                   a percent_rank.
    GROUP BY sekce_id
                                                                                             Jedná se o rozšíření vnitřního spojení – kromě řádků, které se spárovaly se do výsledku           Pozor – pro analytické funkce nelze použít klauzuli HAVING – filtrování hodnot se řeší
Poddotazy                                                                                    zařadí i nespárované řádky z tabulky nalevo od slova JOIN (LEFT JOIN) nebo napravo                použitím derivované tabulky.
                                                                                             od slova JOIN (RIGHT JOIN). Chybějící hodnoty se nahradí hodnotou NULL.
                                                                                                                                                                                               Následující dotaz vybere deset nejdéle zaměstnaných pracovníků (na základě porovnání
 Příkaz SELECT může obsahovat vnořené příkazy SELECT. Vnořený příkaz SELECT se               Často se používá dohromady s testem na hodnotu NULL – operátorem IS NULL11. Tím se                osobních čísel):
 nazývá poddotaz a vkládá se do oblých závorek. Poddotazy se mohou použít i u dalších        vyberou nespárované řádky – např. pro zobrazení zaměstnanců, kteří nemají děti, lze
 SQL příkazů.                                                                                                                                                                                   SELECT jmeno, prijmeni
                                                                                             použít dotaz:                                                                                         FROM (SELECT rank() OVER (ORDER BY id),
                                                                                                                                                                                                                jmeno, prijmeni
Poddotaz ve WHERE                                                                             SELECT z.jmeno, z.prijmeni
                                                                                                 FROM zamestnanci z
                                                                                                                                                                                                            FROM zamestnanci
                                                                                                                                                                                                           WHERE ukonceni_prac_pomeru IS NULL) s
                                                                                                      LEFT JOIN deti d
 Používá se pro filtrování – následující dotaz zobrazí obce z okresu Benešov:                                                                                                                     WHERE s.rank <= 10
                                                                                                      ON z.id = d.zamestnanec_id
                                                                                                WHERE d.id IS NULL.
  SELECT nazev                                                                                                                                                                                 Zobrazení dvou nejstarších zaměstnanců z každého oddělení:
     FROM obce o
    WHERE o.okres_id = (SELECT id
                           FROM okresy
                                                                                          Použití derivované tabulky                                                                            SELECT jmeno, prijmeni
                                                                                                                                                                                                   FROM (SELECT rank() OVER (PARTITION BY sekce_id
                          WHERE kod = 'BN')                                                                                                                                                                                  ORDER BY vek DESC),
                                                                                             Poddotaz se může objevit i v klauzuli FROM – pak jej označujeme jako derivovaná tabulka 12.                        jmeno, prijmeni
                                                                                             I derivovanou tabulku lze spojovat s běžnými tabulkami (obojí je relací).                                      FROM zamestnanci) s
Korelované poddotazy                                                                                                                                                                              WHERE s.rank <= 2
                                                                                             Následující příklad zobrazí seznam nejstarších zaměstnanců z každého oddělení:
 Poddotaz se může odkazovat na výsledek, který produkuje vnější dotaz.                                                                                                                         Seznam tří nejlépe hodnocených pracovníků z každého oddělení:
                                                                                              SELECT z.jmeno, z.prijmeni
                                                                                                 FROM zamestnanci z                                                                             SELECT jmeno, prijmeni
 Pro každého zaměstnance zobrazí seznam jeho dětí:                                                    JOIN (SELECT sekce_id, MAX(vek) AS vek                                                       FROM (SELECT rank() OVER (PARTITION BY sekce_id
  SELECT jmeno, prijmeni                                                                                       FROM zamestnanci                                                                                              ORDER BY hodnoceni),
         (SELECT string_agg(jmeno, ',')                                                                       GROUP BY sekce_id) s                                                                              jmeno, prijmeni, hodnoceni, sekce_id
             FROM deti d                                                                              ON z.sekce_id = s.sekce_id                                                                            FROM zamestnanci) s
            WHERE d.zamestnanec_id = z.id)                                                           AND z.vek = s.vek                                                                            WHERE s.rank <= 2
     FROM zamestnanci z                                                                                                                                                                           ORDER BY sekce_id, hodnoceni

 Zobrazí zaměstnance, kteří mají děti:                                                    Dotazy s LATERAL relacemi                                                                            O síle analytických funkcí nás může přesvědčit následující příkaz. V tabulce statistics se
                                                                                                                                                                                               ukládají aktuální hodnoty čítačů množství vložených, aktualizovaných a odstraněných
  SELECT jmeno, prijmeni                                                                     Klauzule LATERAL13 umožňuje ke každému záznamu relace X připojit výsledek poddotazu               řádek. Odečet čítačů se provádí každých 5 minut. Následující dotaz zobrazí počet
     FROM zamestanci z                                                                       (derivované tabulky), uvnitř kterého je možné použít referenci na relaci X. Místo derivované      vložených, aktualizovaných a odstraněných řádek pro každý interval:
    WHERE EXISTS(SELECT id
                    FROM deti d                                                              tabulky lze použít funkci, která vrací tabulku, a pak atribut(y) z relace X může být
                                                                                                                                                                                                SELECT dbname, time,
                   WHERE d.zamestnanec_id = z.id)                                            argumentem této funkce.
                                                                                                                                                                                                      tup_inserted - lag14(tup_inserted) OVER w as tup_inserted,
                                                                                                                                                                                                       tup_updated - lag(tup_updated) OVER w as tup_updated,
 Zobrazí z každého oddělení dva nejstarší zaměstnance (více násobné použití tabulky)         Pro každý záznam z tabulky a vrátí všechny záznamy z tabulky b, pro které platí, že atribut               tup_deleted - lag(tup_deleted) OVER w as tup_deleted,
                                                                                             a je větší než dvojnásobek atributu b.                                                                FROM statistcs WINDOW w15 AS (PARTITION BY dbname,
  SELECT jmeno, prijmeni                                                                                                                                                                                                         ORDER BY time)
      FROM zamestnanci z1
     WHERE vek IN (SELECT vek
                      FROM zamestnanci z2                                                 10 Tabulka je relací. Výsledek SQL dotazu je relací. Tudíž příkaz SELECT můžeme aplikovat na
                     WHERE z2.sekce_id = z1.sekce_id                                         tabulku nebo i na výsledek jiného příkazu SELECT.
                     ORDER BY vek DESC                                                    11 Pro hodnotu NULL není možné použít operator =.
                     LIMIT 2)                                                             12 SELECT ze SELECTu                                                                              14 Funkce lag vrací předchozí hodnotu atributu v podmnožině.
                                                                                          13 V PostgreSQL 9.3                                                                               15 Příklad obsahuje ukázku sdílené definice okna (podmnožiny) w.
Publikováno pod licencí BSD © Pavel Stěhule 2012 CSPUG pavel.stehule@gmail.com http://www.cspug.cz http://www.postgres.cz http://groups.google.com/group/postgresql-cz?hl=cs


Common Table Expressions – CTE                                                             Ostatní SQL příkazy                                                                                     string_to_array(a, ',')                                  parsuje řetězec do pole
                                                                                                                                                                                                   string_agg(a, ', ')                                      agreguje do seznamu hodnot
   Pomocí CTE můžeme dočasně (v rámci jednoho SQL příkazu) definovat novou relaci a na                                                                                                             concat('A',NULL,'B')                                     spojuje řetězce, ignoruje NULL
   tuto relaci se můžeme opakovaně odkazovat.                                              INSERT                                                                                                  concat_ws(',', 'A',NULL,'B')                             spojuje řetězce daným separátorem

                                                                                              Jednoduchý INSERT s vložením defaultní hodnoty                                                       'Hello' || 'World'                                  spojuje řetězce (citlivé na NULL)
Nerekurzivní CTE                                                                                                                                                                                   10 IS NULL                                          test na NULL
                                                                                               INSERT INTO tab1(id, t) VALUES(DEFAULT, '2012-12-16');
                                                                                                                                                                                                   10 IS NOT NULL                                      negace testu na NULL
   CTE klauzule umožňuje řetězení (pipelining) SQL příkazů (archivuje zrušené záznamy):
                                                                                              Vícenásobný INSERT                                                                                   10 IS DISTINCT FROM 20                   NULL bezpečný test na neekvivalenci
    WITH t1 AS (DELETE FROM tabulka RETURNING *),                                                                                                                                                  10 IS NOT DISTINCT FROM 20               NULL bezpečný test na ekvivalenci
         t2 AS (INSERT INTO archiv SELECT * FROM t1 RETURNING *)                               INSERT INTO tab2(a, b)             VALUES(10,20),(30,40)
      SELECT * FROM t2;
                                                                                              INSERT SELECT – vloží výsledek dotazu včetně aktuálního času                                         row_number()                                             číslo řádku v podmnožině
   Vrací čísla dělitelná 2 a 3 beze zbytku z intervalu 1 až 20 (zabraňuje opakovanému                                                                                                              rank()                                                   pořadí v podmn. – nesouvislá řada
                                                                                               INSERT INTO statistics                                                                              dense_rank()                                             pořadí v podmn. – souvislá řada
   výpočtu):                                                                                     SELECT CURRENT_TIMESTAMP, *
                                                                                                                                                                                                   lag(a, 1, -1)                                            n-tá předchozí hodnota v podmn.
    WITH iterator AS (SELECT i FROM generate_series(1,20) g(i))                                     FROM pg_stat_user_tables
                                                                                                                                                                                                   lead(a, 1, -1)                                           n-tá následující hodnota v podmn.
      SELECT * FROM iterator WHERE i % 2 = 0
                                                                                                                                                                                                   ntile(10)                                                vrací číslo podmnožiny z n skupin18
      UNION
      SELECT * FROM iterator WHERE i % 3 = 0
                                                                                           UPDATE
      ORDER BY 1;                                                                                                                                                                                  nazev ~ 'xx$'              test na regulární výraz (citlivé na velikost písmen)
                                                                                              Aktualizace na základě dat z jiné tabulky                                                            název ~* 'XX$'             test na regulární výraz (bez ohledu na velikost písmen)
   V PostgreSQL mohou relace vzniknout i na základě DML příkazů ( INSERT, UPDATE,              UPDATE zamestnanci z
   DELETE).                                                                                       SET mzda = n.mzda                                                                                Přibližné dohledání mediánu:
                                                                                                 FROM novy_vymer n
    WITH upsert16 AS (UPDATE target t SET c = s.c                                                WHERE z.id = n.id                                                                                  SELECT max(a)
                        FROM source s                                                                                                                                                                  FROM (SELECT a, ntile(2) OVER (ORDER BY a)
                       WHERE t.id = s.id                                                                                                                                                                        FROM a) x
                       RETURNING s.id)                                                     DELETE                                                                                                     WHERE ntile = 1;
      INSERT INTO target
        SELECT *                                                                              Příkaz DELETE odstraňuje záznamy z tabulky
           FROM source s                                                                                                                                                                        Monitoring
          WHERE s.id NOT IN (SELECT id                                                         DELETE FROM produkty
                                 FROM upsert)                                                    WHERE id IN (SELECT id
                                                                                                                 FROM ukoncene_produkty)                                                        Offline
Rekurzivní CTE                                                                                Častou úlohou je odstranění duplicitních řádek:                                                      Základní úkolem je monitorování pomalých dotazů 19, popřípadě monitorování událostí, které
                                                                                                                                                                                                   jsou obvykle spojeny s výkonnostními problémy.
   Lokální relace vzniká jako výsledek iniciálního SELECTu S1, který vrací kořen               DELETE FROM lidi l
   a opakovaného volání SELECTu S2, který vrací všechny potomky uzlů, které byly                 WHERE ctid17 <> (SELECT ctid                                                                       log_min_duration_statement = 200
                                                                                                                     FROM lidi
   dohledány v předchozí iteraci. Rekurze končí, pokud výsledkem S2 je prázdná relace:
                                                                                                                    WHERE prijmeni=l.prijmeni                                                      zapíše dotaz, který běžel déle než 200 ms
    WITH RECURSIVE ti                                                                                                 AND jmeno=l.jmeno
      AS (SELECT S1                                                                                                 LIMIT 1);                                                                       log_lock_waits = on
          UNION ALL
          SELECT S2                                                                                                                                                                                zaloguje čekání na zámek delší než detekce deadlocku (1 sec)
             FROM tabulka t                                                                Často používané funkce a operátory
                  JOIN ti                                                                                                                                                                           log_temp_files = 1MB
                  ON t.parent = ti.id)                                                        substring('ABC' FROM 1 FOR 2)                            vrátí podřetězec
      SELECT *                                                                                                                                                                                     zaloguje vytvoření dočasného souboru většího než 1MB20
                                                                                              upper('ahoj')                                            převede text na velká písmena
         FROM ti;
                                                                                              lower('AHOJ')                                            převede text na malá písmena
   Zobrazí seznam všech zaměstnanců, kteří jsou přímo nebo nepřímo podřízení zaměstnanci      to_char(now(), 'DD.MM.YY')                               formátuje datum                          Online
   s id = 1 (včetně hloubky rekurze):                                                         to_char(now(), 'HH24:MI:SS')                             formátuje čas
                                                                                              trim('   aa ')                                           odstraní krajní mezery                      Dotazy do systémových tabulek můžeme zjistit aktuální stav a provoz databáze, případně
    WITH RECURSIVE os                                                                         EXTRACT(dow FROM now())                                  vratí den v týdnu                           využití jednotlivých databázových objektů.
      AS (SELECT , 1 AS hloubka
                                                                                              EXTRACT(day FROM now())                                  vrátí den v měsíci                          Stav otevřených spojení (přihlášených uživatelů do db)
             FROM zamestnanci
            WHERE id = 1                                                                      EXTRACT(month FROM now())                                vrátí měsíc
          UNION ALL                                                                           EXTRACT(year FROM now())                                 vrátí rok                                    SELECT * FROM pg_stat_activity;
          SELECT z.*, hloubka + 1                                                             date_trunc('month', now())                               vrátí nejbližší začátek období
             FROM zamestnanci z                                                                                                                                                                    Přerušení všech dotazů běžících déle než 5 min
                                                                                              COALESCE(a,b,c)                                          vrátí první ne NULL hodnotu
                  JOIN os
                  ON z.nadrizeny = os.id)                                                     array_lower(a, 1)                                        vrátí spodní index pole nté dimenze          SELECT pg_cancel_backend(pid)
      SELECT *                                                                                array_upper(a,1)                                         vrátí horní index pole nté dimenze              FROM pg_stat_activity
         FROM os;                                                                             random()                                                 vrátí pseudonáhodné číslo [0..1)
                                                                                              generate_series(l,h)                                     generuje posloupnost od l do h
                                                                                              array_to_string(a, ',')                                  serializuje pole                         18 Rozdělí množinu do n podobně velkých podmnožin. Lze použít pro orientační určení mediánu a
                                                                                                                                                                                                   kvantilů.
                                                                                                                                                                                                19 Pro analýzu pomalých dotazů lze použít pgFouine nebo pgbadger. K monitorování lze použít
                                                                                           17 Ctid je fyzický identifikátor záznamu – v podstatě je to pozice záznamu v datovém souboru. Hodí      extenze auto_explain (zapíše do logu prováděcí plán pomalého dotazu)
16 V případě, že záznam existuje, provede UPDATE, jinak INSERT.                               se pouze pro některého úlohy, neboť po aktualizaci má záznam jiné ctid.                           20 Velké množství dočasných souborů může signalizovat nízkou hodnotu work_mem.
Publikováno pod licencí BSD © Pavel Stěhule 2012 CSPUG pavel.stehule@gmail.com http://www.cspug.cz http://www.postgres.cz http://groups.google.com/group/postgresql-cz?hl=cs

      WHERE current_timestamp – query_start > interval '5 min';                                  což může být řádově dražší úloha než test na existenci hodnoty:                          Polymorfní funkce
   Využití jednotlivých db (včetně aktuálně přihlášených uživatelů k db)                          BEGIN
                                                                                                    IF EXISTS(SELECT 1                                                                       Polymorfní funkce jsou generické funkce, navržené tak, aby byly funkční s libovolným
    SELECT * FROM pg_stat_database;                                                                              FROM zamestnanci z                                                          datovým typem. Místo konkrétního typu parametru použijeme generický typ – ANYELEMT,
                                                                                                                WHERE z.jmeno = _jmeno                                                       ANYARRAY, ANYNONARRAY, ANYRANGE a ANYENUM.
   Využití tabulek21 (počet čtení, počet zápisů, ...)                                                           FOR UPDATE24)
                                                                                                    THEN                                                                                     Generická funkce myleast by mohla vypadat následujícím způsobem:
    SELECT * FROM pg_stat_user_tables;                                                                 ...
                                                                                                    END IF;                                                                                   CREATE OR REPLACE FUNCTION myleast(VARIADIC ANYARRAY)
   Využití IO, cache vztažené k tabulkám                                                          END;                                                                                        RETURNS ANYELEMENT AS $$
                                                                                                                                                                                               SELECT MIN(v)
    SELECT * FROM pg_statio_user_tables;                                                         Ošetření chyby                                                                                FROM unnest($1) g(v);
                                                                                                                                                                                              $$ LANGUAGE sql;
   Po instalaci doplňku pg_buffercache můžeme monitorovat obsah PostgreSQL cache.                PL/pgSQL vytváří subtransakci pro každý chráněný blok – v případě zachycení výjimky je
   Funkce z doplňku pgstattuple umožňují provést nízkoúrovňovou diagnostiku datových             tato subtransakce automaticky odvolána:
   souborů tabulek a indexů.
                                                                                                                                                                                          SECURITY DEFINER funkce
                                                                                                  CREATE OR REPLACE FUNCTION fx(a int, b int)
                                                                                                  RETURNS int AS $$                                                                          Kód funkce v PostgreSQL běží s právy uživatele, který danou funkci aktivoval 25 (podobné je
                                                                                                  BEGIN
Pl/pgSQL                                                                                            RETURN a / b;
                                                                                                                                                                                             to i u triggerů). Toto chování lze změnit – pomocí atributu funkce SECURITY DEFINER.
                                                                                                  EXCEPTION WHEN division_by_zero THEN                                                       Tato technika se používá v situacích, kdy dočasně musíme zpřístupnit data, ke kterým
   PL/pgSQL je jednoduchý programovací jazyk vycházející z PL/SQL (Oracle) a potažmo ze             RAISE EXCEPTION 'deleni nulou';                                                          běžně není přístup.
   zjednodušeného programovacího jazyka ADA. Je těsně spjat s prostředím PostgreSQL –             END;
                                                                                                  $$ LANGUAGE plpgsql IMMUTABLE STRICT;                                                      Následující funkci musí zaregistrovat (tím se stane jejím vlastníkem) uživatel s přístupem
   k dispozici jsou pouze datové typy, které nabízí PostgreSQL a operátory a funkce pro tyto
                                                                                                                                                                                             k tabulce users:
   typy. Je to ideální lepidlo pro SQL příkazy, které mohou být vykonány na serveru, čímž se
   odbourávají latence způsobené sítí a protokolem.                                            Funkce s defaultními parametry                                                                 CREATE OR REPLACE FUNCTION verify_login(usrname text,
                                                                                                                                                                                                                                        password text)
                                                                                                 PostgreSQL podporuje defaultní hodnoty parametrů funkce – při volání funkce, lze             RETURNS boolean AS $$
Základní funkce                                                                                  parametr, který má přiřazenou defaultní hodnotu vynechat.                                    BEGIN
                                                                                                                                                                                                IF EXISTS(SELECT *
   Funkce slouží k získání výsledku nebo provedení nějaké operace nad daty. Funkce               Následující funkce vrátí tabulku existujících databází – a v případě, že parametr                            FROM users u
   v PostgreSQL mohou vracet skalární hodnotu (jeden atribut), záznam (více atributů), pole,                                                                                                                 WHERE u.passwd = md5(verify_login.password)
                                                                                                 vynecháme, tak tabulku databází aktuálního uživatele:                                                          AND u.name = verify_login.usrname)
   případně tabulku. Uvnitř funkcí nelze používat explicitně řízení transakcí 22.
                                                                                                  CREATE OR REPLACE FUNCTION dblist(username text                                               THEN
    CREATE OR REPLACE FUNCTION novy_zamestnanec(jmeno text,                                                                                 DEFAULT CURRENT_USER)                                  RETURN true;
                                             plny_uvazek boolean)                                                                                                                               ELSE
    RETURNS void AS $$                                                                            RETURNS SETOF text AS          $$                                                                RAISE WARNING 'unsuccessful login: %', usrname;
    BEGIN                                                                                         BEGIN                                                                                            PERFORM pg_sleep(random() * 3);
      IF plny_uvazek THEN                                                                           RETURN QUERY SELECT          datname::text                                                     RETURN false;
         INSERT INTO zamestnanci                                                                                   FROM          pg_database d                                                  END;
           VALUES(novy_zamestnanec.jmeno);                                                                        WHERE          pg_catalog.pg_get_userbyid(d.datdba)                         END;
      ELSE                                                                                                             =         username;                                                    $$ SECURITY DEFINER
         INSERT INTO externisti                                                                     RETURN;                                                                                   LANGUAGE plpgsql;
           VALUES(novy_zamestnanec.jmeno);                                                        END;
      END IF;                                                                                     $$ LANGUAGE plpgsql;                                                                       Výhodou tohoto řešení je skutečnost, že i když útočník dokáže kompromitovat účet běžného
    END;                                                                                                                                                                                     uživatele, nezíská přístup k tabulce users.
    $$ LANGUAGE plpgsql;                                                                          SELECT * FROM dblist('postgres');
                                                                                                  SELECT * FROM dblist(username := 'postgres');
    SELECT novy_zamestnanec('Stehule, true');                                                     SELECT * FROM dblist();                                                                 Triggery
    SELECT novy_zamestnanec(jmeno := 'Stehule', true);
                                                                                                                                                                                             Triggrem se v PostgreSQL myslí vazba mezi určitou událostí a jednou konkrétní funkcí.
   Iterace nad výsledkem dotazu                                                                Variadické funkce                                                                             Pokud ta událost nastane, tak se vykoná dotyčná funkce. Triggerem můžeme sledovat
   V některých případech potřebujeme zpracovat výsledek dotazu – iterace FOR SELECT nám                                                                                                      změny dat v tabulkách (klasické BEFORE, AFTER triggery), pokus o změnu dat v pohledu
                                                                                                 Variadická funkce je funkce s proměnlivým počtem parametrů. Posledním parametrem této       (INSTEAD OF triggery), případně změny v systémovém katalogu ( EVENT triggery26).
   umožňuje provést určitý proces nad každým záznamem vrácené relace (pozor – v případě,         funkce je tzv variadický parametr typu pole.
   že lze iteraci nahradit jedním čitelným SQL příkazem, měli bychom preferovat jeden SQL                                                                                                    Nejčastěji používané jsou BEFORE, AFTER triggery volané po operacích INSERT, UPDATE
   příkaz):                                                                                      Následující ukázka je vlastní implementace funkce least – získání minimální hodnoty ze      a DELETE. Vybrané funkce se mohou spouštět pro každý příkazem dotčený řádek ( ROW
                                                                                                 seznamu hodnot:
    DECLARE r record;                                                                                                                                                                        trigger) nebo jednou pro příkaz (STATEMENT trigger). U řádkových triggerů máme
    BEGIN                                                                                         CREATE OR REPLACE FUNCTION myleast(VARIADIC numeric[])                                     k dispozici proměnnou NEW a OLD, obsahující záznam před provedením a po provedení
      FOR r IN SELECT * FROM pg_database                                                          RETURNS numeric AS $$                                                                      příkazu. Modifikací proměnné NEW můžeme záznam měnit (v BEFORE triggeru). V době
      LOOP                                                                                          SELECT MIN(v)
         RAISE NOTICE23 '%', r;                                                                                                                                                              provedení funkcí BEFORE triggerů je dotčený záznam ještě v nezměněné podobě. Funkce
                                                                                                       FROM unnest($1) g(v);
      END LOOP;                                                                                   $$ LANGUAGE sql;                                                                           AFTER triggerů se volají v době, kdy tabulka obsahuje nové verze všech záznamů27.
    END;
                                                                                                 Zde se nejedná o PL/pgSQL funkci, ale o SQL funkci – pro triviální funkce je vhodnější
   Provedení akce pokud hodnota existuje                                                         používat tento jazyk:                                                                    25 Toto chování je podobné přístupu k uživatelským právům v Unixu. Pozor – prakticky ve všech
   Jedná se o typický vzor, kde je začátečnickou chybou rozhodovat nad počtem záznamů –                                                                                                      ostatních db (včetně ANSI SQL) je to jinak – kód uvnitř funkce je vykonáván s právy vlastníka
                                                                                                  SELECT myleast(10,1,2);                                                                    funkce.
                                                                                                  SELECT myleast(VARIADIC ARRAY[10,1,2])                                                  26 V PostgreSQL 9.3
21 Pro indexy - pg_stat_user_indexes                                                                                                                                                      27 AFTER triggery používáme, když potřebujeme vidět změny v tabulce. Provádějí se až po vložení,
22 Používají se pouze subtransakce (implicitní) a to k zajištění ošetření zachycení výjimky.                                                                                                 aktualizaci, odstranění všech řádků realizovaných jedním SQL příkazem a jsou proto o něco málo
23 Zobrazí text na ladící výstup.                                                              24 Pozor na případnou RACE CONDITION.                                                         náročnější než BEFORE triggery – musí se udržovat fronta nevyhodnocených AFTER triggerů.
Publikováno pod licencí BSD © Pavel Stěhule 2012 CSPUG pavel.stehule@gmail.com http://www.cspug.cz http://www.postgres.cz http://groups.google.com/group/postgresql-cz?hl=cs

    CREATE OR REPLACE FUNCTION pridej_razitko()                                                           CREATE OR REPLACE FUNCTION public.objednavka_bi()
    RETURNS trigger AS $$                                                                                 RETURNS trigger AS $$                                                                         echo $SQL | psql postgres -q -t -A -v owner=$1 | 
    BEGIN                                                                                                 BEGIN                                                                                         psql -e postgres
      NEW.vlozeno := CURRENT_TIMESTAMP;                                                                     CASE EXTRACT(year FROM NEW.vlozeno)
      NEW.provedl := SESSION_USER;                                                                              WHEN 2011 THEN                                                                         Jednodušší skripty můžeme napsat pomocí tzv online bloků 32 – kódu v plpgsq.
      RETURN NEW;                                                                                                  INSERT INTO objednavka_2011 VALUES(NEW.*);
    END;                                                                                                        WHEN 2012 THEN                                                                          SQL=$(cat <<'EOF'
    $$ LANGUAGE plpgsql;                                                                                           INSERT INTO objednavka_2012 VALUES(NEW.*);                                           SELECT set_config('custom.owner', :'owner', false);
                                                                                                                ELSE                                                                                    DO $$
    CREATE TRIGGER orazitkuj_zmenu_zamestnanci                                                                     RAISE EXCEPTION 'chybejici partition pro rok %',                                     DECLARE name text;
      BEFORE INSERT OR UPDATE ON zamestnanci                                                                              EXTRACT(year FROM NEW.vlozeno);                                               BEGIN
      FOR EACH ROW                                                                                          END CASE;                                                                                      FOR name IN SELECT d.*
      EXECUTE PROCEDURE pridej_razitko();                                                                   RETURN NULL;                                                                                               FROM pg_database d
                                                                                                          END;                                                                                                        WHERE pg_catalog.pg_get_userbyid(d.datdba)
                                                                                                          $$LANGUAGE plpgsql                                                                                                 = current_setting('custom.owner')
Partitioning                                                                                                                                                                                               LOOP
                                                                                                          CREATE TRIGGER objednavka_before_insert_trg                                                        RAISE NOTICE 'databaze=%', name;
                                                                                                           BEFORE INSERT ON objednavka                                                                     END LOOP;
   Partitioning umožňuje rozdělit data v relaci do definovaných fyzicky oddělených disjunktních                                                                                                         END;
                                                                                                          FOR EACH ROW EXECUTE PROCEDURE public.objednavka_bi()
   podmnožin. Později, při zpracování dotazu se použijí pouze ty partitions, které jsou pro                                                                                                             $$
   zpracování dotazu nezbytné.                                                                                                                                                                          EOF
                                                                                                        Použití                                                                                         )

Dědičnost relací                                                                                         Při plánování dotazu se provádí identifikace partitions, které lze bezpečně vyjmout            echo $SQL | psql postgres -v owner=$1
                                                                                                         z plánování neboť obsahují pouze řádky, které 100% nevyhovují podmínkám, a tyto
   Velice specifickou vlastností PostgreSQL je částečná podpora OOP – podpora dědičnosti.                                                                                                              Zajímavým trikem je generování obsahu ve formátu vhodném pro příkaz COPY, který se
                                                                                                         partitions se při zpracování dotazu nepoužijí. U každého dotazu, kde předpokládáme
   Relace může být vytvořena děděním jiné relace. Relace potomka obsahuje atributy rodiče                                                                                                              pomocí roury natlačí do další instance konzole. Následující příkaz uloží aktuální stav
                                                                                                         aplikaci partitioningu si ověřujeme (příkaz EXPLAIN), že dotaz je napsán tak, že planner
   a případně další. Relace rodiče obsahuje všechny záznamy relací, které vznikly jejím                                                                                                                provozních statistik a uloží je do souhrnné tabulky v databázi postgres.
                                                                                                         z něj dokáže detekovat nepotřebné partitions.
   přímým nebo nepřímým poděděním. Například z relace lidé (jméno, přijmení) podědím
   relace studenti (jméno, příjmení, obor) a zaměstnanci (jméno, příjmení, zařazení). Dotaz do                                                                                                          SQL_stat=$(cat <<'EOF'
                                                                                                          postgres=# EXPLAIN SELECT *
                                                                                                                                                                                                        SELECT current_database(), current_timestamp::timestamp(0),
   relace lidé zobrazí jak všechny studenty tak všechny zaměstnance.                                                            FROM objednavka
                                                                                                                                                                                                                sum(n_tup_ins) tup_iserted,
                                                                                                                               WHERE EXTRACT(year from vlozeno) > 2012;
    CREATE TABLE lide(jmeno text, prijmeni text);                                                                                                                                                               sum(n_tup_upd) tup_updated,
                                                                                                                          QUERY PLAN
    CREATE TABLE studenti(obor text) INHERITS (lide);                                                                                                                                                           sum(n_tup_del) tup_deleted,
                                                                                                          ──────────────────────────────────────────────────────────────
    CREATE TABLE zamestnanci(zarazeni text) INHERITS(lide);                                                                                                                                                 FROM pg_stat_user_tables
                                                                                                          Result (cost=0.00..77.05 rows=1087 width=20)
                                                                                                                                                                                                          GROUP BY substring(relname from 1 for 2);
                                                                                                           -> Append (cost=0.00..77.05 rows=1087 width=20)
                                                                                                                                                                                                        EOF
   Partition je v PostgreSQL poděděná relace (tabulka) s definovaným omezením. Toto                         -> Seq Scan on objednavka
                                                                                                                                                                                                        )
   omezení by mělo časově invariantní (tj neměl bych se snažit o partition pro „posledních 30                      Filter: (date_part('year', vlozeno) > 2012)
   dní“)                                                                                                    -> Seq Scan on objednavka_2013
                                                                                                                                                                                                        for d in `psql -At -c "select datname from pg_database where
                                                                                                                   Filter: (date_part('year', vlozeno) > 2012)
                                                                                                                                                                                                        pg_get_userbyid(datdba) <> 'postgres'"`
    CREATE TABLE objednavka(vlozeno date, castka numeric(12,2));                                          (6 rows)
                                                                                                                                                                                                        do
    CREATE TABLE objednavka_2012                                                                                                                                                                           echo $SQL_stat | psql -At $d -F$'t' | 
           (CHECK(EXTRACT(year FROM vlozeno) = 2012))
      INHERITS (objednavka);                                                                            Kombinace bash a psql                                                                              psql postgres -c "COPY statistics FROM stdin"
                                                                                                                                                                                                        done
    CREATE TABLE objednavka_2011
           (CHECK(EXTRACT(year FROM vlozeno) = 2011))                                                    psql lze použít i pro jednodušší skriptování (automatizaci) v kombinaci s Bashem.
      INHERITS (objednavka);
                                                                                                         V jednodušších případech stačí použít parametr -c "SQL příkaz". Ten ovšem nelze            Online fyzické zálohování
                                                                                                         použít, když chceme použít dotaz parametrizovat pomocí psql proměnných.
Omezení                                                                                                                                                                                             Kontinuální
                                                                                                         Ukázka využívá psql proměnných, heredoc zápis a binární ASCII unit separator :
 ✗ Partitions se nevytváří automaticky28 - musíme si je vytvořit manuálně.
                                                                                                          SQL=$(cat <<EOF                                                                              Při kontinuálním zálohování archivujeme segmenty transakčního logu. Na základě obsahu
                                                                                                          SELECT datname, pg_catalog.pg_get_userbyid(d.datdba)                                         transakčního logu jsme schopni zrekonstruovat stav databáze v libovolném okamžiku od
 ✗ Umístění záznamů do odpovídajících partitions se neprovádí automaticky29 - musíme si                       FROM pg_database d
   napsat distribuční trigger.                                                                                                                                                                         vytvoření kompletní zálohy do okamžiku získání posledního validního segmentu
                                                                                                            WHERE pg_catalog.pg_get_userbyid(d.datdba) = :'owner'
                                                                                                          EOF                                                                                          transakčního logu.
 ✗ Počet partitions je omezen – neměl by přesáhnout 100 partitions jedné tabulky30.                       )
                                                                                                                                                                                                      Konfigurace
                                                                                                          echo $SQL | psql postgres -q -t -A -v owner=$1 -F $'x1f'| 
Redistribuční trigger                                                                                                                                                                                  Pro vytvoření zálohy musíme povolit export segmentů transakčního logu a nastavit
                                                                                                          while IFS=$'x1f' read -r a b;
                                                                                                          do                                                                                           tzv archive_command33:
   Úkolem tohoto triggeru je přesun záznamu z rodičovské tabulky do odpovídající poděděné                    echo -e "datname='$a'towner='$b'";
                                                                                                                                                                                                        archive_mode = on
   tabulky31 (pro větší počet partitions – cca nad 20 je praktické použití dynamického SQL).              done
                                                                                                                                                                                                        archive_command = 'cp %p /var/backup/xlogs/%f'
                                                                                                                                                                                                        archive_timeout = 300
                                                                                                         Oblíbeným trikem je vygenerování DDL příkazů v psql, které se pošlou jiné instanci psql,
28 Lze je vytvářet uvnitř triggerů, ale to nedoporučuji – hrozí race condition nebo ztráta výkonu        kde se provedou. Následující skript odstraní všechny databáze vybraného uživatele:
   z důvodu čekání na zámek. Nejjednodušší a nejpraktičtější je vyrobit partitions na rok dopředu.
                                                                                                          SQL=$(cat <<EOF
29 Základem je distribuční BEFORE INSERT trigger nad rodičovskou tabulkou. V případě, že
                                                                                                          SELECT format('DROP DATABASE %I;', datname)
   dochází při UPDATE k přesunu mezi partitions je nutný BEFORE UPDATE trigger nad každou                     FROM pg_database d
   poděděnou tabulkou.                                                                                      WHERE pg_catalog.pg_get_userbyid(d.datdba) = :'owner'
30 Při velkém počtu partitions je problém s paměťovými nároky optimazátoru.                               EOF                                                                                       32 Předávání parametrů dovnitř online bloku je o něco málo komplikovanější..
31 Také aplikace může přesněji cílit a zapisovat do tabulek, které odpovídají partitions a nikoliv do     )                                                                                         33 Vždy při naplnění segmentu transakčního logu nebo vypršení časového intervalu PostgreSQL volá
   rodičovské tabulky – tím se ušetří volání redistribučního triggeru a INSERT bude rychlejší.                                                                                                         archive command, jehož úkolem je zajistit zápis segmentu na bezpečné médium.
Publikováno pod licencí BSD © Pavel Stěhule 2012 CSPUG pavel.stehule@gmail.com http://www.cspug.cz http://www.postgres.cz http://groups.google.com/group/postgresql-cz?hl=cs

                                                                                               úprava pg_hba.conf:                                                                         můžeme provést fyzickou zálohu (zkopírování datového adresáře – full backup). K řízení
  Vytvoření zálohy
                                                                                                local      replication            backup                                   md5             replikace slouží následující funkce:
   Vynucení checkpointu a nastavení štítku (label) plné zálohy
    SELECT pg_start_backup(current_timestamp::text);                                           Vytvoření uživatele backup:                                                                 pg_xlog_replay_pause()                   pozastaví replikaci
                                                                                                                                                                                           pg_xlog_replay_resume()                  obnoví replikaci
   Záloha datového adresáře – bez transakčních logů                                             CREATE ROLE backup LOGIN REPLICATION;                                                      pg_is_xlog_replay_paused()               vrátí true v případě pozastavené replikace
                                                                                                ALTER ROLE backup PASSWORD 'heslo';
    cd /usr/local/pgsql
    tar -cjf pgdata.tar.bz2 --exclude='pg_xlog' data/*                                         Tato změna konfigurace vyžaduje restart databáze.                                          Využití systémového katalogu
   Ukončení plné zálohy (full backup)                                                          Vlastní zálohování
                                                                                                                                                                                           V PostgreSQL jsou všechna data potřebná pro provoz databáze uložena v systémových
    SELECT pg_stop_backup();                                                                   Spustíme příkaz pg_basebackup, kde uvedeme adresář, kde chceme mít uložený klon.            tabulkách. Orientace v systémových tabulkách a pohledech není jednoduchá, lze ovšem
                                                                                                                                                                                           využít jeden trik – většina dotazů do těchto objektů je pokryta příkazy v psql. A pokud se
   Adresář /var/backup/xlogs se začne plnit transakčními logy34.                                [pavel@diana ~]$ /usr/local/pgsql91/bin/pg_basebackup -D 
                                                                                                 zaloha9 -U backup -v -P -x -c fast                                                        psql pustí s parametrem -E, tak dojde k zobrazení všech SQL příkazů, které se posílají do
                                                                                                Password:                                                                                  DB – a tedy i dotazů do systémového katalogu.
  Obnova ze zálohy                                                                              xlog start point: 0/21000020
                                                                                                50386/50386 kB (100%), 1/1 tablespace                                                       bash-4.2$ psql -E postgres
   Rozbalení poslední plné zálohy                                                                                                                                                           psql (9.3devel)
                                                                                                xlog end point: 0/21000094                                                                  Type "help" for help.
    cd /usr/local/pgsql
    tar xvfj pgdata.tar.bz2                                                                     pg_basebackup: base backup completed
                                                                                                                                                                                            postgres=# l
                                                                                                                                                                                            ********* QUERY **********
   V datovém adresáři vytvořte soubor recovery.conf, ve kterém definujete tzv                  Obnova ze zálohy                                                                             SELECT d.datname as "Name",
   restore_commad (analogicky k archive_command):                                              Obsah adresáře zálohy zkopírujeme do adresáře clusteru PostgreSQL a nastartujeme                    pg_catalog.pg_get_userbyid(d.datdba) as "Owner",
                                                                                               server. Pozor - vlastníkem souborů bude uživatel, pod kterým byl spuštěn                         pg_catalog.pg_encoding_to_char(d.encoding) as "Encoding",
    restore_command = 'cp /var/backup/xlogs/%f %p'                                                                                                                                                 d.datcollate as "Collate",
                                                                                               pg_basebackup, což pravděpodobně nebude uživatel postgres, a proto je nutné nejprve                 d.datctype as "Ctype",
   Pokud je čitelný adresář s transakčními logy původního serveru, tak můžeme tento adresář    hromadně změnit vlastníka souborů.                                                               pg_catalog.array_to_string(d.datacl, E'n') AS "privileges"
   zkopírovat do datového adresáře obnoveného serveru. Jinak vytvoříme prázdný adresář                                                                                                      FROM pg_catalog.pg_database d
                                                                                                                                                                                            ORDER BY 1;
    mkdir pg_xlog                                                                             Fyzická replikace                                                                             **************************
   Nastartujeme server. Po úspěšném startu by měl být soubor recovery.conf přejmenován na      Potřebujeme opět uživatele s právem REPLICATION a přístupem k db replication.               Běžně se v systémovém katalogu dohledává seznam tabulek, databází, uživatelů.
   recovery.done a v logu bychom měli najít záznam:                                                                                                                                        Systémový katalog můžeme využít k zobrazení tabulek obsahující určité sloupce nebo
                                                                                               Základem sekundárního (ro) serveru je klon primárního serveru (rw).
    LOG:     archive recovery complete                                                                                                                                                     uložených procedur, které obsahují hledaný řetězec.
    LOG:     database system is ready to accept connections                                    Úpravy konfigurace – master
                                                                                                                                                                                           Zobrazí tabulky obsahující hledaný sloupec:
                          35
                                                                                                wal_level = hot_standby
   PostgreSQL implicitně provádí obnovu do okamžiku, ke kterému dohledá poslední validní        max_wal_senders = 1                                                                         SELECT attrelid::regclass
   segment transakčního logu. Záznam v logu referuje o postupu hledání segmentů:                                                                                                               FROM pg_catalog.pg_attribute a
                                                                                                # v případě větších db zvýšit                                                                 WHERE a.attname = 'jmeno' AND NOT a.attisdropped;
    LOG: restored log file "000000010000000000000007" from archive                              wal_keep_segments = 100
    LOG: restored log file "000000010000000000000008" from archive
    LOG: restored log file "000000010000000000000009" from archive                                                                                                                         Místo systémového katalogu lze použít standardizované information_schema:
                                                                                               Úpravy konfigurace – slave
    cp: cannot stat `/var/backup/xlogs/00000001000000000000000A':                                                                                                                           SELECT table_name
    No such file or directory LOG: could not open file                                         Pozor, po naklonování se slave nikdy nesmí spustit jako samostatný server. Pokud možno,         FROM information_schema.columns
    "pg_xlog/00000001000000000000000A": No such file or directory                              klonujte s konfigurací wal_level = hot_standby na masteru.                                     WHERE column_name ='jmeno';
                                                                                                hot_standby = on                                                                           Zobrazí funkce, které ve zdrojovém kódu obsahují hledaný řetězec:
Jednorázové                                                                                     hot_standby_feedback = on# pro zajištění pomalých dotazů na sl.
                                                                                                                                                                                            SELECT oid::regprocedure
   Jednorázovým zálohováním se míní vytvoření klonu běžící databáze. Základem této             Vytvořte soubor recovery.conf, který uložte do cluster adresáře serveru slave:                  FROM pg_proc
   metody je časově omezená replikace záznamů transakčních logů. Výhodou je jednoduchost                                                                                                      WHERE prosrc ILIKE '%hello%';
   použití – rychlost zálohování a obnovy ze zálohy je limitována rychlostí IO.                 standby_mode='on'
                                                                                                primary_conninfo='host=localhost user=backup password=heslo'
  Konfigurace                                                                                                                                                                             Vyhledávání v textu
                                                                                               Před startem repliky vymažte log a pid file. Po startu by měl log obsahovat záznam:
   Tato metoda vyžaduje úpravu konfiguračního souboru a uživatele s oprávněním
                                                                                                LOG:    entering standby mode
   REPLICATION a přístupem k fiktivní databázi replication (přístup se povoluje
                                                                                                LOG:    consistent recovery state reached at 0/300014C                                    Fulltext
   v souboru pg_hba.conf).                                                                      LOG:    record with zero length at 0/300014C
                                                                                                LOG:    database system is ready to accept read only connections                           Fultext umožňuje case insensitive vyhledávání slov (případně prefixů slov) v textu. S
    wal_level = archive                                                                                                                                                                    drobnými úpravami lze vyhledávat lexémy a nebo lze při vyhledávání ignorovat diakritiku.
                                                                                                LOG:    streaming replication successfully connected to primary
    max_wal_senders = 1
                                                                                                                                                                                           Každé slovo se při fulltextovém zpracování definovaným způsobem transformuje. Seznam
    # v případě větších db zvýšit
                                                                                               Po startu je slave v read only režimu. Signálem jej lze přepnout do role master. Pozor –    těchto transformací (každá třída slov může mít jinou transformaci) pro určitý jazyk
    wal_keep_segments = 100                                                                    tato změna je nevratná. Nový slave se vytvoří kopií nového masteru.                         nazýváme konfigurací. Nejjednodušší konfigurací je konfigurace simple. Pro urychlení
                                                                                                su postgres                                                                                fulltextového vyhledávání potřebujeme fulltextový index (GiST, GIN funkcionální index)
                                                                                                pg_ctl -D /usr/local/pgsql/data.repl/ promote
                                                                                                                                                                                            CREATE INDEX ON obce
34 Transakční logy lze velice dobře komprimovat – např. asynchronně (viz BARMAN)                                                                                                                               USING gist ((to_tsvector('simple',nazev)));
35 Nastavením recovery_target_time v recovery.conf lze určit okamžik, kdy se má                synchronizaci lze na každé replikovaném serveru dočasně blokovat – a během té doby
   s přehráváním transakčních logů skončit – například před okamžik, kdy došlo k odstranění
   důležitých dat.
Tahak postgresql

Contenu connexe

En vedette

Product Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsProduct Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsPixeldarts
 
How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthHow Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthThinkNow
 
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfmarketingartwork
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024Neil Kimberley
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)contently
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024Albert Qian
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsKurio // The Social Media Age(ncy)
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Search Engine Journal
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summarySpeakerHub
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next Tessa Mero
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentLily Ray
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best PracticesVit Horky
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project managementMindGenius
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...RachelPearson36
 
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Applitools
 
12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at WorkGetSmarter
 

En vedette (20)

Product Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsProduct Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage Engineerings
 
How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthHow Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental Health
 
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
 
Skeleton Culture Code
Skeleton Culture CodeSkeleton Culture Code
Skeleton Culture Code
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search Intent
 
How to have difficult conversations
How to have difficult conversations How to have difficult conversations
How to have difficult conversations
 
Introduction to Data Science
Introduction to Data ScienceIntroduction to Data Science
Introduction to Data Science
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best Practices
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project management
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
 
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
 
12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work
 

Tahak postgresql

  • 1. Publikováno pod licencí BSD © Pavel Stěhule 2012 CSPUG pavel.stehule@gmail.com http://www.cspug.cz http://www.postgres.cz http://groups.google.com/group/postgresql-cz?hl=cs Export a import dat Mělo by platit6: PostgreSQL ve verzi 9.2 a 9.3 shared_buffers + 2 * work_mem * max_connection <= 2/3 RAM shared_buffers + 2 * maintenance_work_mem <= 1/2 RAM createdb jménodb Příkaz COPY max_connections <= 10 * (počet_CPU) Vytvoří novou databázi Pomocí příkazu COPY můžeme číst a zapisovat soubory na serveru (pouze superuser) nebo checkpoint_segments udává počet 16MB segmentů transakčního logu po jejichž naplnění číst ze stdin a zapisovat na stdout. Podobný příkaz copy v psql umožňuje číst dojde k tzv CHECKPONTu (databáze zapíše modifikované datové stránky (cache) na disk). dropdb jménodb a zapisovat soubory na klientském počítači. checkpoint_segments = 32 odstraní existující databázi Export tabulky zaměstnanci do CSV souboru Po CHECKPOINTu lze zahodit transakční logy vztažené k času před CHECKPOINTem. Za psql jménodb COPY zamestnanci TO '/tmp/zam.csv' optimální frekvenci CHECKPOINTů se považuje 5 – 15 min. Obvykle se zvyšuje na 32 až CSV HEADER 2567 (pro vytížené servery). spustí SQL konzole DELIMITER ';' FORCE QUOTE *; wal_buffers = 256kB pg_dump jménodb > jméno_souboru Import tabulky zaměstnanci z domovského adresáře uživatele (v konzoli) Velikost bufferu transakčního logu by měl být větší než je velikost běžné transakce – pak se Vytvoří zálohu databáze copy zamestnanci from ~/zamestnanci.dta provede pouze jeden zápis do transakčního logu během transakce (256kB..1MB). Aktuálně výchozí hodnotou je -1, což znamená zhruba 3% shared_buffers (max 16MB). SQL konzole – psql pg_dump – zajímavé parametry listen_addresses = '*' Umožní zadání SQL příkazu a zobrazí jeho výsledek. Příkaz pg_dump slouží k jednoduchému zálohování databáze 2. A pro vzdálený přístup povolit TCP -f specifikuje cílový soubor Přehled důležitých příkazů -a exportuje pouze data SQL -s exportuje pouze definice Každý příkaz začíná zpětným lomítkem “” a není ukončen středníkem. -c odstraní objekty před jejich importem Nejdůležitějším SQL příkazem je příkaz SELECT. Při zápisu je nutné dodržovat pořadí c jménodb přepnutí do jiné databáze -C vloží příkaz pro vytvoření nové databáze jednotlivých klauzulí: l zobrazí seznamu databází -t exportuje pouze jmenovanou tabulku d objekt zobrazí popis objektu (tabulky, pohledu) -T neexportuje uvedenou tabulku SELECT AVG(a.sloupec1), b.sloupec4 --disable-triggers během importu blokuje triggery FROM tabulka1 a dt+ zobrazí seznamu tabulek JOIN tabulka2 b dv zobrazí seznam pohledů --inserts generuje příkazy INSERT místo COPY ON a.sloupec1 = b.sloupec2 df *filtr* zobrazí seznam funkcí --Fc záloha je průběžně komprimovaná s dodatečnými meta informacemi 3 WHERE b.sloupec3 = 'něco' sf funkce zobrazí zdrojový kód funkce GROUP BY b.sloupec4 HAVING AVG(a.sloupec1) > 100 i importuje soubor Základní konfigurace PostgreSQL ORDER BY 1 h SQL zobrazí syntaxi SQL příkazu LIMIT 10 ? zobrazí seznam psql příkazů Soubor postgresql.conf q ukončí konzolu x přepíná řádkové a sloupcové zobrazení Po instalaci PostgreSQL je nutné nastavit několik málo konfiguračních parametrů, které Sjednocení, průnik, rozdíl relací timing on zapíná měření času zpracování dotazu ovlivňují využití operační paměti (výchozí nastavení je zbytečně úsporné). Pro relace (tabulky) existují operace sjednocení ( UNION), průnik (INTERSECT) a rozdíl shared_buffers4= 2GB (EXCEPT). Častou operací je sjednocení relací – výsledků dvou příkazů SELECT – operace Konfigurace konzole sloučí řádky (a zároveň odstraní případné duplicitní řádky). Podmínkou je stejný počet velikost paměti pro uložení datových stránek (1/5..1/3 RAM 5) sloupců a konvertibilní datové typy slučovaných relací. Soubor .bashrc work_mem = 10MB export PAGER=less Vybere 10 nejstarších zaměstnanců bez ohledu zdali se jedná o interního nebo externího export LESS="-iMSx4 -RSFX -e" limit paměti pro běžnou manipulaci s daty (10..100MB) zaměstnance: maintenance_work_mem = 200MB SELECT jmeno, prijmeni, vek Soubor .psqlrc FROM zamestnanci pset pager always limit paměti pro údržbu (100MB ..) UNION8 pset linestyle unicode SELECT jmeno, prijmeni, vek effective_cache_size = 6GB FROM externi_zamestnanci pset null 'NULL' set FETCH_COUNT 1000 ORDER BY vek DESC9 set HISTSIZE 5000 odhad objemu dat cache (2/3 RAM) LIMIT 10; timing max_connections = 100 set HISTFILE ~/.psql_history-:DBNAME set HISTCONTROL ignoredups CASE set PROMPT1 '(%n@%M:%>) [%/] > ' max počet přihlášených uživatelů (často zbytečně vysoké) set ON_ERROR_ROLLBACK on Konstrukce CASE se používá pro transformace hodnot – zobrazení, bez nutnosti definovat set AUTOCOMMIT off1 vlastní funkce. Existují dva zápisy – první hledá konstantu, v druhém se hledá platný výraz: 2 Příkaz pg_dump nezálohuje uživatele. K tomuto účelu se používá příkaz pg_dumpall s parametrem -r. Zálohování příkazem pg_dump je vhodné pro databáze do velikosti cca 50GB. Pro větší databáze je praktičtější použít jiné metody zálohování. 6 Jedná se o orientační hodnoty určené pro počáteční konfiguraci “typického použití” databáze. 3 Pro obnovu je nutné použít pg_restore, obnovit lze i každou vybranou tabulku. 7 Příliš vysoká hodnota může zvýšit dobu obnovy po havárii, kdy se kontroluje transakční log. 4 Nastavení shared_buffers nad 28MB typicky vyžaduje úpravu SHMMAX (na os. Linux) 8 Při použití UNION ALL nedochází k odstranění duplicitních řádků – což může zrychlit vykonání 1 Doporučeno pro produkci – vynucuje potvrzení změn explicitním COMMITem. Po vypnutí 5 Doporučené hodnoty platí pro tzv dedikovaný server – tj počítač, který je vyhrazen primárně pro dotazu. autocommitu se psql bude chovat podobně jako konzole Oracle. provoz databáze. 9 Klauzule ORDER BY se aplikuje na výsledek algebraických operací
  • 2. Publikováno pod licencí BSD © Pavel Stěhule 2012 CSPUG pavel.stehule@gmail.com http://www.cspug.cz http://www.postgres.cz http://groups.google.com/group/postgresql-cz?hl=cs SELECT CASE sloupec WHEN 0 THEN 'NE' Spojení relací10 JOIN SELECT * WHEN 1 THEN 'ANO' END FROM a, FROM tabulka; LATERAL (SELECT * Příkaz JOIN spojuje relace (tabulky) vedle sebe a to na základě stejných hodnot v jednom FROM b SELECT CASE WHEN sloupec = 0 THEN 'NE' nebo více atributu (sloupci). Každé spojení specifikuje dvě relace (spojkou je klíčové slovo WHERE a.a > 2 * b.b) x; WHEN sloupec = 1 THEN 'ANO' END JOIN) a podmínku, která určuje, jak se tyto relace budou spojovat (zapsanou za klíčovým FROM tabulka; slovem ON). Pro každou hodnotu vrátí součet všech kladných celých čísel menší rovno této hodnotě: V případě, že se nenajde hledaná konstanta a nebo že žádný výraz není pravdivý, tak je SELECT a, sum(i) výsledkem hodnota za klíčovým slovem ELSE – nebo NULL, pokud chybí ELSE. Vnitřní spojení relací – INNER JOIN FROM a, LATERAL generate_series(1, a) g(i) Nejčastější varianta – do výsledku se zahrnou pouze řádky, které se podařilo dohledat GROUP BY a Agregační funkce s definovaným pořadím v obou relacích (stejné hodnota/hodnoty) se nalezly v obou tabulkách. ORDER BY 1; Výsledek novějších agregačních funkcí – string_agg, array_agg zavisí na pořadí ve Zobrazí jméno dítěte a jméno rodiče (zaměstnance) – v případě, že má zaměstnanec více Analytické (window) funkce kterém se zpravovávala agregovaná data. Proto je možné přímo v agregační funkci určit dětí, tak jeho jméno bude uvedeno opakovaně: v jakém pořadí bude agragační funkce načítat hodnoty. Klauzule ORDER BY musí být za SELECT d.jmeno, d.prijmeni, z.jmeno, z.prijmeni Analytické funkce se počítají pro každý prvek definované podmnožiny, např. pořadí prvku posledním argumentem agregační funkce. FROM deti d v podmnožině. Na rozdíl od agregačních funkcí se podmnožiny nedefinují klauzulí GROUP JOIN zamestnanci z BY, ale klauzulí PARTITION hned za voláním analytické funkce (v závorce za klíčovým Vrátí seznam zaměstnanců v každém oddělení řazený podle příjmení: ON d.zamestnanec_id = z.id slovem OVER). Mezi nejčastěji používané analytické funkce bude patřit funkce SELECT sekce_id, string_agg(prijmeni, ',' ORDER BY prijmeni) row_number (číslo řádku) nebo ranking (pořadí hodnoty), případně dense_rank FROM zamestnanci Vnější spojení relací – OUTER JOIN a percent_rank. GROUP BY sekce_id Jedná se o rozšíření vnitřního spojení – kromě řádků, které se spárovaly se do výsledku Pozor – pro analytické funkce nelze použít klauzuli HAVING – filtrování hodnot se řeší Poddotazy zařadí i nespárované řádky z tabulky nalevo od slova JOIN (LEFT JOIN) nebo napravo použitím derivované tabulky. od slova JOIN (RIGHT JOIN). Chybějící hodnoty se nahradí hodnotou NULL. Následující dotaz vybere deset nejdéle zaměstnaných pracovníků (na základě porovnání Příkaz SELECT může obsahovat vnořené příkazy SELECT. Vnořený příkaz SELECT se Často se používá dohromady s testem na hodnotu NULL – operátorem IS NULL11. Tím se osobních čísel): nazývá poddotaz a vkládá se do oblých závorek. Poddotazy se mohou použít i u dalších vyberou nespárované řádky – např. pro zobrazení zaměstnanců, kteří nemají děti, lze SQL příkazů. SELECT jmeno, prijmeni použít dotaz: FROM (SELECT rank() OVER (ORDER BY id), jmeno, prijmeni Poddotaz ve WHERE SELECT z.jmeno, z.prijmeni FROM zamestnanci z FROM zamestnanci WHERE ukonceni_prac_pomeru IS NULL) s LEFT JOIN deti d Používá se pro filtrování – následující dotaz zobrazí obce z okresu Benešov: WHERE s.rank <= 10 ON z.id = d.zamestnanec_id WHERE d.id IS NULL. SELECT nazev Zobrazení dvou nejstarších zaměstnanců z každého oddělení: FROM obce o WHERE o.okres_id = (SELECT id FROM okresy Použití derivované tabulky SELECT jmeno, prijmeni FROM (SELECT rank() OVER (PARTITION BY sekce_id WHERE kod = 'BN') ORDER BY vek DESC), Poddotaz se může objevit i v klauzuli FROM – pak jej označujeme jako derivovaná tabulka 12. jmeno, prijmeni I derivovanou tabulku lze spojovat s běžnými tabulkami (obojí je relací). FROM zamestnanci) s Korelované poddotazy WHERE s.rank <= 2 Následující příklad zobrazí seznam nejstarších zaměstnanců z každého oddělení: Poddotaz se může odkazovat na výsledek, který produkuje vnější dotaz. Seznam tří nejlépe hodnocených pracovníků z každého oddělení: SELECT z.jmeno, z.prijmeni FROM zamestnanci z SELECT jmeno, prijmeni Pro každého zaměstnance zobrazí seznam jeho dětí: JOIN (SELECT sekce_id, MAX(vek) AS vek FROM (SELECT rank() OVER (PARTITION BY sekce_id SELECT jmeno, prijmeni FROM zamestnanci ORDER BY hodnoceni), (SELECT string_agg(jmeno, ',') GROUP BY sekce_id) s jmeno, prijmeni, hodnoceni, sekce_id FROM deti d ON z.sekce_id = s.sekce_id FROM zamestnanci) s WHERE d.zamestnanec_id = z.id) AND z.vek = s.vek WHERE s.rank <= 2 FROM zamestnanci z ORDER BY sekce_id, hodnoceni Zobrazí zaměstnance, kteří mají děti: Dotazy s LATERAL relacemi O síle analytických funkcí nás může přesvědčit následující příkaz. V tabulce statistics se ukládají aktuální hodnoty čítačů množství vložených, aktualizovaných a odstraněných SELECT jmeno, prijmeni Klauzule LATERAL13 umožňuje ke každému záznamu relace X připojit výsledek poddotazu řádek. Odečet čítačů se provádí každých 5 minut. Následující dotaz zobrazí počet FROM zamestanci z (derivované tabulky), uvnitř kterého je možné použít referenci na relaci X. Místo derivované vložených, aktualizovaných a odstraněných řádek pro každý interval: WHERE EXISTS(SELECT id FROM deti d tabulky lze použít funkci, která vrací tabulku, a pak atribut(y) z relace X může být SELECT dbname, time, WHERE d.zamestnanec_id = z.id) argumentem této funkce. tup_inserted - lag14(tup_inserted) OVER w as tup_inserted, tup_updated - lag(tup_updated) OVER w as tup_updated, Zobrazí z každého oddělení dva nejstarší zaměstnance (více násobné použití tabulky) Pro každý záznam z tabulky a vrátí všechny záznamy z tabulky b, pro které platí, že atribut tup_deleted - lag(tup_deleted) OVER w as tup_deleted, a je větší než dvojnásobek atributu b. FROM statistcs WINDOW w15 AS (PARTITION BY dbname, SELECT jmeno, prijmeni ORDER BY time) FROM zamestnanci z1 WHERE vek IN (SELECT vek FROM zamestnanci z2 10 Tabulka je relací. Výsledek SQL dotazu je relací. Tudíž příkaz SELECT můžeme aplikovat na WHERE z2.sekce_id = z1.sekce_id tabulku nebo i na výsledek jiného příkazu SELECT. ORDER BY vek DESC 11 Pro hodnotu NULL není možné použít operator =. LIMIT 2) 12 SELECT ze SELECTu 14 Funkce lag vrací předchozí hodnotu atributu v podmnožině. 13 V PostgreSQL 9.3 15 Příklad obsahuje ukázku sdílené definice okna (podmnožiny) w.
  • 3. Publikováno pod licencí BSD © Pavel Stěhule 2012 CSPUG pavel.stehule@gmail.com http://www.cspug.cz http://www.postgres.cz http://groups.google.com/group/postgresql-cz?hl=cs Common Table Expressions – CTE Ostatní SQL příkazy string_to_array(a, ',') parsuje řetězec do pole string_agg(a, ', ') agreguje do seznamu hodnot Pomocí CTE můžeme dočasně (v rámci jednoho SQL příkazu) definovat novou relaci a na concat('A',NULL,'B') spojuje řetězce, ignoruje NULL tuto relaci se můžeme opakovaně odkazovat. INSERT concat_ws(',', 'A',NULL,'B') spojuje řetězce daným separátorem Jednoduchý INSERT s vložením defaultní hodnoty 'Hello' || 'World' spojuje řetězce (citlivé na NULL) Nerekurzivní CTE 10 IS NULL test na NULL INSERT INTO tab1(id, t) VALUES(DEFAULT, '2012-12-16'); 10 IS NOT NULL negace testu na NULL CTE klauzule umožňuje řetězení (pipelining) SQL příkazů (archivuje zrušené záznamy): Vícenásobný INSERT 10 IS DISTINCT FROM 20 NULL bezpečný test na neekvivalenci WITH t1 AS (DELETE FROM tabulka RETURNING *), 10 IS NOT DISTINCT FROM 20 NULL bezpečný test na ekvivalenci t2 AS (INSERT INTO archiv SELECT * FROM t1 RETURNING *) INSERT INTO tab2(a, b) VALUES(10,20),(30,40) SELECT * FROM t2; INSERT SELECT – vloží výsledek dotazu včetně aktuálního času row_number() číslo řádku v podmnožině Vrací čísla dělitelná 2 a 3 beze zbytku z intervalu 1 až 20 (zabraňuje opakovanému rank() pořadí v podmn. – nesouvislá řada INSERT INTO statistics dense_rank() pořadí v podmn. – souvislá řada výpočtu): SELECT CURRENT_TIMESTAMP, * lag(a, 1, -1) n-tá předchozí hodnota v podmn. WITH iterator AS (SELECT i FROM generate_series(1,20) g(i)) FROM pg_stat_user_tables lead(a, 1, -1) n-tá následující hodnota v podmn. SELECT * FROM iterator WHERE i % 2 = 0 ntile(10) vrací číslo podmnožiny z n skupin18 UNION SELECT * FROM iterator WHERE i % 3 = 0 UPDATE ORDER BY 1; nazev ~ 'xx$' test na regulární výraz (citlivé na velikost písmen) Aktualizace na základě dat z jiné tabulky název ~* 'XX$' test na regulární výraz (bez ohledu na velikost písmen) V PostgreSQL mohou relace vzniknout i na základě DML příkazů ( INSERT, UPDATE, UPDATE zamestnanci z DELETE). SET mzda = n.mzda Přibližné dohledání mediánu: FROM novy_vymer n WITH upsert16 AS (UPDATE target t SET c = s.c WHERE z.id = n.id SELECT max(a) FROM source s FROM (SELECT a, ntile(2) OVER (ORDER BY a) WHERE t.id = s.id FROM a) x RETURNING s.id) DELETE WHERE ntile = 1; INSERT INTO target SELECT * Příkaz DELETE odstraňuje záznamy z tabulky FROM source s Monitoring WHERE s.id NOT IN (SELECT id DELETE FROM produkty FROM upsert) WHERE id IN (SELECT id FROM ukoncene_produkty) Offline Rekurzivní CTE Častou úlohou je odstranění duplicitních řádek: Základní úkolem je monitorování pomalých dotazů 19, popřípadě monitorování událostí, které jsou obvykle spojeny s výkonnostními problémy. Lokální relace vzniká jako výsledek iniciálního SELECTu S1, který vrací kořen DELETE FROM lidi l a opakovaného volání SELECTu S2, který vrací všechny potomky uzlů, které byly WHERE ctid17 <> (SELECT ctid log_min_duration_statement = 200 FROM lidi dohledány v předchozí iteraci. Rekurze končí, pokud výsledkem S2 je prázdná relace: WHERE prijmeni=l.prijmeni zapíše dotaz, který běžel déle než 200 ms WITH RECURSIVE ti AND jmeno=l.jmeno AS (SELECT S1 LIMIT 1); log_lock_waits = on UNION ALL SELECT S2 zaloguje čekání na zámek delší než detekce deadlocku (1 sec) FROM tabulka t Často používané funkce a operátory JOIN ti log_temp_files = 1MB ON t.parent = ti.id) substring('ABC' FROM 1 FOR 2) vrátí podřetězec SELECT * zaloguje vytvoření dočasného souboru většího než 1MB20 upper('ahoj') převede text na velká písmena FROM ti; lower('AHOJ') převede text na malá písmena Zobrazí seznam všech zaměstnanců, kteří jsou přímo nebo nepřímo podřízení zaměstnanci to_char(now(), 'DD.MM.YY') formátuje datum Online s id = 1 (včetně hloubky rekurze): to_char(now(), 'HH24:MI:SS') formátuje čas trim(' aa ') odstraní krajní mezery Dotazy do systémových tabulek můžeme zjistit aktuální stav a provoz databáze, případně WITH RECURSIVE os EXTRACT(dow FROM now()) vratí den v týdnu využití jednotlivých databázových objektů. AS (SELECT , 1 AS hloubka EXTRACT(day FROM now()) vrátí den v měsíci Stav otevřených spojení (přihlášených uživatelů do db) FROM zamestnanci WHERE id = 1 EXTRACT(month FROM now()) vrátí měsíc UNION ALL EXTRACT(year FROM now()) vrátí rok SELECT * FROM pg_stat_activity; SELECT z.*, hloubka + 1 date_trunc('month', now()) vrátí nejbližší začátek období FROM zamestnanci z Přerušení všech dotazů běžících déle než 5 min COALESCE(a,b,c) vrátí první ne NULL hodnotu JOIN os ON z.nadrizeny = os.id) array_lower(a, 1) vrátí spodní index pole nté dimenze SELECT pg_cancel_backend(pid) SELECT * array_upper(a,1) vrátí horní index pole nté dimenze FROM pg_stat_activity FROM os; random() vrátí pseudonáhodné číslo [0..1) generate_series(l,h) generuje posloupnost od l do h array_to_string(a, ',') serializuje pole 18 Rozdělí množinu do n podobně velkých podmnožin. Lze použít pro orientační určení mediánu a kvantilů. 19 Pro analýzu pomalých dotazů lze použít pgFouine nebo pgbadger. K monitorování lze použít 17 Ctid je fyzický identifikátor záznamu – v podstatě je to pozice záznamu v datovém souboru. Hodí extenze auto_explain (zapíše do logu prováděcí plán pomalého dotazu) 16 V případě, že záznam existuje, provede UPDATE, jinak INSERT. se pouze pro některého úlohy, neboť po aktualizaci má záznam jiné ctid. 20 Velké množství dočasných souborů může signalizovat nízkou hodnotu work_mem.
  • 4. Publikováno pod licencí BSD © Pavel Stěhule 2012 CSPUG pavel.stehule@gmail.com http://www.cspug.cz http://www.postgres.cz http://groups.google.com/group/postgresql-cz?hl=cs WHERE current_timestamp – query_start > interval '5 min'; což může být řádově dražší úloha než test na existenci hodnoty: Polymorfní funkce Využití jednotlivých db (včetně aktuálně přihlášených uživatelů k db) BEGIN IF EXISTS(SELECT 1 Polymorfní funkce jsou generické funkce, navržené tak, aby byly funkční s libovolným SELECT * FROM pg_stat_database; FROM zamestnanci z datovým typem. Místo konkrétního typu parametru použijeme generický typ – ANYELEMT, WHERE z.jmeno = _jmeno ANYARRAY, ANYNONARRAY, ANYRANGE a ANYENUM. Využití tabulek21 (počet čtení, počet zápisů, ...) FOR UPDATE24) THEN Generická funkce myleast by mohla vypadat následujícím způsobem: SELECT * FROM pg_stat_user_tables; ... END IF; CREATE OR REPLACE FUNCTION myleast(VARIADIC ANYARRAY) Využití IO, cache vztažené k tabulkám END; RETURNS ANYELEMENT AS $$ SELECT MIN(v) SELECT * FROM pg_statio_user_tables; Ošetření chyby FROM unnest($1) g(v); $$ LANGUAGE sql; Po instalaci doplňku pg_buffercache můžeme monitorovat obsah PostgreSQL cache. PL/pgSQL vytváří subtransakci pro každý chráněný blok – v případě zachycení výjimky je Funkce z doplňku pgstattuple umožňují provést nízkoúrovňovou diagnostiku datových tato subtransakce automaticky odvolána: souborů tabulek a indexů. SECURITY DEFINER funkce CREATE OR REPLACE FUNCTION fx(a int, b int) RETURNS int AS $$ Kód funkce v PostgreSQL běží s právy uživatele, který danou funkci aktivoval 25 (podobné je BEGIN Pl/pgSQL RETURN a / b; to i u triggerů). Toto chování lze změnit – pomocí atributu funkce SECURITY DEFINER. EXCEPTION WHEN division_by_zero THEN Tato technika se používá v situacích, kdy dočasně musíme zpřístupnit data, ke kterým PL/pgSQL je jednoduchý programovací jazyk vycházející z PL/SQL (Oracle) a potažmo ze RAISE EXCEPTION 'deleni nulou'; běžně není přístup. zjednodušeného programovacího jazyka ADA. Je těsně spjat s prostředím PostgreSQL – END; $$ LANGUAGE plpgsql IMMUTABLE STRICT; Následující funkci musí zaregistrovat (tím se stane jejím vlastníkem) uživatel s přístupem k dispozici jsou pouze datové typy, které nabízí PostgreSQL a operátory a funkce pro tyto k tabulce users: typy. Je to ideální lepidlo pro SQL příkazy, které mohou být vykonány na serveru, čímž se odbourávají latence způsobené sítí a protokolem. Funkce s defaultními parametry CREATE OR REPLACE FUNCTION verify_login(usrname text, password text) PostgreSQL podporuje defaultní hodnoty parametrů funkce – při volání funkce, lze RETURNS boolean AS $$ Základní funkce parametr, který má přiřazenou defaultní hodnotu vynechat. BEGIN IF EXISTS(SELECT * Funkce slouží k získání výsledku nebo provedení nějaké operace nad daty. Funkce Následující funkce vrátí tabulku existujících databází – a v případě, že parametr FROM users u v PostgreSQL mohou vracet skalární hodnotu (jeden atribut), záznam (více atributů), pole, WHERE u.passwd = md5(verify_login.password) vynecháme, tak tabulku databází aktuálního uživatele: AND u.name = verify_login.usrname) případně tabulku. Uvnitř funkcí nelze používat explicitně řízení transakcí 22. CREATE OR REPLACE FUNCTION dblist(username text THEN CREATE OR REPLACE FUNCTION novy_zamestnanec(jmeno text, DEFAULT CURRENT_USER) RETURN true; plny_uvazek boolean) ELSE RETURNS void AS $$ RETURNS SETOF text AS $$ RAISE WARNING 'unsuccessful login: %', usrname; BEGIN BEGIN PERFORM pg_sleep(random() * 3); IF plny_uvazek THEN RETURN QUERY SELECT datname::text RETURN false; INSERT INTO zamestnanci FROM pg_database d END; VALUES(novy_zamestnanec.jmeno); WHERE pg_catalog.pg_get_userbyid(d.datdba) END; ELSE = username; $$ SECURITY DEFINER INSERT INTO externisti RETURN; LANGUAGE plpgsql; VALUES(novy_zamestnanec.jmeno); END; END IF; $$ LANGUAGE plpgsql; Výhodou tohoto řešení je skutečnost, že i když útočník dokáže kompromitovat účet běžného END; uživatele, nezíská přístup k tabulce users. $$ LANGUAGE plpgsql; SELECT * FROM dblist('postgres'); SELECT * FROM dblist(username := 'postgres'); SELECT novy_zamestnanec('Stehule, true'); SELECT * FROM dblist(); Triggery SELECT novy_zamestnanec(jmeno := 'Stehule', true); Triggrem se v PostgreSQL myslí vazba mezi určitou událostí a jednou konkrétní funkcí. Iterace nad výsledkem dotazu Variadické funkce Pokud ta událost nastane, tak se vykoná dotyčná funkce. Triggerem můžeme sledovat V některých případech potřebujeme zpracovat výsledek dotazu – iterace FOR SELECT nám změny dat v tabulkách (klasické BEFORE, AFTER triggery), pokus o změnu dat v pohledu Variadická funkce je funkce s proměnlivým počtem parametrů. Posledním parametrem této (INSTEAD OF triggery), případně změny v systémovém katalogu ( EVENT triggery26). umožňuje provést určitý proces nad každým záznamem vrácené relace (pozor – v případě, funkce je tzv variadický parametr typu pole. že lze iteraci nahradit jedním čitelným SQL příkazem, měli bychom preferovat jeden SQL Nejčastěji používané jsou BEFORE, AFTER triggery volané po operacích INSERT, UPDATE příkaz): Následující ukázka je vlastní implementace funkce least – získání minimální hodnoty ze a DELETE. Vybrané funkce se mohou spouštět pro každý příkazem dotčený řádek ( ROW seznamu hodnot: DECLARE r record; trigger) nebo jednou pro příkaz (STATEMENT trigger). U řádkových triggerů máme BEGIN CREATE OR REPLACE FUNCTION myleast(VARIADIC numeric[]) k dispozici proměnnou NEW a OLD, obsahující záznam před provedením a po provedení FOR r IN SELECT * FROM pg_database RETURNS numeric AS $$ příkazu. Modifikací proměnné NEW můžeme záznam měnit (v BEFORE triggeru). V době LOOP SELECT MIN(v) RAISE NOTICE23 '%', r; provedení funkcí BEFORE triggerů je dotčený záznam ještě v nezměněné podobě. Funkce FROM unnest($1) g(v); END LOOP; $$ LANGUAGE sql; AFTER triggerů se volají v době, kdy tabulka obsahuje nové verze všech záznamů27. END; Zde se nejedná o PL/pgSQL funkci, ale o SQL funkci – pro triviální funkce je vhodnější Provedení akce pokud hodnota existuje používat tento jazyk: 25 Toto chování je podobné přístupu k uživatelským právům v Unixu. Pozor – prakticky ve všech Jedná se o typický vzor, kde je začátečnickou chybou rozhodovat nad počtem záznamů – ostatních db (včetně ANSI SQL) je to jinak – kód uvnitř funkce je vykonáván s právy vlastníka SELECT myleast(10,1,2); funkce. SELECT myleast(VARIADIC ARRAY[10,1,2]) 26 V PostgreSQL 9.3 21 Pro indexy - pg_stat_user_indexes 27 AFTER triggery používáme, když potřebujeme vidět změny v tabulce. Provádějí se až po vložení, 22 Používají se pouze subtransakce (implicitní) a to k zajištění ošetření zachycení výjimky. aktualizaci, odstranění všech řádků realizovaných jedním SQL příkazem a jsou proto o něco málo 23 Zobrazí text na ladící výstup. 24 Pozor na případnou RACE CONDITION. náročnější než BEFORE triggery – musí se udržovat fronta nevyhodnocených AFTER triggerů.
  • 5. Publikováno pod licencí BSD © Pavel Stěhule 2012 CSPUG pavel.stehule@gmail.com http://www.cspug.cz http://www.postgres.cz http://groups.google.com/group/postgresql-cz?hl=cs CREATE OR REPLACE FUNCTION pridej_razitko() CREATE OR REPLACE FUNCTION public.objednavka_bi() RETURNS trigger AS $$ RETURNS trigger AS $$ echo $SQL | psql postgres -q -t -A -v owner=$1 | BEGIN BEGIN psql -e postgres NEW.vlozeno := CURRENT_TIMESTAMP; CASE EXTRACT(year FROM NEW.vlozeno) NEW.provedl := SESSION_USER; WHEN 2011 THEN Jednodušší skripty můžeme napsat pomocí tzv online bloků 32 – kódu v plpgsq. RETURN NEW; INSERT INTO objednavka_2011 VALUES(NEW.*); END; WHEN 2012 THEN SQL=$(cat <<'EOF' $$ LANGUAGE plpgsql; INSERT INTO objednavka_2012 VALUES(NEW.*); SELECT set_config('custom.owner', :'owner', false); ELSE DO $$ CREATE TRIGGER orazitkuj_zmenu_zamestnanci RAISE EXCEPTION 'chybejici partition pro rok %', DECLARE name text; BEFORE INSERT OR UPDATE ON zamestnanci EXTRACT(year FROM NEW.vlozeno); BEGIN FOR EACH ROW END CASE; FOR name IN SELECT d.* EXECUTE PROCEDURE pridej_razitko(); RETURN NULL; FROM pg_database d END; WHERE pg_catalog.pg_get_userbyid(d.datdba) $$LANGUAGE plpgsql = current_setting('custom.owner') Partitioning LOOP CREATE TRIGGER objednavka_before_insert_trg RAISE NOTICE 'databaze=%', name; BEFORE INSERT ON objednavka END LOOP; Partitioning umožňuje rozdělit data v relaci do definovaných fyzicky oddělených disjunktních END; FOR EACH ROW EXECUTE PROCEDURE public.objednavka_bi() podmnožin. Později, při zpracování dotazu se použijí pouze ty partitions, které jsou pro $$ zpracování dotazu nezbytné. EOF Použití ) Dědičnost relací Při plánování dotazu se provádí identifikace partitions, které lze bezpečně vyjmout echo $SQL | psql postgres -v owner=$1 z plánování neboť obsahují pouze řádky, které 100% nevyhovují podmínkám, a tyto Velice specifickou vlastností PostgreSQL je částečná podpora OOP – podpora dědičnosti. Zajímavým trikem je generování obsahu ve formátu vhodném pro příkaz COPY, který se partitions se při zpracování dotazu nepoužijí. U každého dotazu, kde předpokládáme Relace může být vytvořena děděním jiné relace. Relace potomka obsahuje atributy rodiče pomocí roury natlačí do další instance konzole. Následující příkaz uloží aktuální stav aplikaci partitioningu si ověřujeme (příkaz EXPLAIN), že dotaz je napsán tak, že planner a případně další. Relace rodiče obsahuje všechny záznamy relací, které vznikly jejím provozních statistik a uloží je do souhrnné tabulky v databázi postgres. z něj dokáže detekovat nepotřebné partitions. přímým nebo nepřímým poděděním. Například z relace lidé (jméno, přijmení) podědím relace studenti (jméno, příjmení, obor) a zaměstnanci (jméno, příjmení, zařazení). Dotaz do SQL_stat=$(cat <<'EOF' postgres=# EXPLAIN SELECT * SELECT current_database(), current_timestamp::timestamp(0), relace lidé zobrazí jak všechny studenty tak všechny zaměstnance. FROM objednavka sum(n_tup_ins) tup_iserted, WHERE EXTRACT(year from vlozeno) > 2012; CREATE TABLE lide(jmeno text, prijmeni text); sum(n_tup_upd) tup_updated, QUERY PLAN CREATE TABLE studenti(obor text) INHERITS (lide); sum(n_tup_del) tup_deleted, ────────────────────────────────────────────────────────────── CREATE TABLE zamestnanci(zarazeni text) INHERITS(lide); FROM pg_stat_user_tables Result (cost=0.00..77.05 rows=1087 width=20) GROUP BY substring(relname from 1 for 2); -> Append (cost=0.00..77.05 rows=1087 width=20) EOF Partition je v PostgreSQL poděděná relace (tabulka) s definovaným omezením. Toto -> Seq Scan on objednavka ) omezení by mělo časově invariantní (tj neměl bych se snažit o partition pro „posledních 30 Filter: (date_part('year', vlozeno) > 2012) dní“) -> Seq Scan on objednavka_2013 for d in `psql -At -c "select datname from pg_database where Filter: (date_part('year', vlozeno) > 2012) pg_get_userbyid(datdba) <> 'postgres'"` CREATE TABLE objednavka(vlozeno date, castka numeric(12,2)); (6 rows) do CREATE TABLE objednavka_2012 echo $SQL_stat | psql -At $d -F$'t' | (CHECK(EXTRACT(year FROM vlozeno) = 2012)) INHERITS (objednavka); Kombinace bash a psql psql postgres -c "COPY statistics FROM stdin" done CREATE TABLE objednavka_2011 (CHECK(EXTRACT(year FROM vlozeno) = 2011)) psql lze použít i pro jednodušší skriptování (automatizaci) v kombinaci s Bashem. INHERITS (objednavka); V jednodušších případech stačí použít parametr -c "SQL příkaz". Ten ovšem nelze Online fyzické zálohování použít, když chceme použít dotaz parametrizovat pomocí psql proměnných. Omezení Kontinuální Ukázka využívá psql proměnných, heredoc zápis a binární ASCII unit separator : ✗ Partitions se nevytváří automaticky28 - musíme si je vytvořit manuálně. SQL=$(cat <<EOF Při kontinuálním zálohování archivujeme segmenty transakčního logu. Na základě obsahu SELECT datname, pg_catalog.pg_get_userbyid(d.datdba) transakčního logu jsme schopni zrekonstruovat stav databáze v libovolném okamžiku od ✗ Umístění záznamů do odpovídajících partitions se neprovádí automaticky29 - musíme si FROM pg_database d napsat distribuční trigger. vytvoření kompletní zálohy do okamžiku získání posledního validního segmentu WHERE pg_catalog.pg_get_userbyid(d.datdba) = :'owner' EOF transakčního logu. ✗ Počet partitions je omezen – neměl by přesáhnout 100 partitions jedné tabulky30. ) Konfigurace echo $SQL | psql postgres -q -t -A -v owner=$1 -F $'x1f'| Redistribuční trigger Pro vytvoření zálohy musíme povolit export segmentů transakčního logu a nastavit while IFS=$'x1f' read -r a b; do tzv archive_command33: Úkolem tohoto triggeru je přesun záznamu z rodičovské tabulky do odpovídající poděděné echo -e "datname='$a'towner='$b'"; archive_mode = on tabulky31 (pro větší počet partitions – cca nad 20 je praktické použití dynamického SQL). done archive_command = 'cp %p /var/backup/xlogs/%f' archive_timeout = 300 Oblíbeným trikem je vygenerování DDL příkazů v psql, které se pošlou jiné instanci psql, 28 Lze je vytvářet uvnitř triggerů, ale to nedoporučuji – hrozí race condition nebo ztráta výkonu kde se provedou. Následující skript odstraní všechny databáze vybraného uživatele: z důvodu čekání na zámek. Nejjednodušší a nejpraktičtější je vyrobit partitions na rok dopředu. SQL=$(cat <<EOF 29 Základem je distribuční BEFORE INSERT trigger nad rodičovskou tabulkou. V případě, že SELECT format('DROP DATABASE %I;', datname) dochází při UPDATE k přesunu mezi partitions je nutný BEFORE UPDATE trigger nad každou FROM pg_database d poděděnou tabulkou. WHERE pg_catalog.pg_get_userbyid(d.datdba) = :'owner' 30 Při velkém počtu partitions je problém s paměťovými nároky optimazátoru. EOF 32 Předávání parametrů dovnitř online bloku je o něco málo komplikovanější.. 31 Také aplikace může přesněji cílit a zapisovat do tabulek, které odpovídají partitions a nikoliv do ) 33 Vždy při naplnění segmentu transakčního logu nebo vypršení časového intervalu PostgreSQL volá rodičovské tabulky – tím se ušetří volání redistribučního triggeru a INSERT bude rychlejší. archive command, jehož úkolem je zajistit zápis segmentu na bezpečné médium.
  • 6. Publikováno pod licencí BSD © Pavel Stěhule 2012 CSPUG pavel.stehule@gmail.com http://www.cspug.cz http://www.postgres.cz http://groups.google.com/group/postgresql-cz?hl=cs úprava pg_hba.conf: můžeme provést fyzickou zálohu (zkopírování datového adresáře – full backup). K řízení Vytvoření zálohy local replication backup md5 replikace slouží následující funkce: Vynucení checkpointu a nastavení štítku (label) plné zálohy SELECT pg_start_backup(current_timestamp::text); Vytvoření uživatele backup: pg_xlog_replay_pause() pozastaví replikaci pg_xlog_replay_resume() obnoví replikaci Záloha datového adresáře – bez transakčních logů CREATE ROLE backup LOGIN REPLICATION; pg_is_xlog_replay_paused() vrátí true v případě pozastavené replikace ALTER ROLE backup PASSWORD 'heslo'; cd /usr/local/pgsql tar -cjf pgdata.tar.bz2 --exclude='pg_xlog' data/* Tato změna konfigurace vyžaduje restart databáze. Využití systémového katalogu Ukončení plné zálohy (full backup) Vlastní zálohování V PostgreSQL jsou všechna data potřebná pro provoz databáze uložena v systémových SELECT pg_stop_backup(); Spustíme příkaz pg_basebackup, kde uvedeme adresář, kde chceme mít uložený klon. tabulkách. Orientace v systémových tabulkách a pohledech není jednoduchá, lze ovšem využít jeden trik – většina dotazů do těchto objektů je pokryta příkazy v psql. A pokud se Adresář /var/backup/xlogs se začne plnit transakčními logy34. [pavel@diana ~]$ /usr/local/pgsql91/bin/pg_basebackup -D zaloha9 -U backup -v -P -x -c fast psql pustí s parametrem -E, tak dojde k zobrazení všech SQL příkazů, které se posílají do Password: DB – a tedy i dotazů do systémového katalogu. Obnova ze zálohy xlog start point: 0/21000020 50386/50386 kB (100%), 1/1 tablespace bash-4.2$ psql -E postgres Rozbalení poslední plné zálohy psql (9.3devel) xlog end point: 0/21000094 Type "help" for help. cd /usr/local/pgsql tar xvfj pgdata.tar.bz2 pg_basebackup: base backup completed postgres=# l ********* QUERY ********** V datovém adresáři vytvořte soubor recovery.conf, ve kterém definujete tzv Obnova ze zálohy SELECT d.datname as "Name", restore_commad (analogicky k archive_command): Obsah adresáře zálohy zkopírujeme do adresáře clusteru PostgreSQL a nastartujeme pg_catalog.pg_get_userbyid(d.datdba) as "Owner", server. Pozor - vlastníkem souborů bude uživatel, pod kterým byl spuštěn pg_catalog.pg_encoding_to_char(d.encoding) as "Encoding", restore_command = 'cp /var/backup/xlogs/%f %p' d.datcollate as "Collate", pg_basebackup, což pravděpodobně nebude uživatel postgres, a proto je nutné nejprve d.datctype as "Ctype", Pokud je čitelný adresář s transakčními logy původního serveru, tak můžeme tento adresář hromadně změnit vlastníka souborů. pg_catalog.array_to_string(d.datacl, E'n') AS "privileges" zkopírovat do datového adresáře obnoveného serveru. Jinak vytvoříme prázdný adresář FROM pg_catalog.pg_database d ORDER BY 1; mkdir pg_xlog Fyzická replikace ************************** Nastartujeme server. Po úspěšném startu by měl být soubor recovery.conf přejmenován na Potřebujeme opět uživatele s právem REPLICATION a přístupem k db replication. Běžně se v systémovém katalogu dohledává seznam tabulek, databází, uživatelů. recovery.done a v logu bychom měli najít záznam: Systémový katalog můžeme využít k zobrazení tabulek obsahující určité sloupce nebo Základem sekundárního (ro) serveru je klon primárního serveru (rw). LOG: archive recovery complete uložených procedur, které obsahují hledaný řetězec. LOG: database system is ready to accept connections Úpravy konfigurace – master Zobrazí tabulky obsahující hledaný sloupec: 35 wal_level = hot_standby PostgreSQL implicitně provádí obnovu do okamžiku, ke kterému dohledá poslední validní max_wal_senders = 1 SELECT attrelid::regclass segment transakčního logu. Záznam v logu referuje o postupu hledání segmentů: FROM pg_catalog.pg_attribute a # v případě větších db zvýšit WHERE a.attname = 'jmeno' AND NOT a.attisdropped; LOG: restored log file "000000010000000000000007" from archive wal_keep_segments = 100 LOG: restored log file "000000010000000000000008" from archive LOG: restored log file "000000010000000000000009" from archive Místo systémového katalogu lze použít standardizované information_schema: Úpravy konfigurace – slave cp: cannot stat `/var/backup/xlogs/00000001000000000000000A': SELECT table_name No such file or directory LOG: could not open file Pozor, po naklonování se slave nikdy nesmí spustit jako samostatný server. Pokud možno, FROM information_schema.columns "pg_xlog/00000001000000000000000A": No such file or directory klonujte s konfigurací wal_level = hot_standby na masteru. WHERE column_name ='jmeno'; hot_standby = on Zobrazí funkce, které ve zdrojovém kódu obsahují hledaný řetězec: Jednorázové hot_standby_feedback = on# pro zajištění pomalých dotazů na sl. SELECT oid::regprocedure Jednorázovým zálohováním se míní vytvoření klonu běžící databáze. Základem této Vytvořte soubor recovery.conf, který uložte do cluster adresáře serveru slave: FROM pg_proc metody je časově omezená replikace záznamů transakčních logů. Výhodou je jednoduchost WHERE prosrc ILIKE '%hello%'; použití – rychlost zálohování a obnovy ze zálohy je limitována rychlostí IO. standby_mode='on' primary_conninfo='host=localhost user=backup password=heslo' Konfigurace Vyhledávání v textu Před startem repliky vymažte log a pid file. Po startu by měl log obsahovat záznam: Tato metoda vyžaduje úpravu konfiguračního souboru a uživatele s oprávněním LOG: entering standby mode REPLICATION a přístupem k fiktivní databázi replication (přístup se povoluje LOG: consistent recovery state reached at 0/300014C Fulltext v souboru pg_hba.conf). LOG: record with zero length at 0/300014C LOG: database system is ready to accept read only connections Fultext umožňuje case insensitive vyhledávání slov (případně prefixů slov) v textu. S wal_level = archive drobnými úpravami lze vyhledávat lexémy a nebo lze při vyhledávání ignorovat diakritiku. LOG: streaming replication successfully connected to primary max_wal_senders = 1 Každé slovo se při fulltextovém zpracování definovaným způsobem transformuje. Seznam # v případě větších db zvýšit Po startu je slave v read only režimu. Signálem jej lze přepnout do role master. Pozor – těchto transformací (každá třída slov může mít jinou transformaci) pro určitý jazyk wal_keep_segments = 100 tato změna je nevratná. Nový slave se vytvoří kopií nového masteru. nazýváme konfigurací. Nejjednodušší konfigurací je konfigurace simple. Pro urychlení su postgres fulltextového vyhledávání potřebujeme fulltextový index (GiST, GIN funkcionální index) pg_ctl -D /usr/local/pgsql/data.repl/ promote CREATE INDEX ON obce 34 Transakční logy lze velice dobře komprimovat – např. asynchronně (viz BARMAN) USING gist ((to_tsvector('simple',nazev))); 35 Nastavením recovery_target_time v recovery.conf lze určit okamžik, kdy se má synchronizaci lze na každé replikovaném serveru dočasně blokovat – a během té doby s přehráváním transakčních logů skončit – například před okamžik, kdy došlo k odstranění důležitých dat.