Как сбросить последовательность первичных ключей postgres, если они не синхронизированы?

411

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

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

Кажется, что это вызвано тем, что import/restores не поддерживает правильную последовательность.

  • 0
    Мне любопытно .. вы бросаете БД, прежде чем делать восстановление? У меня есть слабое воспоминание об этом, но я могу ошибаться: P
  • 23
    В вики PostgreSQL есть страница, посвященная исправлению последовательностей .
Показать ещё 3 комментария
Теги:
database
primary-key
database-sequence

25 ответов

528
Лучший ответ
-- 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 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;

Источник - Ruby Forum

  • 0
    Спасибо, phpPgAdmin имеет его на вкладке под названием «Индексы», поэтому я подумал, что это правильное наименование. Итак, я предполагаю, что «sequence» - это свойство «index», которое обновляется?
  • 0
    Последовательность - это просто специальное значение, которое увеличивается / устанавливается с помощью функций nextval () и setval (). Он используется главным образом для создания столбцов, которые будут увеличиваться автоматически, у вас есть первичный ключ со значением по умолчанию 'nextval (' primary_key_name_seq ') и, таким образом, каждый раз, когда вы вставляете ...
Показать ещё 16 комментариев
148

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 i.e.

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;
  • 9
    Нет необходимости в +1 в запросе, setval() устанавливает текущее значение, а nextval() уже вернет текущее значение +1.
  • 1
    Функция обтекания этого метода, которая принимает один параметр - имя_таблицы - в моем ответе ниже: stackoverflow.com/a/13308052/237105
Показать ещё 1 комментарий
45

Это будет reset все последовательности от публики, не допуская предположений о именах таблиц или столбцов. Протестировано на версии 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%';
  • 0
    +1 очень полезная функция! Наши имена последовательностей не совпадают точно с именами таблиц, поэтому я использовал substring(column_default, '''(.*)''') Вместо table_name || '_' || column_name || '_seq' . Работает отлично.
  • 0
    +1 именно то, что я искал. Благодарю.
Показать ещё 3 комментария
33

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;
  • 8
    это синтаксическая ошибка для меня в 8.4 (в ^ (SELECT ...). RESTART WITH, кажется, принимает только порядковый номер. Это работает, хотя: SELECT setval (pg_get_serial_sequence ('table_name', 'id'), (SELECT MAX ( id) FROM table_name) +1);
  • 0
    @ красиво, спасибо!
Показать ещё 2 комментария
30

самый короткий и быстрый способ:

  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 значение true, что означает, что next nextval будет продвигать последовательность, прежде чем возвращать значение.

Смелый акцент мой.

Параллелизм

Нет защиты от одновременной активности последовательности или записи в таблицу в вышеуказанных запросах. Если это актуально, вы можете заблокировать таблицу в эксклюзивном режиме. Он удерживает параллельные транзакции от записи большего числа, пока вы пытаетесь синхронизировать. (Он также временно блокирует безобидные записи, не возиться с максимальным числом.)

Но он не учитывает клиентов, которые могли бы получить порядковые номера заранее без каких-либо блокировок в основной таблице, но (что может случиться). Для этого также допускается только увеличить текущее значение последовательности, никогда не уменьшая ее. Это может показаться параноидальным, но это согласуется с характером последовательностей и защитой от проблем concurrency.

<Предварительно > <код > НАЧАТЬ; LOCK TABLE В ЭКСКЛЮЗИВНОМ РЕЖИМЕ; SELECT setval ('tbl_tbl_id_seq', max (tbl_id)) FROM tbl HAVING max (tbl_id) > (SELECT last_value FROM tbl_tbl_id_seq); COMMIT; Код >
  • 1
    Второй запрос для меня сработал!
  • 0
    Где находится «СТАНДАРТНАЯ община-библиотека основных функций»? Второе предложение select этого ответа в EXECUTE format() (например, @ EB.) Является важной функцией! Как исправить это отсутствие стандартной библиотеки в PostgreSQL ????
Показать ещё 3 комментария
15

Эта команда только для изменения автоматически генерирует значение последовательности клавиш в postgresql

ALTER SEQUENCE "your_sequence_name" RESTART WITH 0;

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

имя последовательности по умолчанию будет "TableName_FieldName_seq". Например, если ваше имя таблицы "MyTable" и ваше имя поля "MyID", ваше имя последовательности будет "MyTable_MyID_seq".

Это ответ такой же, как и ответ @murugesanponappan, но в его решении есть синтаксическая ошибка. вы не можете использовать запрос sub (select max()...) в alter. Чтобы либо использовать фиксированное числовое значение, либо вам нужно использовать переменную вместо подзапроса.

  • 0
    Это идеальное решение, большое спасибо, сэр. Но в моем случае у меня была ошибка, поэтому мне пришлось изменить ее на ALTER SEQUENCE "your_sequence_name" RESTART WITH 1;
15

Reset все последовательности, без предположений об именах, за исключением того, что первичный ключ каждой таблицы является "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%';
  • 0
    Отлично работал на моей версии 9.1
  • 0
    Вам нужно добавить цитату, если таблица содержит верхний регистр: pg_get_serial_sequence(''"' || tablename || '"''
Показать ещё 1 комментарий
12

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

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, и он вернет наибольшее число, используемое любой таблицей, которая имеет последовательность по умолчанию; или вы можете запустить его с таким запросом, чтобы reset все последовательности в вашей базе данных:

 select relname, setval(oid, sequence_max_value(oid))
   from pg_class
  where relkind = 'S';

Используя другой класс, вы можете reset только последовательность в определенной схеме и т.д. Например, если вы хотите настроить последовательности в "общедоступной" схеме:

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\d:

alvherre=# \d baz
                     Tabla «public.baz»
 Columna |  Tipo   |                 Modificadores                  
---------+---------+------------------------------------------------
 a       | integer | default nextval(('foo_a_seq'::text)::regclass)

Обратите внимание, что вызов nextval() в этом объявлении по умолчанию имеет добавление:: text в дополнение к листу:: regclass. Я думаю, что это связано с тем, что базы данных pg_dump'ed из старых версий 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)

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

  • 0
    Это удивительно, спасибо! Следует отметить, что мне нужно было добавить приведение в присваивании (строка 21 в коде функции) следующим образом: newmax := r.max::bigint; чтобы он работал правильно для меня.
  • 0
    Пришлось также изменить это: 'SELECT max(' || quote_ident(colname) || ') FROM ' => 'SELECT max(' || quote_ident(colname) || '::bigint) FROM ' заметили добавленное ::bigint приведен внутри динамически ::bigint запроса.
6

Reset вся последовательность из общедоступных

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';
  • 0
    Похоже, что этот подход делает предположения об именах столбцов и таблиц, поэтому он не работает для меня
  • 0
    Не повредит ли это данным в базе данных?
5

Я предлагаю это решение найти на wiki postgres. Он обновляет все последовательности ваших таблиц.

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;

Как использовать (из wiki postgres):

  • Сохраните это в файле, скажем 'reset.sql'
  • Запустите файл и сохраните его вывод таким образом, чтобы он не включал обычные заголовки, а затем запускал этот вывод. Пример:

Пример:

psql -Atq -f reset.sql -o temp
psql -f temp
rm temp

Оригинальная статья (также с исправлением для правопорядка) здесь

5

Некоторые действительно хардкорные ответы здесь, я предполагаю, что это было очень плохо в то время, когда это было задано, так как множество ответов отсюда не работает для версии 9.3. Документация с версии 8.0 дает ответ на этот вопрос:

SELECT setval('serial', max(id)) FROM distributors;

Кроме того, если вам нужно позаботиться о именах последовательностей, чувствительных к регистру, как это сделать:

SELECT setval('"Serial"', max(id)) FROM distributors;
  • 0
    Очень краткое и точное решение. Спасибо
5

Объединяя все это

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).

5

Моя версия использует первый, с некоторой проверкой ошибок...

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;
  • 0
    Спасибо за проверку ошибок! Очень ценится, так как имена таблиц / столбцов усекаются, если они слишком длинные, что вы определили для меня в вашем RAISE WARNING .
4

Повторно проверьте всю последовательность в общедоступной схеме

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;
4

прежде чем я еще не пробовал код: в следующем сообщении версия для SQL-кода для решений Klaus и user457226 который работал на моем компьютере [Postgres 8.3], с некоторыми небольшими корректировками для Клауса один и моей версии для пользователя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';
3

Еще один 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 предоставит список, а не сбросит значение

3

Чтобы перезапустить всю последовательность до 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';
2

Я потратил час, пытаясь заставить djsnowsill ответить на работу с базой данных, используя таблицы и столбцы Mixed Case, а затем, наконец, наткнулся на решение благодаря комментарию от Мануэля Дарво, но я думал, что смогу сделать его немного понятным для всех

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 a table or column
'TableName' --it thinks it a string, but makes it lower case
'"TableName"' --it works!

Это достигается с помощью ''%1$I'' в строке формата, '' делает апостроф 1$ означает первый arg, а I означает в кавычках

2

Этот ответ - копия мауро.

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();
2

Если вы видите эту ошибку при загрузке пользовательских SQL-данных для инициализации, то другой способ избежать этого:

Вместо записи:

INSERT INTO book (id, name, price) VALUES (1 , 'Alchemist' , 10),

Удалите id (первичный ключ) из исходных данных

INSERT INTO book (name, price) VALUES ('Alchemist' , 10),

Это синхронизирует последовательность Postgres!

1

Ужасно взломать его, используя некоторую магию оболочки, а не отличное решение, но может вызвать у других подобные проблемы:)

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 -
1

Ответ Клауса самый полезный, для небольшого промаха: вы нужно добавить 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 для случая, когда Некоторое имя заинтересованного столбца не является идентификатором.

  • 0
    ... конечно, также необходимо изменить "reset_sequence", то есть добавить параметр "columnname", чтобы использовать вместо "id".
0

Эта проблема возникает со мной при использовании структуры сущностей для создания базы данных, а затем засеивает базу данных с исходными данными, что делает несоответствие последовательности.

Я решил это, создав script для запуска после посева базы данных:

<Предварительно > <код > DO $ $Делать DECLARE tablename текст; НАЧАТЬ   - изменить, где статусы включать или исключать любые таблицы, которые вам нужны   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 ('SELECT setval (pg_get_serial_sequence (' ' "% s" ' ',' 'Id' '), (SELECT MAX ( "Id" ) + 1 из "% s" ))', tablename, tablename);   END LOOP; КОНЕЦ $ $Делать Код >
  • 1
    почему MAX("Id") + 1 это работает лучше всего для меня, когда последовательность = до макс.
0

SELECT setval... делает JDBC bork, поэтому здесь совместимый с Java способ:

-- work around JDBC 'A result was returned when none was expected.'
-- fix broken nextval due to poorly written 20140320100000_CreateAdminUserRoleTables.sql
DO 'BEGIN PERFORM setval(pg_get_serial_sequence(''admin_user_role_groups'', ''id''), 1 + COALESCE(MAX(id), 0), FALSE) FROM admin_user_role_groups; END;';
0

Попробуйте reindex.

ОБНОВЛЕНИЕ: Как указано в комментариях, это было в ответ на исходный вопрос.

  • 0
    Reindex не работает, он только увеличивает индекс на 1
  • 3
    Reindex не работал, потому что он отвечал на ваш первоначальный вопрос, об индексах базы данных, а не о последовательностях

Ещё вопросы

Сообщество Overcoder
Наверх
Меню