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

PostgreSQL:Auto-inkrement založený na vícesloupcovém jedinečném omezení

Bylo by hezké, kdyby PostgreSQL podporoval inkrementaci „na sekundárním sloupci v indexu s více sloupci“, jako jsou tabulky MyISAM MySQL

Jo, ale všimněte si, že tím MyISAM uzamkne celý váš stůl. Díky tomu můžete bezpečně najít největší +1 bez obav ze souběžných transakcí.

V Postgres to můžete udělat také a bez zamykání celého stolu. Poradní zámek a spoušť budou dost dobré:

CREATE TYPE animal_grp AS ENUM ('fish','mammal','bird');

CREATE TABLE animals (
    grp animal_grp NOT NULL,
    id INT NOT NULL DEFAULT 0,
    name varchar NOT NULL,
    PRIMARY KEY (grp,id)
);

CREATE OR REPLACE FUNCTION animals_id_auto()
    RETURNS trigger AS $$
DECLARE
    _rel_id constant int := 'animals'::regclass::int;
    _grp_id int;
BEGIN
    _grp_id = array_length(enum_range(NULL, NEW.grp), 1);

    -- Obtain an advisory lock on this table/group.
    PERFORM pg_advisory_lock(_rel_id, _grp_id);

    SELECT  COALESCE(MAX(id) + 1, 1)
    INTO    NEW.id
    FROM    animals
    WHERE   grp = NEW.grp;

    RETURN NEW;
END;
$$ LANGUAGE plpgsql STRICT;

CREATE TRIGGER animals_id_auto
    BEFORE INSERT ON animals
    FOR EACH ROW WHEN (NEW.id = 0)
    EXECUTE PROCEDURE animals_id_auto();

CREATE OR REPLACE FUNCTION animals_id_auto_unlock()
    RETURNS trigger AS $$
DECLARE
    _rel_id constant int := 'animals'::regclass::int;
    _grp_id int;
BEGIN
    _grp_id = array_length(enum_range(NULL, NEW.grp), 1);

    -- Release the lock.
    PERFORM pg_advisory_unlock(_rel_id, _grp_id);

    RETURN NEW;
END;
$$ LANGUAGE plpgsql STRICT;

CREATE TRIGGER animals_id_auto_unlock
    AFTER INSERT ON animals
    FOR EACH ROW
    EXECUTE PROCEDURE animals_id_auto_unlock();

INSERT INTO animals (grp,name) VALUES
    ('mammal','dog'),('mammal','cat'),
    ('bird','penguin'),('fish','lax'),('mammal','whale'),
    ('bird','ostrich');

SELECT * FROM animals ORDER BY grp,id;

Výsledkem je:

  grp   | id |  name   
--------+----+---------
 fish   |  1 | lax
 mammal |  1 | dog
 mammal |  2 | cat
 mammal |  3 | whale
 bird   |  1 | penguin
 bird   |  2 | ostrich
(6 rows)

Existuje jedno upozornění. Poradní zámky jsou drženy, dokud se neuvolní nebo dokud nevyprší relace. Pokud během transakce dojde k chybě, zámek zůstane zachován a musíte jej uvolnit ručně.

SELECT pg_advisory_unlock('animals'::regclass::int, i)
FROM generate_series(1, array_length(enum_range(NULL::animal_grp),1)) i;

V Postgresu 9.1 můžete zahodit spouštěč odemknutí a nahradit volání pg_advisory_lock() voláním pg_advisory_xact_lock(). Ten je automaticky zadržen a uvolněn na konci transakce.

Na samostatnou poznámku bych se držel použití staré dobré sekvence. To vše urychlí – i když to při pohledu na data nevypadá tak pěkně.

A konečně, jedinečnou sekvenci pro (rok, měsíc) combo lze také získat přidáním další tabulky, jejíž primární klíč je sériový a jejíž hodnota (rok, měsíc) má jedinečné omezení.



  1. Jak se EDB stala lídrem na trhu Postgres

  2. Kurzy databáze SQL Server ( MSSQL DBA ) pro začínající správce databází

  3. Jak deklarovat proměnnou v MySQL?

  4. Obnova databáze Oracle