Добавление «серийный номер» в существующий столбец в Postgres - PullRequest
74 голосов
/ 29 февраля 2012

В моей базе данных Postgres 9.0 есть небольшая таблица (~ 30 строк) с полем целочисленного идентификатора (первичным ключом), который в настоящее время содержит уникальные последовательные целые числа, начиная с 1, но который не был создан с использованием ключевого слова serial.

Как я могу изменить эту таблицу так, чтобы отныне вставки в эту таблицу приводили к тому, что это поле будет вести себя так, как если бы оно было создано с типом 'serial'?

Ответы [ 3 ]

108 голосов
/ 29 февраля 2012

Посмотрите на следующие команды (особенно закомментированный блок).

DROP TABLE foo;
DROP TABLE bar;

CREATE TABLE foo (a int, b text);
CREATE TABLE bar (a serial, b text);

INSERT INTO foo (a, b) SELECT i, 'foo ' || i::text FROM generate_series(1, 5) i;
INSERT INTO bar (b) SELECT 'bar ' || i::text FROM generate_series(1, 5) i;

-- blocks of commands to turn foo into bar
CREATE SEQUENCE foo_a_seq;
ALTER TABLE foo ALTER COLUMN a SET DEFAULT nextval('foo_a_seq');
ALTER TABLE foo ALTER COLUMN a SET NOT NULL;
ALTER SEQUENCE foo_a_seq OWNED BY foo.a;    -- 8.2 or later

SELECT MAX(a) FROM foo;
SELECT setval('foo_a_seq', 5);  -- replace 5 by SELECT MAX result

INSERT INTO foo (b) VALUES('teste');
INSERT INTO bar (b) VALUES('teste');

SELECT * FROM foo;
SELECT * FROM bar;
38 голосов
/ 16 января 2014

Вы также можете использовать START WITH для запуска последовательности с определенной точки, хотя setval выполняет то же самое, что и в ответе Эйлера, например,

SELECT MAX(a) + 1 FROM foo;
CREATE SEQUENCE foo_a_seq START WITH 12345; -- replace 12345 with max above
ALTER TABLE foo ALTER COLUMN a SET DEFAULT nextval('foo_a_seq');
21 голосов
/ 28 мая 2018

Неинтерактивное решение

Просто добавление к двум другим ответам, для тех из нас, кому необходимо, чтобы эти Sequence были созданы с помощью неинтерактивного сценария , при этом, например, при исправлении базы данных live-ish.

То есть, когда вы не хотите SELECT значение вручную и введите его самостоятельно в последующий оператор CREATE.

Короче говоря, вы можете не делать:

CREATE SEQUENCE foo_a_seq
    START WITH ( SELECT max(a) + 1 FROM foo);

... поскольку предложение START [WITH] в CREATE SEQUENCE ожидает значение , а не подзапрос.

Примечание. Как правило, это относится ко всем операторам, не относящимся к CRUD (, т.е. : что-либо, кроме INSERT, SELECT, UPDATE, DELETE) в * 1030. * pgSQL AFAIK.

Однако, setval() делает! Таким образом, абсолютно нормально следующее:

SELECT setval('foo_a_seq', max(a)) FROM foo;

Если данных нет и вы не хотите их знать, используйте coalesce(), чтобы установить значение по умолчанию:

SELECT setval('foo_a_seq', coalesce(max(a), 0)) FROM foo;
--                         ^      ^         ^
--                       defaults to:       0

Однако текущее значение последовательности, установленное на 0, является неуклюжим, если не недопустимым.
Использование трехпараметрической формы setval было бы более уместным:

--                                             vvv
SELECT setval('foo_a_seq', coalesce(max(a), 0) + 1, false) FROM foo;
--                                                  ^   ^
--                                                is_called

Установка необязательного третьего параметра от setval до false будет препятствовать следующему nextval продвигать последовательность перед возвратом значения, и, таким образом:

следующий nextval вернет точно указанное значение, и последовательность начинается со следующего nextval.

- с эта запись в документации

В неродственной заметке вы также можете указать столбец, которому принадлежит Sequence напрямую, с помощью CREATE, вам не нужно будет изменять его позже:

CREATE SEQUENCE foo_a_seq OWNED BY foo.a;

В итоге:

CREATE SEQUENCE foo_a_seq OWNED BY foo.a;
SELECT setval('foo_a_seq', coalesce(max(a), 0) + 1, false) FROM foo;
ALTER TABLE foo ALTER COLUMN a SET DEFAULT nextval('foo_a_seq'); 

Использование Function

В качестве альтернативы, если вы планируете сделать это для нескольких столбцов, вы можете выбрать использование фактического Function.

CREATE OR REPLACE FUNCTION make_into_serial(table_name TEXT, column_name TEXT) RETURNS INTEGER AS $$
DECLARE
    start_with INTEGER;
    sequence_name TEXT;
BEGIN
    sequence_name := table_name || '_' || column_name || '_seq';
    EXECUTE 'SELECT coalesce(max(' || column_name || '), 0) + 1 FROM ' || table_name
            INTO start_with;
    EXECUTE 'CREATE SEQUENCE ' || sequence_name ||
            ' START WITH ' || start_with ||
            ' OWNED BY ' || table_name || '.' || column_name;
    EXECUTE 'ALTER TABLE ' || table_name || ' ALTER COLUMN ' || column_name ||
            ' SET DEFAULT nextVal(''' || sequence_name || ''')';
    RETURN start_with;
END;
$$ LANGUAGE plpgsql VOLATILE;

Используйте это так:

INSERT INTO foo (data) VALUES ('asdf');
-- ERROR: null value in column "a" violates not-null constraint

SELECT make_into_serial('foo', 'a');
INSERT INTO foo (data) VALUES ('asdf');
-- OK: 1 row(s) affected
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...