Наличие порядкового столбца без пробелов - PullRequest
0 голосов
/ 28 октября 2018

Я хочу иметь порядковый столбец, в котором значения всегда начинаются с 1 и не имеют пробелов.Я разработал решение с помощью триггеров, но я хотел бы знать, есть ли лучший или более элегантный способ.

ПЕРЕД триггером INSERT перенумеровывает строки, которые идут после вставленного значения.Если значение не указано или слишком велико, для него устанавливается число строк + 1. Аналогично, триггер AFTER DELETE перенумеровывает строки, которые идут после удаленного значения.Оба триггера блокируют строки перед изменением значения.

CREATE OR REPLACE FUNCTION ids_insert() RETURNS trigger AS $BODY$
DECLARE
    _lock_sql text;
    _id bigint;
BEGIN
    IF TG_OP = 'INSERT' THEN
        IF NEW.id < 1 THEN
            RAISE EXCEPTION 'ID must be greater than zero.';
        END IF;
        EXECUTE format('SELECT COUNT(*) + 1 FROM %I', TG_TABLE_NAME)
            INTO _id;
        IF NEW.id IS NULL OR NEW.id > _id THEN
            NEW.id := _id;
        ELSE
            _lock_sql := format(
                'SELECT id FROM %I '
                'WHERE id >= %s '
                'ORDER BY id DESC '
                'FOR UPDATE', TG_TABLE_NAME, NEW.id
            );
            FOR _id IN EXECUTE _lock_sql LOOP
                EXECUTE format('UPDATE %I SET id = id + 1 WHERE id = %s', TG_TABLE_NAME, _id);
            END LOOP;
        END IF;
    ELSE
        IF NEW.id != OLD.id THEN
            RAISE EXCEPTION 'Changing the ID directly is not allowed.';
        END IF;
    END IF;

    RETURN NEW;
END;
$BODY$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION ids_delete() RETURNS trigger AS $BODY$
DECLARE
    _lock_sql text;
    _id bigint;
BEGIN
    _lock_sql := format(
        'SELECT id FROM %I '
        'WHERE id > %s '
        'ORDER BY id '
        'FOR UPDATE', TG_TABLE_NAME, OLD.id
    );
    FOR _id IN EXECUTE _lock_sql LOOP
        EXECUTE format('UPDATE %I SET id = id - 1 WHERE id = %s', TG_TABLE_NAME, _id);
    END LOOP;
    RETURN OLD;
END;
$BODY$ LANGUAGE plpgsql;

CREATE TABLE test (
    id bigint PRIMARY KEY,
    ...
)

CREATE TRIGGER test_insert BEFORE INSERT OR UPDATE OF id ON test
    FOR EACH ROW WHEN (pg_trigger_depth() < 1) EXECUTE PROCEDURE ids_insert();

CREATE TRIGGER test_delete AFTER DELETE ON test
    FOR EACH ROW EXECUTE PROCEDURE ids_delete();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...