tiny ‘n’ smart
database layer

Odkazy: dibi | API reference

Oznámení

Omlouváme se, provoz fóra byl ukončen

PostgreSQL a práce se sekvencemi

před 7 lety

Milo
Nette Core | 1124

V PostgreSQL se sekvece nejčastěji používají jako autoinkrementální hodnota pro primární klíč (obdoba AUTO_INCREMENT u MySQL). Sekvenci lze ale použít i samostatně.

CREATE SEQUENCE counter;

Tím jsme vytvořili sekvenci counter s výchozími parametry. Sekvence je vlastně jen speciální jednořádková tabulka a lze tak s ní i částečně zacházet.

$counterInfo = $dibi->query('SELECT * FROM %n', 'counter')->fetch();
/*
DibiRow Object
(
    [sequence_name] => counter
    [last_value] => 1
    [increment_by] => 1
    [max_value] => 9223372036854775807
    [min_value] => 1
    [cache_value] => 1
    [log_cnt] => 1
    [is_cycled] => f
    [is_called] => f
)
*/

Maximální hodnota devět ?trilionů? už na ledacos vystačí. Následující číslo v pořadí sekvence získáme pomocí funkce nextval().

// Tohle funguje, ale má to malý háček. Generuje SELECT nextval('counter')
$next = $dibi->query('SELECT nextval(%s)', 'counter')->fetchSingle();   // 1
$next = $dibi->query('SELECT nextval(%s)', 'counter')->fetchSingle();   // 2
$next = $dibi->query('SELECT nextval(%s)', 'counter')->fetchSingle();   // 3

// Lepší. Generuje SELECT nextval('"counter"')
$next = $dibi->query('SELECT nextval(%s)', $dibi->translate('%n', 'counter'))->fetchSingle();   // 1
$next = $dibi->query('SELECT nextval(%s)', $dibi->translate('%n', 'counter'))->fetchSingle();   // 2
$next = $dibi->query('SELECT nextval(%s)', $dibi->translate('%n', 'counter'))->fetchSingle();   // 3

/*
Rozdíl je v těch uvozovkách. Je to obdobné jako u názvů tabulek a sloupců. Pokud se uvozovky vynechají,
název sekvence se vždy převede na malá písmena. Většinou to tak vyhovuje. Dál budu používat zápis
bez uvozovek, je to názornější ikdyž chybné.
*/

Občas je potřeba sekvenci změnit na jinou hodnotu. K tomu poslouží funkce setval().

// Syntax: setval(regclass sequenceName, bigint newValue, boolean isCalled)

$dibi->query('SELECT setval(%s, %i, %b)', 'counter', 10, false);
$next = $dibi->query('SELECT nextval(%s)', 'counter')->fetchSingle();   // 10

$dibi->query('SELECT setval(%s, %i, %b)', 'counter', 10, true);
$next = $dibi->query('SELECT nextval(%s)', 'counter')->fetchSingle();   // 11

/*
Třetí parametr je trochu záludný. Určuje, zda už byla sekvence volána a nastavovaná hodnota už byla vrácena,
nebo ještě ne. Záleží na nás co chceme. Pokud třetí parametr isCalled vynecháme, je to jako by byl true.
*/

Někdy je potřeba zjistit, jaká hodnota byla naposledy vrácena aniž bychom sekvenci inkrementovali. Lze použít funkci currval().

$dibi->query('SELECT setval(%s, %i, %b)', 'counter', 10, true);
$curr = $dibi->query('SELECT currval(%s)', 'counter')->fetchSingle();   // 10

$dibi->query('SELECT setval(%s, %i, %b)', 'counter', 10, false);
$curr = $dibi->query('SELECT currval(%s)', 'counter')->fetchSingle();
// DibiDriverException protože isCalled je false, tedy hodnota sekvence nebyla ještě vrácena

Tuto trojici funkcí doplňuje funkce lastval(). Nepřijímá žádné argumenty a vrací poslední vrácenou hodnotu poslední sekvence v dané session. Pokud v session nebyla žádná vrácena. Opět DibiDriverException.

Jednou z vyjímečných vlastností sekvencí je to, že jsou „imunní“ vůči transakcím. Jakmile se hodnota ze sekvence přečte a proběhne ROLLBACK, hodnota už se nevrací.

BEGIN;
SELECT nextval('"counter"');    -- 10
SELECT nextval('"counter"');    -- 11
ROLLBACK;

SELECT nextval('"counter"');    -- 12
SELECT nextval('"counter"');    -- 13

Toho lze s výhodou využít jako zdroj společného identifikátoru pro více různých aplikací. Protože ať se děje co se děje, nextval() vrací vždy novou hodnotu.