Como redefinir a sequência no postgres e preencher a coluna de identificação com novos dados?

126

Eu tenho uma mesa com mais de um milhão de linhas. Preciso redefinir a sequência e reatribuir a coluna id com novos valores (1, 2, 3, 4 ... etc ...). Existe alguma maneira fácil de fazer isso?

Sennin
fonte
6
A verdadeira questão: por que diabos você gostaria de fazer isso? Presumivelmente, o ID é a chave primária, portanto, não há benefício algum em alterar a chave primária. Uma chave primária é um valor sem sentido (no seu caso, artificial). A "renumeração" não serve a nenhum propósito sensato em um banco de dados relacional.
a_horse_with_no_name
2
Inicialmente, eu tinha o aplicativo em execução localmente, depois copiei os dados na produção. Mas idnão começou a partir de 1. Portanto, a ordem resultou da seguinte forma: 150, 151 ..., 300, 1, 2 ... E causaria erros de identificação duplicados eventualmente, suponho, se não tivesse renumerado os ids. Além disso, ordenar por idgeralmente é melhor que ordenar por created_at. E aqui está o que funcionou para mim .
X-yuri
O objetivo é fazer com que você possa continuar usando um int regular, em vez de bigint, para uma chave primária em um banco de dados que continua a incrementar a chave seqüencial, mas recebe constantemente novos dados. Você encontrará rapidamente o limite inteiro assinado e, se não for importante manter a identificação existente, esse processo o levará de volta a números de identificação gerenciáveis.
Ben Wilson
Outro uso para isso é o teste. Você deseja redefinir uma tabela para um estado conhecido antes de iniciar cada teste e isso requer que os IDs sejam redefinidos.
Safa Alai

Respostas:

203

Se você não deseja manter a ordem dos IDs, pode

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

Duvido que exista uma maneira fácil de fazer isso na ordem de sua escolha, sem recriar toda a tabela.

Michael Krelin - hacker
fonte
4
Isso não deveria ser ALTER SEQUENCE seq RESTART WITH 1;?
Lars Haugseth
5
Isso pode causar identificações duplicadas. Para evitar isso, você pode primeiro definir todos os valores muito altos: UPDATE t SET idcolumn = 1000000 + nextval ('seq'); em seguida, execute o script acima.
tahagh
5
SELECT setval('seq', 1, FALSE)deve fazer o mesmo (aqui, o terceiro argumento, FALSE, faz a mágica, pois mostra que nextvaldeve ser 1 em vez de 2)
Vasilen Donchev
@VassilenDontchev, absolutamente.
Michael Krelin - hacker de
55

Com o PostgreSQL 8.4 ou mais recente, não há mais a necessidade de especificar WITH 1. O valor inicial que foi registrado CREATE SEQUENCEou o último definido por ALTER SEQUENCE START WITHserá usado (provavelmente será 1).

Redefina a sequência:

ALTER SEQUENCE seq RESTART;

Atualize a coluna ID da tabela:

UPDATE foo SET id = DEFAULT;

Fonte: Docs do PostgreSQL

Oliver
fonte
3
Essa parece ser a melhor resposta, pois evita fazer suposições sobre o valor inicial da sequência.
sheepdog
Melhor resposta para o meu caso também. Eu combino esta resposta com esta , que explica o comando ALTER SEQUENCE ... então mudei 'seq' por mytable_id_seq onde 'mytable' é o nome da minha tabela e 'id' é o nome da minha coluna serial
Javi
42

Redefina a sequência:

SELECT setval('sequence_name', 0);

Atualizando registros atuais:

UPDATE foo SET id = DEFAULT;
Frank Heikens
fonte
3
Sequence poderia ter um maior valor mínimo de 0. (E o valor mínimo padrão usado por tipo seriale CREATE SEQUENCEé 1!)
brk
18

Ambas as soluções fornecidas não funcionaram para mim;

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

setval('seq', 1)inicia a numeração com 2 e também ALTER SEQUENCE seq START 1a numeração com 2, porque seq.is_called é verdadeiro (Postgres versão 9.0.4)

A solução que funcionou para mim é:

> ALTER SEQUENCE seq RESTART WITH 1;
> UPDATE foo SET id = DEFAULT;
hera
fonte
1
Mesmo problema aqui. Sua solução também funciona para o PostgreSQL 8.3.10.
PeqNP 13/12/12
17

Apenas para simplificar e esclarecer o uso adequado do setter ALTER SEQUENCE e SELECT para redefinir a sequência:

ALTER SEQUENCE sequence_name RESTART WITH 1;

é equivalente a

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

Qualquer uma das instruções pode ser usada para redefinir a sequência e você pode obter o próximo valor por nextval ('sequence_name'), como indicado aqui também:

nextval('sequence_name')
Ali Raza Bhayani
fonte
Obrigado Ali. Eu só notei é crucial para definir que 3 parâmetro para false com a função setval
DavidHyogo
14

A melhor maneira de redefinir uma sequência para começar com o número 1 é executar o seguinte:

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

Portanto, por exemplo, para a tabela de usuários, seria:

ALTER SEQUENCE users_id_seq RESTART WITH 1
jahmed31
fonte
6

Para manter a ordem das linhas:

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

FYI: se você precisar especificar um novo valor inicial entre um intervalo de IDs (256 - 10000000, por exemplo):

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

Apenas redefinir a sequência e atualizar todas as linhas pode causar erros de identificação duplicados. Em muitos casos, você precisa atualizar todas as linhas duas vezes. Primeiro com IDs mais altos para evitar duplicatas, depois com os IDs que você realmente deseja.

Evite adicionar uma quantia fixa a todos os IDs (como recomendado em outros comentários). O que acontece se você tiver mais linhas que esse valor fixo? Supondo que o próximo valor da sequência seja maior que todos os IDs das linhas existentes (você só deseja preencher as lacunas), eu faria assim:

UPDATE table SET id = DEFAULT;
ALTER SEQUENCE seq RESTART;
UPDATE table SET id = DEFAULT;
Frank
fonte
1

No meu caso, consegui isso com:

ALTER SEQUENCE table_tabl_id_seq RESTART WITH 6;

Onde minha tabela se chama table

Diego Santa Cruz Mendezú
fonte
Obrigado por incluir um exemplo concreto com o nome da sua tabela. As outras respostas foram um pouco ambíguas.
Brylie Christopher Oxley
0

Inspirado pelas outras respostas aqui, criei uma função SQL para fazer uma migração de sequência. A função move uma sequência de teclas primárias para uma nova sequência contígua iniciando com qualquer valor (> = 1) dentro ou fora do intervalo de sequências existente.

Explico aqui como usei essa função na migração de dois bancos de dados com o mesmo esquema, mas com valores diferentes em um banco de dados.

Primeiro, a função (que imprime os comandos SQL gerados para que fique claro o que realmente está acontecendo):

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;

A função migrate_pkey_sequenceaceita os seguintes argumentos:

  1. arg_table: nome da tabela (por exemplo 'example')
  2. arg_column: nome da coluna da chave primária (por exemplo 'id')
  3. arg_sequence: nome da sequência (por exemplo 'example_id_seq')
  4. arg_next_value: próximo valor para a coluna após a migração

Ele executa as seguintes operações:

  1. Mova os valores da chave primária para um intervalo livre. Suponho que se nextval('example_id_seq')segue max(id)e que a sequência começa com 1. Isso também lida com o caso em que arg_next_value > max(id).
  2. Mova os valores da chave primária para o intervalo contíguo que começa com arg_next_value. A ordem dos valores-chave é preservada, mas os furos no intervalo não são preservados.
  3. Imprima o próximo valor a seguir na sequência. Isso é útil se você deseja migrar as colunas de outra tabela e mesclar com esta.

Para demonstrar, usamos uma sequência e uma tabela definidas da seguinte forma (por exemplo, usando 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)
  );

Em seguida, inserimos alguns valores (começando, por exemplo, em 3):

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

Por fim, migramos os example.idvalores para começar com 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)

O resultado:

# SELECT * FROM example;
 id
----
  1
  2
  3
(3 rows)
Sean Leather
fonte
0

Mesmo a coluna de incremento automático não é PK (neste exemplo, é chamada seq - também conhecida como sequência), você pode conseguir isso com um gatilho:

DROP TABLE SE EXISTE 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();
Yordan Georgiev
fonte
-1

Se você estiver usando o pgAdmin3, expanda 'Sequências', clique com o botão direito do mouse em uma sequência, vá para 'Propriedades' e, na guia 'Definição', altere 'Valor atual' para o valor que desejar. Não há necessidade de uma consulta.

Dinesh Patil
fonte
3
Sua resposta não agrega valor se você não nos contar pelo menos qual ferramenta está usando.
11101101b
3
Esta é a maneira mais fácil, obviamente, eu acho que ele está dizendo pg administrador 3
MvcCmsJon