Як скинути послідовність основного ключа postgres, коли він не синхронізується?


523

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

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

Схоже, це викликано імпортом / відновленням, не підтримуючи послідовність належним чином.


Мені цікаво .. Ви скидаєте db перед тим, як зробити відновлення? У мене слабке спогад про це відбувається, але я можу помилятися: P
Артур Томас,

25
У вікі PostgreSQL є сторінка Fixing Sequences .
Бред Кох

14
Тільки для полегшення googleability, повідомлення про помилку, кинене тут: "повторюване значення ключа порушує унікальне обмеження ..."
суперлюмінація

4
Ось як це робить sqlsequencereset в Django: SELECT setval (pg_get_serial_sequence ("<ім'я табелі>", "id"), coalesce (max ("id"), 1), max ("id") НЕ нульовий) ВІД "< Таблиця_назви> ";
користувач

Перший екземпляр <ім'я таблиці> потрібно загорнути в єдині лапки, щоб функція pg_get_serioal_sequence працювала: SELECT setval (pg_get_serial_sequence ('<table_name>', 'id'), coalesce (max ("id"), 1) , max ("id") НЕ нуль) ВІД "<table_name>"
nclu

Відповіді:


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

Джерело - Форум Рубі


12
У будь-якому випадку, додавання 1 до MAX (id) залишить один ідентичний номер у ваших ідентифікаторах, оскільки те, що встановлює setval, є останнє значення послідовності, а не наступне.
mikl

6
Ваш приклад не буде працювати, якщо в таблиці немає рядків. Отже, SQL, наведений нижче, є більш безпечним: SELECT setval ('your_table_id_seq', coalesce ((виберіть max (id) +1 від your_table), 1), true);
Валерій Вікторовський

10
@Valery: Але щоб уникнути прогалин, про які згадував @mikl два коментарі вище, вам потрібноSELECT setval('your_table_id_seq', coalesce((select max(id)+1 from your_table), 1), false);
Ентоні Хетчкінс

20
Усі вирішені питання об’єднані в один запит:SELECT setval('your_seq',(SELECT GREATEST(MAX(your_id)+1,nextval('your_seq'))-1 FROM your_table))
Frunsi

15
Якщо ваша програма піклується про прогалини в послідовностях, ваша програма буде порушена. Прогалини в послідовностях є нормальними і можуть виникати через незаплановані вимкнення бази даних, відхилення транзакцій після помилок тощо
Крейг Рінгер

202

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;

Це кліщає всі мої скриньки:

  1. уникає жорсткого кодування фактичного імені послідовності
  2. правильно обробляє порожні таблиці
  3. обробляє таблиці з існуючими даними і не залишає отворів у послідовності

Нарешті, зауважте, що це 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;

11
У запиті немає необхідності "+1", setval()встановлює поточне значення і nextval()вже поверне поточне значення +1.
Ентоні Хеткінс

1
Функція обгортання цього методу, який приймає один параметр - table_name - є у моїй відповіді нижче: stackoverflow.com/a/13308052/237105
Antony Hatchkins

@AntonyHatchkins ура. Щойно побачив чергове повторення помилки +1, нарешті, промайнув, що я, сподіваюся,
tardate

99

Короткий і швидкий спосіб:

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буде просунути послідовність перед поверненням значення.

Сміливий акцент мій.

Якщо таблиця може бути порожньою і фактично починати з 1 у цьому випадку:

SELECT setval(pg_get_serial_sequence('tbl', 'tbl_id')
            , COALESCE(max(tbl_id) + 1, 1)
            , false)
FROM tbl;

Ми не можемо просто використовувати форму 2-paremater і почати з того, 0що нижня межа послідовностей дорівнює 1 за замовчуванням (якщо не налаштована).

Паралельність

Проте немає захисту від паралельної активності послідовностей або записується до таблиці у вищезазначених запитах. Якщо це актуально, ви можете заблокувати таблицю в ексклюзивному режимі. Він утримує одночасні транзакції від запису більшої кількості, поки ви намагаєтеся синхронізуватись. (Він також тимчасово блокує нешкідливі записи, не возившись з максимальною кількістю.)

Але це не враховує клієнтів, які, можливо, заздалегідь отримали порядкові номери без жодних блокувань на головній таблиці (що може статися). Щоб дозволити це також, тільки збільшуйте поточне значення послідовності, ніколи не зменшуйте його. Це може здатися параноїчним, але це узгоджується з характером послідовностей і захистом від проблем з одночасністю.

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;

Де "СТАНДАРТНА спільнота-бібліотека основних функцій"? Другий пункт вибору цієї відповіді в EXECUTE format()(наприклад, @ EB.'s) є важливою функцією! Як виправити цю відсутність стандартної бібліотеки в PostgreSQL ????
Пітер Краус

Не має значення, чи є за межами. Прогалини в послідовностях є нормальними. Якщо ваш додаток не може впоратися, ваш додаток виправлено, тому що прогалини можуть виникнути через відмову транзакцій, незаплановані відключення сервера тощо.
Крейг Рінгер

1
@Craig: Помилка, за якою я звертався один за одним (і її немає) матиме значення, оскільки в іншому випадку ми ризикуємо помилкою повторюваного ключа. Протилежний напрямок ваших міркувань; здається непорозумінням.
Ервін Брандстеттер

ах, має сенс.
Крейг Рінгер

Це працює для мене
hectk

54

Це скине всі послідовності з публічного, не створюючи припущень щодо назв таблиці чи стовпців. Тестовано у версії 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%';

1
+1 дуже корисна функція! Наші імена послідовностей не відповідали точно назвам таблиць, тому я використовував substring(column_default, '''(.*)''')замість table_name || '_' || column_name || '_seq'. Працює чудово.
Кріс Лерчер

4
Зауважте, що це не вдасться з іменами послідовностей, що містять одиничні лапки або назви таблиць з великими літерами, пробілами тощо. Тут quote_literalі справді слід використовувати quote_identфункції та , або бажано formatфункцію.
Крейг Рінгер

2
Хотілося б, щоб я міг дати це за один голос ... чудово виконаний сер. Чудово працює і на Postgres 9.1, принаймні для мене.
peelman

1
Це чудово. Я використовував, substring(column_default from 'nextval\(''(.+)''::regclass\)')щоб явно схопити назву послідовності. Працював як шарм.
Меттью Макдональд

Я шукав це рішення більше дня, спасибі велике, навіть я використав метод, запропонований @ChrisLercher, для заміни текстуsubstring(column_default, '''(.*)''') instead of table_name || '_' || column_name || '_seq'
Sushin Pv

43

АЛЬТЕР СЕКВЕНЦІЯ_послідовність_Імен RESTART С (ВИБІР макс. (ІД) ВІД таблиці_назви); Не працює.

Скопійовано з @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);
tardate

1
Рішення Муругеса також не працює в 9.4. Не розумію, чому стільки прихильників цієї відповіді. ALTER SEQUENCE не дозволяє підзапити. Рішення від @tardate прекрасно працює. Відредагована відповідь на видалення невірних даних.
Владислав Раструсний

АЛЕ ПОСЛІДЧЕННЯ спрацювало для мене ідеально. Я використовував COPY для введення деяких даних, і в первинних ключах були прогалини, і INSERT кидали дублюючі виключення ключів. Встановлення послідовності зробило трюк. 9.4
користувач542319

22

Ця команда змінює лише автоматично сформоване значення послідовності ключів у postgresql

ALTER SEQUENCE "your_sequence_name" RESTART WITH 0;

Замість нуля ви можете поставити будь-яке число, з якого потрібно перезапустити послідовність.

ім'я послідовності за замовчуванням буде "TableName_FieldName_seq". Наприклад, якщо назва вашої таблиці є, "MyTable"а ваше поле - це "MyID"ваше ім'я послідовності "MyTable_MyID_seq".

Ця відповідь така ж, як і відповідь @ murugesanponappan, але в його рішенні є синтаксична помилка. ви не можете використовувати підзапроси (select max()...)в alterкоманді. Так що або вам доведеться використовувати фіксоване числове значення, або вам потрібно використовувати змінну замість підзапиту.


Це ідеальне рішення, дуже дякую, пане. Але в моєму випадку у мене виникла помилка, тому мені довелося змінити її НА ПОЛІШЕННЯ ПОСЛІДОВНОСТІ "your_sequence_name" RESTART WITH 1;
Deunz

18

Скидання всіх послідовностей, жодних припущень щодо імен, за винятком того, що основним ключем кожної таблиці є "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%';

Відмінно працював на моїй версії 9.1
Валентин Васильєв

Вам потрібно додати цитату, якщо таблиця містить верхній регістр:pg_get_serial_sequence(''"' || tablename || '"''
Мануель Дарво,

Це найкраща функція! Ви можете уникнути проблем із цитатами (та підвищити елегантність) із форматом, на кшталт EXECUTE format( 'SELECT setval(pg_get_serial_sequence(%L, %L), coalesce(max(id),0) + 1, false) FROM %I;', $1,$2,$1 );
Пітер Краус

13

Ці функції загрожують небезпеками, коли назви послідовностей, назви стовпців, назви таблиць або назви схем мають забавні символи, такі як пробіли, розділові знаки тощо. Я написав це:

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

Використовуючи іншу якість, ви можете скинути лише послідовність у певній схемі тощо. Наприклад, якщо ви хочете відрегулювати послідовності в схемі "public":

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 () у цьому замовчуванні має: :: текст, доданий до складу :: regclass Я думаю, що це пов'язано з тим, що бази даних pg_dump'ed від старих версій PostgreSQL. Що станеться, що функція послідовності_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)

Як тільки ви виправили це, функція працює правильно для цієї таблиці, а також для всіх інших, які можуть використовувати ту саму послідовність.


Це дивовижна подяка! Слід зазначити, що мені потрібно було додавати атрибут при призначенні (рядок 21 у коді функції) таким чином: newmax := r.max::bigint;щоб він працював правильно для мене.
Томмі Браво

Довелося також змінити це: 'SELECT max(' || quote_ident(colname) || ') FROM ' => 'SELECT max(' || quote_ident(colname) || '::bigint) FROM ' помітити доданий ::bigintамплітуду в запиті динамічної збірки.
Томмі Браво

9

Ще один 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, цей список дасть список, фактично не скидаючи значення


8

Скинути всю послідовність із загальнодоступних

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

Здається, що цей підхід робить припущення щодо імен стовпців і таблиць, щоб він не працював для мене
djsnowsill

Не могли б це пошкодити дані в базі даних?
zennin

8

Я пропоную це рішення знайти на вікі 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

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


7

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

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

Крім того, якщо вам потрібно подбати про чутливі до регістру імена послідовностей, ось як це зробити:

SELECT setval('"Serial"', max(id)) FROM distributors;

7

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

Я вирішив це, створивши сценарій для запуску після висівання бази даних:

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$

1
чому MAX("Id") + 1це працює для мене найкраще, коли послідовність = до макс.
остання посилання

6

Моя версія використовує першу, перевіряючи помилки ...

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;

Дякуємо за перевірку помилок! Багато вдячні за те, що назви таблиці / стовпців обрізаються, якщо вони занадто довгі, що ви RAISE WARNINGвизначили для мене.
Ніколас Райлі

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


4

раніше я ще не пробував код: у наступному я публікую версію для sql-коду як для Klaus, так і для користувача457226 рішень, які працювали на моєму ПК [Postgres 8.3], із лише невеликими налаштуваннями для Klaus one та моєї версії для користувача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';

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;

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

Відповідь Клауса є найкориснішим, крім невеликого промаху: вам потрібно додати 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".


... звичайно, також потрібна зміна "reset_sequence", тобто додавання параметра "ім'я стовпця", щоб використовувати замість "id".
mauro

2

Якщо ви бачите цю помилку під час завантаження користувальницьких даних SQL для ініціалізації, ще один спосіб уникнути цього:

Замість написання:

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

Видаліть id(первинний ключ) з початкових даних

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

Це підтримує синхронізацію послідовності Postgres!


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

Я витратив годину, намагаючись отримати відповідь 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%';

Це має перевагу:

  • не припускаючи, що стовпець ID написаний певним чином.
  • не припускаючи, що всі таблиці мають послідовність.
  • працює для назв таблиці / стовпців змішаного випадку
  • використовуючи формат, щоб бути більш стислим.

Щоб пояснити, проблема полягала в тому, що 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означає в лапки


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

4
Хоча цей код може відповісти на питання, надаючи додатковий контекст щодо того, чому та / або як цей код відповідає на питання, покращує його довгострокове значення.
yeya

0

Спробуйте перевстановити .

ОНОВЛЕННЯ: Як було зазначено в коментарях, це було у відповідь на початкове запитання.


reindex не працював, він, схоже, збільшує індекс на 1
мелеял

3
reindex не працював, тому що він відповідав на ваше первісне запитання щодо індексів бази даних, а не послідовностей
Vinko Vrsalovic

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

Спосіб оновлення всіх послідовностей вашої схеми, які використовуються як ідентифікатор:

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

0

Просто запустіть команду нижче:

SELECT setval('my_table_seq', (SELECT max(id) FROM my_table));

0

Тут є багато хороших відповідей. У мене була така ж потреба після перезавантаження моєї бази даних Django.

Але мені потрібно було:

  • Все в одній функції
  • Не вдалося виправити одну або декілька схем одночасно
  • Не вдалося виправити всю або лише одну таблицю за один раз
  • Також хотіли приємного способу побачити, що саме змінилося чи не змінилося

Це здається дуже схожим на необхідність, яку вимагав оригінальний запит.
Завдяки Балдірі та Мауро вивели мене на правильний шлях.

drop function IF EXISTS reset_sequences(text[], text) RESTRICT;
CREATE OR REPLACE FUNCTION reset_sequences(
    in_schema_name_list text[] = '{"django", "dbaas", "metrics", "monitor", "runner", "db_counts"}',
    in_table_name text = '%') RETURNS text[] as
$body$
  DECLARE changed_seqs text[];
  DECLARE sequence_defs RECORD; c integer ;
  BEGIN
    FOR sequence_defs IN
        select
          DISTINCT(ccu.table_name) as table_name,
          ccu.column_name as column_name,
          replace(replace(c.column_default,'''::regclass)',''),'nextval(''','') as sequence_name
          from information_schema.constraint_column_usage ccu,
               information_schema.columns c
          where ccu.table_schema = ANY(in_schema_name_list)
            and ccu.table_schema = c.table_schema
            AND c.table_name = ccu.table_name
            and c.table_name like in_table_name
            AND ccu.column_name = c.column_name
            AND c.column_default is not null
          ORDER BY sequence_name
   LOOP
      EXECUTE 'select max(' || sequence_defs.column_name || ') from ' || sequence_defs.table_name INTO c;
      IF c is null THEN c = 1; else c = c + 1; END IF;
      EXECUTE 'alter sequence ' || sequence_defs.sequence_name || ' restart  with ' || c;
      changed_seqs = array_append(changed_seqs, 'alter sequence ' || sequence_defs.sequence_name || ' restart with ' || c);
   END LOOP;
   changed_seqs = array_append(changed_seqs, 'Done');

   RETURN changed_seqs;
END
$body$ LANGUAGE plpgsql;

Потім виконати та побачити запущені зміни:

select *
from unnest(reset_sequences('{"django", "dbaas", "metrics", "monitor", "runner", "db_counts"}'));

Повертається

activity_id_seq                          restart at 22
api_connection_info_id_seq               restart at 4
api_user_id_seq                          restart at 1
application_contact_id_seq               restart at 20
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.