Як скинути послідовність у postgres та заповнити стовпчик id новими даними?


126

У мене є таблиця з понад мільйон рядків. Мені потрібно скинути послідовність і присвоїти стовпчик ідентифікаторів з новими значеннями (1, 2, 3, 4 ... і т.д. ...). Чи є якийсь простий спосіб зробити це?


6
Справжнє запитання: чому б на землі ви хотіли це зробити? Імовірно, ідентифікатор є первинним ключем, тому в зміні первинного ключа ніякої користі немає. Первинний ключ - це безглузде (у вашому випадку штучне) значення. "Перерахунок" не служить ніякій розумній меті у реляційній базі даних.
a_horse_with_no_name

2
Спочатку у мене програма працювала локально, потім я скопіював дані у виробництво. Але idце не починалося з 1. Отже, замовлення вийшло таким чином: 150, 151 ..., 300, 1, 2 ... І це призведе до спроб повторюваних помилок id, врешті-решт, я вважаю, якби я не перенумерував ід. Крім того, замовлення на замовлення, idяк правило, краще, ніж замовлення created_at. І ось що для мене спрацювало .
x-yuri

Сенс цього полягає в тому, що ви можете продовжувати використовувати звичайний int замість bigint для первинного ключа в базі даних, який продовжує збільшувати послідовний ключ, але постійно отримує нові дані. Ви швидко наткнетеся на підписаний цілий ліміт, і якщо збереження існуючого ідентифікатора не важливо, цей процес поверне вас до керованих ідентифікаційних номерів.
Бен Вілсон

Ще одне використання для цього - тестування. Перед початком кожного тесту потрібно скинути таблицю до відомого стану, і для цього потрібно скинути ідентифікатори.
Сафа Алай

Відповіді:


203

Якщо ви не хочете зберігати впорядкування ідентифікаторів, то можете

ALTER SEQUENCE seq RESTART WITH 1;
UPDATE t SET idcolumn=nextval('seq');

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


4
Чи не повинно бути це ALTER SEQUENCE seq RESTART WITH 1;?
Lars Haugseth

5
Це може спричинити повторювані ідентифікатори. Щоб запобігти цьому, спочатку можна встановити всі дуже високі значення: UPDATE t SET idcolumn = 1000000 + nextval ('seq'); потім запустіть описаний вище сценарій.
tahagh

5
SELECT setval('seq', 1, FALSE)слід зробити те ж саме (ось третій аргумент, ЛАЖНИЙ, робить магію, як видно, що nextvalповинно бути 1 замість 2)
Василен Дончев,

@VassilenDontchev, абсолютно.
Майкл Крелін - хакер

55

З PostgreSQL 8.4 або новішою версією WITH 1більше не потрібно вказувати . Буде використано початкове значення, яке було записано CREATE SEQUENCEабо встановлено останнім набором ALTER SEQUENCE START WITH(швидше за все це буде 1).

Скидання послідовності:

ALTER SEQUENCE seq RESTART;

Потім оновіть стовпець ідентифікатора таблиці:

UPDATE foo SET id = DEFAULT;

Джерело: PostgreSQL Документи


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

Найкраща відповідь і для моєї справи. Я поєдную цю відповідь з цією , що пояснює команду ALTER SEQUENCE ... тому я змінив 'seq' на mytable_id_seq, де ' mytable ' - це ім’я моєї таблиці, а 'id' - ім’я моєї серійної колонки
Javi

42

Скидання послідовності:

SELECT setval('sequence_name', 0);

Оновлення поточних записів:

UPDATE foo SET id = DEFAULT;

3
Послідовність може мати мінімальне значення, що перевищує 0. (І мінімальне значення за замовчуванням, яке використовується за типом, serialі CREATE SEQUENCEдорівнює 1!)
brk

18

Обидва надані рішення не працювали для мене;

> SELECT setval('seq', 0);
ERROR:  setval: value 0 is out of bounds for sequence "seq" (1..9223372036854775807)

setval('seq', 1) починає нумерацію з 2, і ALTER SEQUENCE seq START 1 починає нумерацію з 2, тому що seq.is_called є істинним (Postgres версія 9.0.4)

Для мене це рішення:

> ALTER SEQUENCE seq RESTART WITH 1;
> UPDATE foo SET id = DEFAULT;

1
Тут же проблема. Ваше рішення працює і для PostgreSQL 8.3.10.
PeqNP

17

Просто для спрощення та уточнення правильного використання ALTER SEQUENCE та SELECT setval для скидання послідовності:

ALTER SEQUENCE sequence_name RESTART WITH 1;

еквівалентно

SELECT setval('sequence_name', 1, FALSE);

Будь-який з операторів може бути використаний для скидання послідовності, і ви можете отримати наступне значення шляхом nextval ('ім'я_послідовності'), як зазначено тут також:

nextval('sequence_name')

Дякую Алі. Я щойно помітив, що дуже важливо встановити цей 3-й параметр false на функції
setval

14

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

ALTER SEQUENCE <tablename>_<id>_seq RESTART WITH 1

Так, наприклад для таблиці користувачів це було б:

ALTER SEQUENCE users_id_seq RESTART WITH 1

6

Щоб зберегти порядок рядків:

UPDATE thetable SET rowid=col_serial FROM 
(SELECT rowid, row_number() OVER ( ORDER BY lngid) AS col_serial FROM thetable ORDER BY lngid) AS t1 
WHERE thetable.rowid=t1.rowid;

4

FYI: Якщо вам потрібно вказати нове початкове значення між діапазоном ідентифікаторів (наприклад, 256 - 10000000):

SELECT setval('"Sequence_Name"', 
       (SELECT coalesce(MAX("ID"),255) 
           FROM "Table_Name" 
           WHERE "ID" < 10000000 and "ID" >= 256)+1
       ); 

2

Просто скидання послідовності та оновлення всіх рядків можуть спричинити повторювані помилки id. У багатьох випадках вам доведеться двічі оновлювати всі рядки. Спочатку з більш високими ідентифікаторами, щоб уникнути дублікатів, а потім із ідентифікаторами, які ви насправді хочете.

Будь ласка, уникайте додавати фіксовану суму до всіх ідентифікаторів (як рекомендовано в інших коментарях). Що станеться, якщо у вас більше рядків, ніж ця фіксована сума? Якщо припустити, що наступне значення послідовності вище, ніж усі ідентифікатори існуючих рядків (ви просто хочете заповнити прогалини), я б це зробив так:

UPDATE table SET id = DEFAULT;
ALTER SEQUENCE seq RESTART;
UPDATE table SET id = DEFAULT;


0

Натхненний іншими відповідями тут, я створив функцію SQL, щоб зробити міграцію послідовностей. Функція переміщує послідовність первинного ключа до нової суміжної послідовності, починаючи з будь-якого значення (> = 1), всередині або поза наявним діапазоном послідовностей.

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

По-перше, функція (яка друкує згенеровані команди SQL, щоб було зрозуміло, що насправді відбувається):

CREATE OR REPLACE FUNCTION migrate_pkey_sequence
  ( arg_table      text
  , arg_column     text
  , arg_sequence   text
  , arg_next_value bigint  -- Must be >= 1
  )
RETURNS int AS $$
DECLARE
  result int;
  curr_value bigint = arg_next_value - 1;
  update_column1 text := format
    ( 'UPDATE %I SET %I = nextval(%L) + %s'
    , arg_table
    , arg_column
    , arg_sequence
    , curr_value
    );
  alter_sequence text := format
    ( 'ALTER SEQUENCE %I RESTART WITH %s'
    , arg_sequence
    , arg_next_value
    );
  update_column2 text := format
    ( 'UPDATE %I SET %I = DEFAULT'
    , arg_table
    , arg_column
    );
  select_max_column text := format
    ( 'SELECT coalesce(max(%I), %s) + 1 AS nextval FROM %I'
    , arg_column
    , curr_value
    , arg_table
    );
BEGIN
  -- Print the SQL command before executing it.
  RAISE INFO '%', update_column1;
  EXECUTE update_column1;
  RAISE INFO '%', alter_sequence;
  EXECUTE alter_sequence;
  RAISE INFO '%', update_column2;
  EXECUTE update_column2;
  EXECUTE select_max_column INTO result;
  RETURN result;
END $$ LANGUAGE plpgsql;

Функція migrate_pkey_sequenceбере такі аргументи:

  1. arg_table: назва таблиці (наприклад 'example')
  2. arg_column: назва стовпця первинного ключа (напр 'id' )
  3. arg_sequence: назва послідовності (наприклад 'example_id_seq')
  4. arg_next_value: наступне значення для стовпця після міграції

Він виконує такі операції:

  1. Перемістіть значення первинного ключа у вільний діапазон. Я припускаю, що nextval('example_id_seq')випливає, max(id)і що послідовність починається з 1. Це також обробляє випадок, де arg_next_value > max(id).
  2. Перемістіть значення первинного ключа до суміжного діапазону, починаючи з arg_next_value. Порядок ключових значень зберігається, але отвори в діапазоні не зберігаються.
  3. Роздрукуйте наступне значення, яке слід за послідовністю. Це корисно, якщо ви хочете перенести стовпці іншої таблиці та об'єднатись із цією.

Для демонстрації ми використовуємо послідовність і таблицю, визначені наступним чином (наприклад, використовуючи psql):

# CREATE SEQUENCE example_id_seq
  START WITH 1
  INCREMENT BY 1
  NO MINVALUE
  NO MAXVALUE
  CACHE 1;
# CREATE TABLE example
  ( id bigint NOT NULL DEFAULT nextval('example_id_seq'::regclass)
  );

Потім ми вставляємо деякі значення (починаючи, наприклад, з 3):

# ALTER SEQUENCE example_id_seq RESTART WITH 3;
# INSERT INTO example VALUES (DEFAULT), (DEFAULT), (DEFAULT);
-- id: 3, 4, 5

Нарешті, ми переносимо example.idзначення для початку з 1.

# SELECT migrate_pkey_sequence('example', 'id', 'example_id_seq', 1);
INFO:  00000: UPDATE example SET id = nextval('example_id_seq') + 0
INFO:  00000: ALTER SEQUENCE example_id_seq RESTART WITH 1
INFO:  00000: UPDATE example SET id = DEFAULT
 migrate_pkey_sequence
-----------------------
                     4
(1 row)

Результат:

# SELECT * FROM example;
 id
----
  1
  2
  3
(3 rows)

0

Навіть стовпець з автоматичним збільшенням не є ПК (у цьому прикладі це називається seq - aka послідовність), ви можете досягти цього за допомогою тригера:

КОРОТКА ТАБЛИЦЯ, ЯКЩО ВИНАГАЄ, devops_guide CASCADE;

SELECT 'create the "devops_guide" table'
;
   CREATE TABLE devops_guide (
      guid           UUID NOT NULL DEFAULT gen_random_uuid()
    , level          integer NULL
    , seq            integer NOT NULL DEFAULT 1
    , name           varchar (200) NOT NULL DEFAULT 'name ...'
    , description    text NULL
    , CONSTRAINT pk_devops_guide_guid PRIMARY KEY (guid)
    ) WITH (
      OIDS=FALSE
    );

-- START trg_devops_guide_set_all_seq
CREATE OR REPLACE FUNCTION fnc_devops_guide_set_all_seq()
    RETURNS TRIGGER
    AS $$
       BEGIN
         UPDATE devops_guide SET seq=col_serial FROM
         (SELECT guid, row_number() OVER ( ORDER BY seq) AS col_serial FROM devops_guide ORDER BY seq) AS tmp_devops_guide
         WHERE devops_guide.guid=tmp_devops_guide.guid;

         RETURN NEW;
       END;
    $$ LANGUAGE plpgsql;

 CREATE TRIGGER trg_devops_guide_set_all_seq
  AFTER UPDATE OR DELETE ON devops_guide
  FOR EACH ROW
  WHEN (pg_trigger_depth() < 1)
  EXECUTE PROCEDURE fnc_devops_guide_set_all_seq();

-1

Якщо ви використовуєте pgAdmin3, розгорніть «Послідовності», клацніть правою кнопкою миші на послідовність, перейдіть до «Властивості» та на вкладці «Визначення» змініть «Поточне значення» на будь-яке значення, яке ви хочете. Немає потреби в запиті.


3
Ваша відповідь не додає значення, якщо ви принаймні не скажете нам, яким інструментом ви користуєтесь.
11101101b

3
Це найпростіший спосіб, очевидно, я думаю, що він говорить pg admin 3
MvcCmsJon
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.