Как сбросить последовательность первичных ключей postgres, если они не синхронизированы?
Я столкнулся с проблемой, что моя последовательность первичных ключей не синхронизирована со строками таблицы.
То есть, когда я вставляю новую строку, я получаю ошибку дублированного ключа, потому что последовательность, подразумеваемая в последовательном типе данных, возвращает число, которое уже существует.
Кажется, это вызвано тем, что импорт / восстановление не поддерживает последовательность должным образом.
35 ответов
-- Login to psql and run the following
-- What is the result?
SELECT MAX(id) FROM your_table;
-- Then run...
-- This should be higher than the last result.
SELECT nextval('your_table_id_seq');
-- If it's not higher... run this set the sequence last to your highest id.
-- (wise to run a quick pg_dump first...)
BEGIN;
-- protect against concurrent inserts while you update the counter
LOCK TABLE your_table IN EXCLUSIVE MODE;
-- Update the sequence
SELECT setval('your_table_id_seq', COALESCE((SELECT MAX(id)+1 FROM your_table), 1), false);
COMMIT;
pg_get_serial_sequence
может использоваться, чтобы избежать любых неправильных предположений о названии последовательности. Это сбрасывает последовательность в одном кадре:
SELECT pg_catalog.setval(pg_get_serial_sequence('table_name', 'id'), (SELECT MAX(id) FROM table_name)+1);
Или более кратко:
SELECT pg_catalog.setval(pg_get_serial_sequence('table_name', 'id'), MAX(id)) FROM table_name;
Однако эта форма не может правильно обрабатывать пустые таблицы, так как max(id) является нулем, и вы также не можете установить 0, потому что это будет вне диапазона последовательности. Одним из способов решения этой проблемы является обращение к ALTER SEQUENCE
синтаксис т.е.
ALTER SEQUENCE table_name_id_seq RESTART WITH 1;
ALTER SEQUENCE table_name_id_seq RESTART; -- 8.4 or higher
Но ALTER SEQUENCE
имеет ограниченное использование, потому что имя последовательности и значение перезапуска не могут быть выражениями.
Кажется, лучшее универсальное решение - позвонить setval
с false в качестве 3-го параметра, что позволяет нам указать "следующее значение для использования":
SELECT setval(pg_get_serial_sequence('t1', 'id'), coalesce(max(id),0) + 1, false) FROM t1;
Это помечает все мои коробки:
- избегает жесткого кодирования фактического имени последовательности
- правильно обрабатывает пустые таблицы
- обрабатывает таблицы с существующими данными и не оставляет дыры в последовательности
Наконец, обратите внимание, что pg_get_serial_sequence
работает только если последовательность принадлежит столбцу. Это будет иметь место, если столбец увеличения был определен как serial
тип, однако, если последовательность была добавлена вручную, необходимо обеспечить ALTER SEQUENCE .. OWNED BY
также выполняется.
т.е. если serial
Тип был использован для создания таблицы, все это должно работать:
CREATE TABLE t1 (
id serial,
name varchar(20)
);
SELECT pg_get_serial_sequence('t1', 'id'); -- returns 't1_id_seq'
-- reset the sequence, regardless whether table has rows or not:
SELECT setval(pg_get_serial_sequence('t1', 'id'), coalesce(max(id),0) + 1, false) FROM t1;
Но если последовательности были добавлены вручную:
CREATE TABLE t2 (
id integer NOT NULL,
name varchar(20)
);
CREATE SEQUENCE t2_custom_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER TABLE t2 ALTER COLUMN id SET DEFAULT nextval('t2_custom_id_seq'::regclass);
ALTER SEQUENCE t2_custom_id_seq OWNED BY t2.id; -- required for pg_get_serial_sequence
SELECT pg_get_serial_sequence('t2', 'id'); -- returns 't2_custom_id_seq'
-- reset the sequence, regardless whether table has rows or not:
SELECT setval(pg_get_serial_sequence('t2', 'id'), coalesce(max(id),0) + 1, false) FROM t1;
Самый короткий и быстрый способ:
SELECT setval('tbl_tbl_id_seq', max(tbl_id)) FROM tbl;
tbl_id
быть serial
столбец таблицы tbl
, опираясь на последовательность tbl_tbl_id_seq
(которое является автоматическим именем по умолчанию).
Если вы не знаете имя присоединенной последовательности (которая не обязательно должна быть в форме по умолчанию), используйте pg_get_serial_sequence()
:
SELECT setval(pg_get_serial_sequence('tbl', 'tbl_id'), max(tbl_id)) FROM tbl;
Здесь нет единой ошибки. По документации:
Форма с двумя параметрами устанавливает в поле last_value последовательности указанное значение и устанавливает его
is_called
поле к истине, что означает, что следующийnextval
продвинет последовательность перед возвратом значения.
Жирный акцент мой.
совпадение
Пока нет защиты от одновременной активности последовательности или записи в таблицу в вышеупомянутых запросах. Если это актуально, вы можете заблокировать таблицу в монопольном режиме. Он не позволяет одновременным транзакциям записывать большее число, пока вы пытаетесь синхронизироваться. (Это также временно блокирует безвредные записи, не связываясь с максимальным числом.)
Но это не учитывает клиентов, которые могли заранее получить порядковые номера без каких-либо блокировок на главной таблице (что может случиться). Чтобы учесть это, тоже только увеличивайте текущее значение последовательности, но никогда не уменьшайте его. Это может показаться параноидальным, но это согласуется с природой последовательностей и защитой от проблем параллелизма.
BEGIN;
LOCK TABLE tbl IN EXCLUSIVE MODE;
SELECT setval('tbl_tbl_id_seq', max(tbl_id))
FROM tbl
HAVING max(tbl_id) > (SELECT last_value FROM tbl_tbl_id_seq);
COMMIT;
Это сбросит все последовательности из общедоступного, не делая предположений о именах таблиц или столбцов. Проверено на версии 8.4
CREATE OR REPLACE FUNCTION "reset_sequence" (tablename text, columnname text, sequence_name text) RETURNS "pg_catalog"."void" AS
$body$
DECLARE
BEGIN
EXECUTE 'SELECT setval( ''' || sequence_name || ''', ' || '(SELECT MAX(' || columnname || ') FROM ' || tablename || ')' || '+1)';
END;
$body$ LANGUAGE 'plpgsql';
select table_name || '_' || column_name || '_seq', reset_sequence(table_name, column_name, table_name || '_' || column_name || '_seq') from information_schema.columns where column_default like 'nextval%';
ALTER SEQUENCE sequence_name RESTART WITH (SELECT max(id) FROM table_name); Не работает
Скопировано из ответа @tardate:
SELECT setval(pg_get_serial_sequence('table_name', 'id'), MAX(id)) FROM table_name;
В приведенном ниже примере имя
таблицыusers
и имя
схемыpublic
(схема по умолчанию), замените ее в соответствии с вашими потребностями.
1. Проверьте максимальный идентификатор:
SELECT MAX(id) FROM public.users;
2. Проверьте следующее значение:
SELECT nextval('public."users_id_seq"');
3. Если
max id
больше, чем
next value
, сбросьте его:
SELECT setval('public."users_id_seq"',
(SELECT MAX(id) FROM public.users)
);
Эта команда только для изменения автоматически сгенерированного значения последовательности клавиш в postgresql
ALTER SEQUENCE "your_sequence_name" RESTART WITH 0;
Вместо нуля вы можете поставить любое число, с которого вы хотите перезапустить последовательность.
имя последовательности по умолчанию будет "TableName_FieldName_seq"
, Например, если имя вашей таблицы "MyTable"
и ваше имя поля "MyID"
тогда ваше имя последовательности будет "MyTable_MyID_seq"
,
Этот ответ такой же, как ответ @ murugesanponappan, но в его решении есть синтаксическая ошибка. Вы не можете использовать подзапрос (select max()...)
в alter
команда. Так что либо вы должны использовать фиксированное числовое значение, либо вам нужно использовать переменную вместо подзапроса.
Сброс всех последовательностей, никаких предположений об именах, за исключением того, что первичным ключом каждой таблицы является "id":
CREATE OR REPLACE FUNCTION "reset_sequence" (tablename text, columnname text)
RETURNS "pg_catalog"."void" AS
$body$
DECLARE
BEGIN
EXECUTE 'SELECT setval( pg_get_serial_sequence(''' || tablename || ''', ''' || columnname || '''),
(SELECT COALESCE(MAX(id)+1,1) FROM ' || tablename || '), false)';
END;
$body$ LANGUAGE 'plpgsql';
select table_name || '_' || column_name || '_seq', reset_sequence(table_name, column_name) from information_schema.columns where column_default like 'nextval%';
Я предлагаю это решение, найденное на postgres wiki. Он обновляет все последовательности ваших таблиц.
SELECT 'SELECT SETVAL(' ||
quote_literal(quote_ident(PGT.schemaname) || '.' || quote_ident(S.relname)) ||
', COALESCE(MAX(' ||quote_ident(C.attname)|| '), 1) ) FROM ' ||
quote_ident(PGT.schemaname)|| '.'||quote_ident(T.relname)|| ';'
FROM pg_class AS S,
pg_depend AS D,
pg_class AS T,
pg_attribute AS C,
pg_tables AS PGT
WHERE S.relkind = 'S'
AND S.oid = D.objid
AND D.refobjid = T.oid
AND D.refobjid = C.attrelid
AND D.refobjsubid = C.attnum
AND T.relname = PGT.tablename
ORDER BY S.relname;
Как использовать (из Вики Postgres):
- Сохраните это в файл, скажем, "reset.sql"
- Запустите файл и сохраните его вывод так, чтобы он не включал обычные заголовки, затем запустите этот вывод. Пример:
Пример:
psql -Atq -f reset.sql -o temp
psql -f temp
rm temp
Оригинальная статья (также с исправлением последовательности владения) здесь
Эти функции сопряжены с опасностями, когда имена последовательностей, имен столбцов, имен таблиц или схем имеют смешные символы, такие как пробелы, знаки пунктуации и тому подобное. Я написал это:
CREATE OR REPLACE FUNCTION sequence_max_value(oid) RETURNS bigint
VOLATILE STRICT LANGUAGE plpgsql AS $$
DECLARE
tabrelid oid;
colname name;
r record;
newmax bigint;
BEGIN
FOR tabrelid, colname IN SELECT attrelid, attname
FROM pg_attribute
WHERE (attrelid, attnum) IN (
SELECT adrelid::regclass,adnum
FROM pg_attrdef
WHERE oid IN (SELECT objid
FROM pg_depend
WHERE refobjid = $1
AND classid = 'pg_attrdef'::regclass
)
) LOOP
FOR r IN EXECUTE 'SELECT max(' || quote_ident(colname) || ') FROM ' || tabrelid::regclass LOOP
IF newmax IS NULL OR r.max > newmax THEN
newmax := r.max;
END IF;
END LOOP;
END LOOP;
RETURN newmax;
END; $$ ;
Вы можете вызвать его для одной последовательности, передав ему OID, и он вернет наибольший номер, используемый любой таблицей, в которой эта последовательность установлена по умолчанию; или вы можете запустить его с таким запросом, чтобы сбросить все последовательности в вашей базе данных:
select relname, setval(oid, sequence_max_value(oid))
from pg_class
where relkind = 'S';
Используя другую квалификацию, вы можете сбросить только последовательность в определенной схеме и т. Д. Например, если вы хотите настроить последовательности в "публичной" схеме:
select relname, setval(pg_class.oid, sequence_max_value(pg_class.oid))
from pg_class, pg_namespace
where pg_class.relnamespace = pg_namespace.oid and
nspname = 'public' and
relkind = 'S';
Обратите внимание, что из-за того, как работает setval(), вам не нужно добавлять 1 к результату.
В качестве заключительного замечания я должен предупредить, что некоторые базы данных, по-видимому, имеют значения по умолчанию, связанные с последовательностями, которые не позволяют системным каталогам иметь полную информацию о них. Это происходит, когда вы видите такие вещи в psql's \d:
alvherre=# \d baz
Tabla «public.baz»
Columna | Tipo | Modificadores
---------+---------+------------------------------------------------
a | integer | default nextval(('foo_a_seq'::text)::regclass)
Обратите внимание, что вызов nextval() в этом предложении по умолчанию имеет приведение::text в дополнение к приведению::regclass. Я думаю, это связано с тем, что базы данных были pg_dump'ом из старых версий PostgreSQL. Что произойдет, так это то, что функция sequence_max_value(), приведенная выше, будет игнорировать такую таблицу. Чтобы устранить проблему, вы можете переопределить предложение DEFAULT, чтобы ссылаться на последовательность напрямую без приведения:
alvherre=# alter table baz alter a set default nextval('foo_a_seq');
ALTER TABLE
Тогда psql отображает это правильно:
alvherre=# \d baz
Tabla «public.baz»
Columna | Tipo | Modificadores
---------+---------+----------------------------------------
a | integer | default nextval('foo_a_seq'::regclass)
Как только вы исправите это, функция будет работать правильно для этой таблицы, а также для всех других, которые могут использовать ту же последовательность.
Еще один plpgsql - сбрасывается только если max(att) > then lastval
do --check seq not in sync
$$
declare
_r record;
_i bigint;
_m bigint;
begin
for _r in (
SELECT relname,nspname,d.refobjid::regclass, a.attname, refobjid
FROM pg_depend d
JOIN pg_attribute a ON a.attrelid = d.refobjid AND a.attnum = d.refobjsubid
JOIN pg_class r on r.oid = objid
JOIN pg_namespace n on n.oid = relnamespace
WHERE d.refobjsubid > 0 and relkind = 'S'
) loop
execute format('select last_value from %I.%I',_r.nspname,_r.relname) into _i;
execute format('select max(%I) from %s',_r.attname,_r.refobjid) into _m;
if coalesce(_m,0) > _i then
raise info '%',concat('changed: ',_r.nspname,'.',_r.relname,' from:',_i,' to:',_m);
execute format('alter sequence %I.%I restart with %s',_r.nspname,_r.relname,_m+1);
end if;
end loop;
end;
$$
;
также комментируя строку --execute format('alter sequence
выдаст список, фактически не сбрасывая значение
Сбросить всю последовательность из общего
CREATE OR REPLACE FUNCTION "reset_sequence" (tablename text) RETURNS "pg_catalog"."void" AS
$body$
DECLARE
BEGIN
EXECUTE 'SELECT setval( '''
|| tablename
|| '_id_seq'', '
|| '(SELECT id + 1 FROM "'
|| tablename
|| '" ORDER BY id DESC LIMIT 1), false)';
END;
$body$ LANGUAGE 'plpgsql';
select sequence_name, reset_sequence(split_part(sequence_name, '_id_seq',1)) from information_schema.sequences
where sequence_schema='public';
Эта проблема возникает у меня при использовании сущностной инфраструктуры для создания базы данных, а затем заполнения базы данных исходными данными, что приводит к несоответствию последовательности.
Я решил это, создав скрипт для запуска после заполнения базы данных:
DO
$do$
DECLARE tablename text;
BEGIN
-- change the where statments to include or exclude whatever tables you need
FOR tablename IN SELECT table_name FROM information_schema.tables WHERE table_schema='public' AND table_type='BASE TABLE' AND table_name != '__EFMigrationsHistory'
LOOP
EXECUTE format('SELECT setval(pg_get_serial_sequence(''"%s"'', ''Id''), (SELECT MAX("Id") + 1 from "%s"))', tablename, tablename);
END LOOP;
END
$do$
Некоторые действительно жесткие ответы здесь, я предполагаю, что это было действительно плохо примерно в то время, когда об этом спрашивали, так как многие ответы отсюда не работают для версии 9.3. Документация, начиная с версии 8.0, дает ответ на этот вопрос:
SELECT setval('serial', max(id)) FROM distributors;
Кроме того, если вам нужно позаботиться о именах последовательностей с учетом регистра, вот как вы это делаете:
SELECT setval('"Serial"', max(id)) FROM distributors;
Моя версия использует первую, с некоторой проверкой ошибок...
BEGIN;
CREATE OR REPLACE FUNCTION reset_sequence(_table_schema text, _tablename text, _columnname text, _sequence_name text)
RETURNS pg_catalog.void AS
$BODY$
DECLARE
BEGIN
PERFORM 1
FROM information_schema.sequences
WHERE
sequence_schema = _table_schema AND
sequence_name = _sequence_name;
IF FOUND THEN
EXECUTE 'SELECT setval( ''' || _table_schema || '.' || _sequence_name || ''', ' || '(SELECT MAX(' || _columnname || ') FROM ' || _table_schema || '.' || _tablename || ')' || '+1)';
ELSE
RAISE WARNING 'SEQUENCE NOT UPDATED ON %.%', _tablename, _columnname;
END IF;
END;
$BODY$
LANGUAGE 'plpgsql';
SELECT reset_sequence(table_schema, table_name, column_name, table_name || '_' || column_name || '_seq')
FROM information_schema.columns
WHERE column_default LIKE 'nextval%';
DROP FUNCTION reset_sequence(_table_schema text, _tablename text, _columnname text, _sequence_name text) ;
COMMIT;
Собираем все вместе
CREATE OR REPLACE FUNCTION "reset_sequence" (tablename text)
RETURNS "pg_catalog"."void" AS
$body$
DECLARE
BEGIN
EXECUTE 'SELECT setval( pg_get_serial_sequence(''' || tablename || ''', ''id''),
(SELECT COALESCE(MAX(id)+1,1) FROM ' || tablename || '), false)';
END;
$body$ LANGUAGE 'plpgsql';
починю 'id'
последовательность данной таблицы (как обычно необходимо, например, для django).
Перепроверьте всю последовательность в общедоступной схеме
CREATE OR REPLACE FUNCTION public.recheck_sequence (
)
RETURNS void AS
$body$
DECLARE
_table_name VARCHAR;
_column_name VARCHAR;
_sequence_name VARCHAR;
BEGIN
FOR _table_name IN SELECT tablename FROM pg_catalog.pg_tables WHERE schemaname = 'public' LOOP
FOR _column_name IN SELECT column_name FROM information_schema.columns WHERE table_name = _table_name LOOP
SELECT pg_get_serial_sequence(_table_name, _column_name) INTO _sequence_name;
IF _sequence_name IS NOT NULL THEN
EXECUTE 'SELECT setval('''||_sequence_name||''', COALESCE((SELECT MAX('||quote_ident(_column_name)||')+1 FROM '||quote_ident(_table_name)||'), 1), FALSE);';
END IF;
END LOOP;
END LOOP;
END;
$body$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 100;
Прежде чем я еще не попробовал код: в следующем я выкладываю версию для sql-кода для решений Klaus и user457226, которая работала на моем компьютере [Postgres 8.3], с небольшими изменениями для Klaus и моей версии для пользователя 457226 один.
Клаус решение:
drop function IF EXISTS rebuilt_sequences() RESTRICT;
CREATE OR REPLACE FUNCTION rebuilt_sequences() RETURNS integer as
$body$
DECLARE sequencedefs RECORD; c integer ;
BEGIN
FOR sequencedefs IN Select
constraint_column_usage.table_name as tablename,
constraint_column_usage.table_name as tablename,
constraint_column_usage.column_name as columnname,
replace(replace(columns.column_default,'''::regclass)',''),'nextval(''','') as sequencename
from information_schema.constraint_column_usage, information_schema.columns
where constraint_column_usage.table_schema ='public' AND
columns.table_schema = 'public' AND columns.table_name=constraint_column_usage.table_name
AND constraint_column_usage.column_name = columns.column_name
AND columns.column_default is not null
LOOP
EXECUTE 'select max('||sequencedefs.columnname||') from ' || sequencedefs.tablename INTO c;
IF c is null THEN c = 0; END IF;
IF c is not null THEN c = c+ 1; END IF;
EXECUTE 'alter sequence ' || sequencedefs.sequencename ||' restart with ' || c;
END LOOP;
RETURN 1; END;
$body$ LANGUAGE plpgsql;
select rebuilt_sequences();
user457226 решение:
--drop function IF EXISTS reset_sequence (text,text) RESTRICT;
CREATE OR REPLACE FUNCTION "reset_sequence" (tablename text,columnname text) RETURNS bigint --"pg_catalog"."void"
AS
$body$
DECLARE seqname character varying;
c integer;
BEGIN
select tablename || '_' || columnname || '_seq' into seqname;
EXECUTE 'SELECT max("' || columnname || '") FROM "' || tablename || '"' into c;
if c is null then c = 0; end if;
c = c+1; --because of substitution of setval with "alter sequence"
--EXECUTE 'SELECT setval( "' || seqname || '", ' || cast(c as character varying) || ', false)'; DOES NOT WORK!!!
EXECUTE 'alter sequence ' || seqname ||' restart with ' || cast(c as character varying);
RETURN nextval(seqname)-1;
END;
$body$ LANGUAGE 'plpgsql';
select sequence_name, PG_CLASS.relname, PG_ATTRIBUTE.attname,
reset_sequence(PG_CLASS.relname,PG_ATTRIBUTE.attname)
from PG_CLASS
join PG_ATTRIBUTE on PG_ATTRIBUTE.attrelid = PG_CLASS.oid
join information_schema.sequences
on information_schema.sequences.sequence_name = PG_CLASS.relname || '_' || PG_ATTRIBUTE.attname || '_seq'
where sequence_schema='public';
Этот ответ является копией от Мауро.
drop function IF EXISTS rebuilt_sequences() RESTRICT;
CREATE OR REPLACE FUNCTION rebuilt_sequences() RETURNS integer as
$body$
DECLARE sequencedefs RECORD; c integer ;
BEGIN
FOR sequencedefs IN Select
DISTINCT(constraint_column_usage.table_name) as tablename,
constraint_column_usage.column_name as columnname,
replace(replace(columns.column_default,'''::regclass)',''),'nextval(''','') as sequencename
from information_schema.constraint_column_usage, information_schema.columns
where constraint_column_usage.table_schema ='public' AND
columns.table_schema = 'public' AND columns.table_name=constraint_column_usage.table_name
AND constraint_column_usage.column_name = columns.column_name
AND columns.column_default is not null
ORDER BY sequencename
LOOP
EXECUTE 'select max('||sequencedefs.columnname||') from ' || sequencedefs.tablename INTO c;
IF c is null THEN c = 0; END IF;
IF c is not null THEN c = c+ 1; END IF;
EXECUTE 'alter sequence ' || sequencedefs.sequencename ||' minvalue '||c ||' start ' || c ||' restart with ' || c;
END LOOP;
RETURN 1; END;
$body$ LANGUAGE plpgsql;
select rebuilt_sequences();
Если вы видите эту ошибку при загрузке пользовательских данных SQL для инициализации, другой способ избежать этого:
Вместо того чтобы писать:
INSERT INTO book (id, name, price) VALUES (1 , 'Alchemist' , 10),
Удалить id
(первичный ключ) из исходных данных
INSERT INTO book (name, price) VALUES ('Alchemist' , 10),
Это поддерживает последовательность Postgres в синхронизации!
Чтобы перезапустить всю последовательность до 1, используйте:
-- Create Function
CREATE OR REPLACE FUNCTION "sy_restart_seq_to_1" (
relname TEXT
)
RETURNS "pg_catalog"."void" AS
$BODY$
DECLARE
BEGIN
EXECUTE 'ALTER SEQUENCE '||relname||' RESTART WITH 1;';
END;
$BODY$
LANGUAGE 'plpgsql';
-- Use Function
SELECT
relname
,sy_restart_seq_to_1(relname)
FROM pg_class
WHERE relkind = 'S';
Так что я могу сказать, что в этой теме недостаточно мнений или изобретенных колес, поэтому я решил оживить ситуацию.
Ниже приведена процедура, которая:
- ориентирован (только влияет) на последовательности, связанные с таблицами
- работает как для столбцов SERIAL, так и для столбцов GENERATED AS IDENTITY
- работает с именами good_column_name и "BAD_column_123"
- автоматически присваивает определенное начальное значение соответствующих последовательностей, если таблица пуста
- позволяет затрагивать только определенные последовательности (в нотации schema.table.column)
- есть режим предварительного просмотра
CREATE OR REPLACE PROCEDURE pg_reset_all_table_sequences(
IN commit_mode BOOLEAN DEFAULT FALSE
, IN mask_in TEXT DEFAULT NULL
) AS
$$
DECLARE
sql_reset TEXT;
each_sec RECORD;
new_val TEXT;
BEGIN
sql_reset :=
$sql$
SELECT setval(pg_get_serial_sequence('%1$s.%2$s', '%3$s'), coalesce(max("%3$s"), %4$s), false) FROM %1$s.%2$s;
$sql$
;
FOR each_sec IN (
SELECT
quote_ident(table_schema) as table_schema
, quote_ident(table_name) as table_name
, column_name
, coalesce(identity_start::INT, seqstart) as min_val
FROM information_schema.columns
JOIN pg_sequence ON seqrelid = pg_get_serial_sequence(quote_ident(table_schema)||'.'||quote_ident(table_name) , column_name)::regclass
WHERE
(is_identity::boolean OR column_default LIKE 'nextval%') -- catches both SERIAL and IDENTITY sequences
-- mask on column address (schema.table.column) if supplied
AND coalesce( table_schema||'.'||table_name||'.'||column_name = mask_in, TRUE )
)
LOOP
IF commit_mode THEN
EXECUTE format(sql_reset, each_sec.table_schema, each_sec.table_name, each_sec.column_name, each_sec.min_val) INTO new_val;
RAISE INFO 'Resetting sequence for: %.% (%) to %'
, each_sec.table_schema
, each_sec.table_name
, each_sec.column_name
, new_val
;
ELSE
RAISE INFO 'Sequence found for resetting: %.% (%)'
, each_sec.table_schema
, each_sec.table_name
, each_sec.column_name
;
END IF
;
END LOOP;
END
$$
LANGUAGE plpgsql
;
для предварительного просмотра:
call pg_reset_all_table_sequences();
совершить:
call pg_reset_all_table_sequences(true);
чтобы указать только вашу целевую таблицу:
call pg_reset_all_table_sequences('schema.table.column');
Просто запустите команду ниже:
SELECT setval('my_table_seq', (SELECT max(id) FROM my_table));
Я потратил час, пытаясь получить ответ djsnowsill для работы с базой данных с использованием таблиц и столбцов в смешанном регистре, а затем, наконец, наткнулся на решение благодаря комментарию Мануэля Дарво, но я подумал, что смогу сделать его немного понятнее для всех:
CREATE OR REPLACE FUNCTION "reset_sequence" (tablename text, columnname text)
RETURNS "pg_catalog"."void" AS
$body$
DECLARE
BEGIN
EXECUTE format('SELECT setval(pg_get_serial_sequence(''%1$I'', %2$L),
(SELECT COALESCE(MAX(%2$I)+1,1) FROM %1$I), false)',tablename,columnname);
END;
$body$ LANGUAGE 'plpgsql';
SELECT format('%s_%s_seq',table_name,column_name), reset_sequence(table_name,column_name)
FROM information_schema.columns WHERE column_default like 'nextval%';
Это имеет преимущество:
- не предполагая, что идентификатор столбца пишется определенным образом.
- не предполагая, что все таблицы имеют последовательность.
- работает для смешанных имен таблиц / столбцов.
- используя формат, чтобы быть более кратким.
Чтобы объяснить, проблема была в том, что pg_get_serial_sequence
принимает строки, чтобы понять, что вы имеете в виду, так что если вы делаете:
"TableName" --it thinks it's a table or column
'TableName' --it thinks it's a string, but makes it lower case
'"TableName"' --it works!
Это достигается с помощью ''%1$I''
в строке формата, ''
делает апостроф 1$
означает первый аргумент и I
значит в кавычках
Ответ Клауса является наиболее полезным, за исключением небольшой ошибки: вы должны добавить DISTINCT в операторе select.
Однако, если вы уверены, что никакие имена таблиц и столбцов не могут быть эквивалентны для двух разных таблиц, вы также можете использовать:
select sequence_name, --PG_CLASS.relname, PG_ATTRIBUTE.attname
reset_sequence(split_part(sequence_name, '_id_seq',1))
from PG_CLASS
join PG_ATTRIBUTE on PG_ATTRIBUTE.attrelid = PG_CLASS.oid
join information_schema.sequences
on information_schema.sequences.sequence_name = PG_CLASS.relname || '_' || PG_ATTRIBUTE.attname
where sequence_schema='public';
который является расширением решения user457226 для случая, когда имя некоторого интересующего столбца не равно "ID".
Метод обновления всех последовательностей в вашей схеме, которые используются в качестве идентификатора:
DO $$ DECLARE
r RECORD;
BEGIN
FOR r IN (SELECT tablename, pg_get_serial_sequence(tablename, 'id') as sequencename
FROM pg_catalog.pg_tables
WHERE schemaname='YOUR_SCHEMA'
AND tablename IN (SELECT table_name
FROM information_schema.columns
WHERE table_name=tablename and column_name='id')
order by tablename)
LOOP
EXECUTE
'SELECT setval(''' || r.sequencename || ''', COALESCE(MAX(id), 1), MAX(id) IS NOT null)
FROM ' || r.tablename || ';';
END LOOP;
END $$;
select 'SELECT SETVAL(' || seq [ 1] || ', COALESCE(MAX('||column_name||')+1, 1) ) FROM '||table_name||';'
from (
SELECT table_name, column_name, column_default, regexp_match(column_default, '''.*''') as seq
from information_schema.columns
where column_default ilike 'nextval%'
) as sequense_query
Ни один из приведенных выше ответов мне не подходит, потому что я усекаю и повторно вставляю исходные данные для модульных тестов.
Если состояние таблицы может быть пустым ИЛИ иметь строки , вам необходимо использовать следующее:
SELECT COALESCE(MAX(id::INTEGER), 1),
SETVAL(
PG_GET_SERIAL_SEQUENCE('TABLE_NAME', 'id'),
COALESCE(MAX(id::INTEGER), 1),
COUNT(id::integer) > 0)
FROM TABLE_NAME;
The
Я предполагаю, что ваш столбец идентификатора НЕ NULLABLE. Если он имеет значение NULL, счетчик может работать не так, как ожидалось.
SELECT setval('sequencename', COALESCE((SELECT MAX(id)+1 FROM tablename), 1), false);
Гадкий взлом, чтобы исправить это с помощью некоторой магии оболочки, не очень хорошее решение, но может вдохновить других на подобные проблемы:)
pg_dump -s <DATABASE> | grep 'CREATE TABLE' | awk '{print "SELECT setval(#" $3 "_id_seq#, (SELECT MAX(id) FROM " $3 "));"}' | sed "s/#/'/g" | psql <DATABASE> -f -