Добавление 'serial' в существующий столбец в Postgres
У меня есть небольшая таблица (~ 30 строк) в моей базе данных Postgres 9.0 с полем целочисленного идентификатора (первичный ключ), который в настоящее время содержит уникальные последовательные целые числа, начинающиеся с 1, но которые не были созданы с использованием ключевого слова 'serial'.
Как мне изменить эту таблицу таким образом, чтобы с этого момента вставки в эту таблицу приводят к тому, что это поле будет вести себя так, как если бы оно было создано с помощью 'serial' в качестве типа?
Ответы
Ответ 1
Посмотрите на следующие команды (особенно закомментированный блок).
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;
Ответ 2
Вы также можете использовать 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');
Ответ 3
Неинтерактивное решение
Просто добавление к двум другим ответам, для тех из нас, кто должен иметь эту Sequence
созданную неинтерактивным скриптом, при исправлении, например, DB реального времени.
То есть, когда вы не хотите SELECT
значение вручную и введите его самостоятельно в последующий оператор CREATE
.
Короче говоря, вы не можете этого сделать:
CREATE SEQUENCE foo_a_seq
START WITH ( SELECT max(a) + 1 FROM foo);
... поскольку предложение START [WITH]
в CREATE SEQUENCE
ожидает значение, а не подзапрос.
Примечание. Как правило, это относится ко всем SELECT
, не относящимся к CRUD (т.е.: ничего, кроме INSERT
, SELECT
, UPDATE
, DELETE
) в pgSQL AFAIK.
Однако setval()
делает! Таким образом, абсолютно верно:
SELECT setval('foo_a_seq', max(a)) FROM foo;
Если нет данных, и вы не знаете (хотите) об этом, используйте coalesce()
для установки значения по умолчанию:
SELECT setval('foo_a_seq', coalesce(max(a), 0)) FROM foo;
В несвязанной заметке вы также можете указать столбец, владеющий 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)) 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
Ответ 4
Ответы на эту страницу устаревшие. Для справок в будущем см. Этот SO ответ