sql >> Database teknologi >  >> RDS >> PostgreSQL

PostgreSQL:Auto-increment baseret på multi-kolonne unik begrænsning

Det ville være rart, hvis PostgreSQL understøttede inkrementering "på en sekundær kolonne i et indeks med flere kolonner" som MySQL's MyISAM-tabeller

Ja, men bemærk, at ved at gøre det låser MyISAM hele dit bord. Hvilket så gør det sikkert at finde den største +1 uden at bekymre dig om samtidige transaktioner.

I Postgres kan du også gøre dette, og uden at låse hele bordet. En rådgivende lås og en trigger vil være godt nok:

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;
 

Dette giver:

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

Der er én advarsel. Rådgivende låse holdes, indtil de frigives, eller indtil sessionen udløber. Hvis der opstår en fejl under transaktionen, holdes låsen rundt, og du skal frigive den manuelt.

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

I Postgres 9.1 kan du kassere oplåsningsudløseren og erstatte pg_advisory_lock()-kaldet med pg_advisory_xact_lock(). Den tilbageholdes automatisk indtil og frigives ved slutningen af ​​transaktionen.

På en separat note, ville jeg holde mig til at bruge en god gammel sekvens. Det vil gøre tingene hurtigere – også selvom det ikke ser så smukt ud, når du ser på dataene.

Endelig kunne en unik sekvens pr. (år, måned) kombination også opnås ved at tilføje en ekstra tabel, hvis primærnøgle er en seriel, og hvis (år, måned) værdi har en unik begrænsning på sig.



  1. Valg af et SQL Server-overvågningsværktøj, der passer til dine behov

  2. Oracles containere til J2EE (OC4J) i R12

  3. Sådan fanges SQLServer timeout undtagelser

  4. Createuser:kunne ikke oprette forbindelse til database postgres:FATAL:rolle tom eksisterer ikke