PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

PostgreSQL:Auto-inkrementacja w oparciu o wielokolumnowe unikatowe ograniczenie

Byłoby dobrze, gdyby PostgreSQL obsługiwał inkrementację "na drugorzędnej kolumnie w indeksie wielokolumnowym", jak tabele MyISAM MySQL

Tak, ale pamiętaj, że w ten sposób MyISAM blokuje cały stół. Dzięki temu można bezpiecznie znaleźć największy +1 bez martwienia się o równoczesne transakcje.

W Postgresie możesz to zrobić również bez blokowania całego stołu. Wystarczy blokada doradcza i wyzwalacz:

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;

To daje:

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

Jest jedno zastrzeżenie. Blokady doradcze są utrzymywane do momentu zwolnienia lub wygaśnięcia sesji. Jeśli podczas transakcji wystąpi błąd, blokada zostanie zachowana i trzeba ją zwolnić ręcznie.

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

W Postgresie 9.1 możesz odrzucić wyzwalacz odblokowujący i zastąpić wywołanie pg_advisory_lock() funkcją pg_advisory_xact_lock(). Ten jest automatycznie wstrzymywany do czasu zakończenia transakcji i zwolniony.

Oddzielnie, trzymałbym się starej dobrej sekwencji. To przyspieszy sprawę - nawet jeśli nie wygląda to tak ładnie, gdy spojrzysz na dane.

Na koniec, unikatową sekwencję na (rok, miesiąc) można również uzyskać, dodając dodatkową tabelę, której kluczem podstawowym jest numer seryjny i której wartość (rok, miesiąc) ma na nią ograniczenie unikalności.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Funkcja LPAD() w PostgreSQL

  2. Tablica PHP do tablicy postgres

  3. Odpowiednik Postgresql GROUP_CONCAT?

  4. Importowanie pliku .sql w systemie Windows do postgresql

  5. SSL dla połączenia PostgreSQL nodejs