Oznámení
PostgreSQL a práce se sekvencemi
před 8 lety
- Milo
- Nette Core | 1119
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.