Как сбросить последовательность в postgres и заполнить столбец id новыми данными?


126

У меня есть таблица с более чем миллионом строк. Мне нужно сбросить последовательность и переназначить столбец идентификатора новыми значениями (1, 2, 3, 4 ... и т. Д.). Есть какой-нибудь простой способ сделать это?


6
Настоящий вопрос: зачем тебе это нужно? Предположительно, ID является первичным ключом, поэтому нет никакой пользы от изменения первичного ключа. Первичный ключ - это бессмысленное (в вашем случае искусственное) значение. «Перенумерация» не служит какой-либо разумной цели в реляционной базе данных.
a_horse_with_no_name

2
Сначала у меня было приложение, работающее локально, затем я скопировал данные в продакшн. Но idэто не началось с 1. Таким образом, порядок получился следующим: 150, 151 ..., 300, 1, 2 ... И, я полагаю, в конечном итоге это привело бы к ошибкам дублирования идентификаторов, если бы я не перенумеровал идентификаторы. Кроме того, заказ по idобычно лучше, чем заказ по created_at. И вот что сработало для меня .
x-yuri

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

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

Ответы:


204

Если вы не хотите сохранять порядок идентификаторов, вы можете

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 Docs


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

Лучший ответ и для моего случая. Я объединяю этот ответ с этим , который объясняет команду ALTER SEQUENCE ... поэтому я изменил seq на mytable_id_seq, где mytable - это имя моей таблицы, а id - имя моего последовательного столбца
Хави

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 ('sequence_name'), как указано здесь :

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

К вашему сведению: если вам нужно указать новое начальное значение между диапазоном идентификаторов (например, 256 - 10000000):

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

2

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

Не добавляйте фиксированную сумму ко всем идентификаторам (как рекомендовано в других комментариях). Что произойдет, если у вас будет больше строк, чем это фиксированное количество? Предполагая, что следующее значение последовательности выше, чем все идентификаторы существующих строк (вы просто хотите заполнить пробелы), я бы сделал это так:

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

1

В моем случае я добился этого с помощью:

ALTER SEQUENCE table_tabl_id_seq RESTART WITH 6;

Где моя таблица называется таблицей


Спасибо, что включили конкретный пример в название вашей таблицы. Остальные ответы были слишком двусмысленными.
Brylie Christopher Oxley

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

Даже столбец автоинкремента не является PK (в этом примере он называется seq - aka sequence), вы можете добиться этого с помощью триггера:

УДАЛИТЬ ТАБЛИЦУ, ЕСЛИ СУЩЕСТВУЕТ 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.