PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

PostgreSQL:Автоматично увеличаване на базата на уникално ограничение за няколко колони

Би било хубаво PostgreSQL да поддържа увеличаване на "на вторична колона в индекс с множество колони" като MyISAM таблиците на MySQL

Да, но имайте предвид, че по този начин MyISAM заключва цялата ви таблица. Което след това прави безопасно намирането на най-голямото +1, без да се притеснявате за едновременни транзакции.

В Postgres можете да направите и това и без да заключвате цялата таблица. Препоръката за заключване и спусъкът ще бъдат достатъчно добри:

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;

Това дава:

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

Има едно предупреждение. Консултативните заключвания се задържат до освобождаване или до изтичане на сесията. Ако възникне грешка по време на транзакцията, заключването се запазва и трябва да го освободите ръчно.

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

В Postgres 9.1 можете да отхвърлите тригера за отключване и да замените извикването pg_advisory_lock() с pg_advisory_xact_lock(). Този автоматично се задържа до и се освобождава в края на транзакцията.

Като отделна бележка, бих се придържал към използването на добра стара последователност. Това ще направи нещата по-бързи – дори и да не изглежда толкова красиво, когато погледнете данните.

И накрая, уникална последователност за комбо (година, месец) може да се получи и чрез добавяне на допълнителна таблица, чийто първичен ключ е сериен и чиято стойност (година, месец) има уникално ограничение за него.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Как работи Round() в PostgreSQL

  2. Импортирайте SQL дъмпа в PostgreSQL база данни

  3. Свържете приложението iPhone към PostgreSQL с помощта на Libpq

  4. Опашка за задания като SQL таблица с множество потребители (PostgreSQL)

  5. Свързване с база данни PostgreSQL чрез SSH тунелиране в Python