Como redefinir a seqüência de chave primária postgres quando ele ficar fora de sincronia?

votos
395

Corri para o problema que minha sequência de chave primária não está em sincronia com as minhas linhas da tabela.

Isto é, quando eu inserir uma nova linha recebo um erro de chave duplicada porque a seqüência implícita no tipo de dados de série retorna um número que já existe.

Parece ser causada por importação / não restaura manter a seqüência corretamente.

Publicado 28/10/2008 em 19:08
fonte usuário
Em outras línguas...                            


28 respostas

votos
0

Tente reindex .

UPDATE: Como foi salientado nos comentários, isso foi em resposta à pergunta inicial.

Respondeu 28/10/2008 em 19:10
fonte usuário

votos
557

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

Fonte - Rubi Forum

Respondeu 28/10/2008 em 19:14
fonte usuário

votos
33

ALTER RESTART SEQÜÊNCIA sequence_name COM (SELECT max (id) FROM table_name); Não funciona.

Copiado de resposta @tardate:

SELECT setval(pg_get_serial_sequence('table_name', 'id'), MAX(id)) FROM table_name;
Respondeu 10/12/2008 em 09:29
fonte usuário

votos
156

pg_get_serial_sequencepode ser utilizado para evitar quaisquer suposições incorrectas sobre o nome da sequência. Isso redefine a seqüência em um tiro:

SELECT pg_catalog.setval(pg_get_serial_sequence('table_name', 'id'), (SELECT MAX(id) FROM table_name)+1);

Ou de forma mais concisa:

SELECT pg_catalog.setval(pg_get_serial_sequence('table_name', 'id'), MAX(id)) FROM table_name;

No entanto, este formulário não pode lidar com mesas vazias corretamente, uma vez max (id) é nulo, e nem você pode setval 0 porque seria fora do alcance da seqüência. Uma solução para isso é recorrer ao ALTER SEQUENCEie sintaxe

ALTER SEQUENCE table_name_id_seq RESTART WITH 1;
ALTER SEQUENCE table_name_id_seq RESTART; -- 8.4 or higher

Mas ALTER SEQUENCEé de uso limitado porque o nome seqüência e valor de reinício não podem ser expressões.

Parece que a melhor solução para todos os fins é chamar setvalcom falso como o 3º parâmetro, permitindo-nos para especificar o "próximo valor a ser usado":

SELECT setval(pg_get_serial_sequence('t1', 'id'), coalesce(max(id),0) + 1, false) FROM t1;

Este carrapatos todos os meus caixas:

  1. evita codificar a sequência real nome
  2. lida com mesas vazias corretamente
  3. lida com as tabelas com dados existentes, e não deixar um buraco na seqüência

Finalmente, note que pg_get_serial_sequencesó funciona se a sequência é de propriedade da coluna. Este será o caso se a coluna incrementando foi definido como um serialtipo, no entanto, se a sequência foi adicionada manualmente, é necessário assegurar ALTER SEQUENCE .. OWNED BYtambém é realizada.

ou seja, se serialtipo foi usado para a criação da tabela, isto deve todo o trabalho:

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;

Mas se as sequências foram adicionados manualmente:

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;
Respondeu 13/09/2010 em 09:19
fonte usuário

votos
6

Repor tudo seqüência de público

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';
Respondeu 24/09/2010 em 12:40
fonte usuário

votos
47

Isso irá redefinir todas as seqüências de tomada de público há suposições sobre nomes de tabela ou coluna. Testado na versão 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%';
Respondeu 04/11/2010 em 22:03
fonte usuário

votos
1

A resposta Klaus é o mais útil, execpt por um pouco de falta: você tem que adicionar DISTINCT na instrução select.

No entanto, se tiver certeza de que há nomes de tabela + coluna pode ser equivalente para duas tabelas diferentes, você também pode usar:

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

que é uma extensão de user457226 solução para o caso quando algum nome da coluna interessado não é 'ID'.

Respondeu 09/03/2011 em 22:05
fonte usuário

votos
4

antes eu não tinha tentado ainda o código: na seguinte eu postar a versão para o código SQL para ambas as soluções Klaus e user457226 que funcionou no meu pc [Postgres 8.3], com apenas alguns pequenos ajustamentos para a Klaus um e da minha versão para o user457226 um.

solução Klaus:

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

solução 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';
Respondeu 09/03/2011 em 22:50
fonte usuário

votos
12

Estas funções estão repletas de perigos quando os nomes de seqüência, nomes de colunas, nomes de tabelas ou nomes de esquema tem personagens engraçados, tais como espaços, sinais de pontuação, e assim por diante. Eu escrevi este:

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

Você pode chamá-lo para uma única seqüência, passando-o OID e ele irá retornar o maior número usado por qualquer tabela que tem a sequência como padrão; ou você pode executá-lo com uma consulta como esta, para repor todas as seqüências em seu banco de dados:

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

Usando um diferente qua você pode repor apenas a seqüência em um determinado esquema, e assim por diante. Por exemplo, se você quiser ajustar seqüências no esquema "público":

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

Note-se que devido à forma como setval () funciona, você não precisa adicionar 1 ao resultado.

Como uma nota de fechamento, eu tenho que avisar que alguns bancos de dados parecem ter padrões que ligam a seqüências de formas que não deixam os catálogos do sistema tem informação completa deles. Isso acontece quando você vê coisas como esta no \ d do psql:

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

Note que o nextval () chamada em que cláusula padrão tem um :: texto elenco, além do elenco :: regclass. Eu acho que isso é devido aos bancos de dados que está sendo pg_dump'ed de versões antigas do PostgreSQL. O que vai acontecer é que a função sequence_max_value () acima irá ignorar essa tabela. Para corrigir o problema, você pode redefinir a cláusula DEFAULT para se referir à seqüência diretamente, sem o elenco:

alvherre=# alter table baz alter a set default nextval('foo_a_seq');
ALTER TABLE

exibe, em seguida, psql-lo corretamente:

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

Assim que você reparou que, a função funciona corretamente para esta tabela, bem como todos os outros que possam usar a mesma sequência.

Respondeu 09/05/2011 em 23:20
fonte usuário

votos
5

Minha versão usar o primeiro, com alguma verificação de erros ...

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;
Respondeu 13/09/2011 em 19:22
fonte usuário

votos
5

Juntando tudo

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

irá corrigir ' id'sequência da tabela fornecida (como geralmente necessário com django por exemplo).

Respondeu 09/11/2012 em 13:26
fonte usuário

votos
15

Repor todas as seqüências, há suposições sobre nomes, exceto que a chave primária de cada tabela é "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%';
Respondeu 31/01/2013 em 20:00
fonte usuário

votos
1

gambiarra para corrigi-lo usando alguma magia shell, não é uma grande solução, mas pode inspirar outras pessoas com problemas semelhantes :)

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 -
Respondeu 27/05/2013 em 23:38
fonte usuário

votos
31

A mais curta e mais rápida maneira:

SELECT setval('tbl_tbl_id_seq', max(tbl_id)) FROM tbl;

tbl_idsendo a serialcoluna da tabela tbl, com base a partir da sequência tbl_tbl_id_seq(que é o nome automática padrão).

Se você não sabe o nome da seqüência em anexo (que não tem que estar em formulário padrão), use pg_get_serial_sequence():

SELECT setval(pg_get_serial_sequence('tbl', 'tbl_id'), max(tbl_id)) FROM tbl;

Não há off-por-um erro aqui. Por documentação:

O formulário de dois parâmetros define campo last_value da seqüência para o valor especificado e define seu is_calledcampo para true, o que significa que a próxima nextvalvai avançar a sequência antes de retornar um valor.

grifo meu Bold.

simultaneidade

Não há defesa contra a atividade sequência concorrente ou escreve para a mesa nas consultas acima, ainda. Se isso é relevante, você pode bloquear a tabela em modo exclusivo. Ele mantém transações concorrentes de escrever um número maior, enquanto você está tentando entrar em sincronia. (Ele também temporariamente blocos inofensivos escreve não mexer com o número máximo.)

Mas ele não leva clientes em conta que pode ter números de seqüência obtida de antecedência sem quaisquer bloqueios na mesa principal, mas (o que pode acontecer). Para permitir que, também, só aumentar o valor atual da seqüência, não diminuí-la. Pode parecer paranóico, mas isso é de acordo com a natureza das sequências e defender contra problemas de concorrência.

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;
Respondeu 30/04/2014 em 15:07
fonte usuário

votos
2

Se você ver este erro quando você estiver carregando dados SQL personalizadas para a inicialização, uma outra maneira de evitar isso é:

Em vez de escrever:

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

Remover o id(tecla primário) a partir de dados inicial

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

Isso mantém os Postgres sequência em sincronia!

Respondeu 23/05/2014 em 14:52
fonte usuário

votos
15

Este comando para única mudança auto gerado valor sequência de teclas no PostgreSQL

ALTER SEQUENCE "your_sequence_name" RESTART WITH 0;

No lugar de zero, você pode colocar qualquer número a partir do qual você deseja reiniciar seqüência.

nome da seqüência padrão será "TableName_FieldName_seq". Por exemplo, se o seu nome da tabela é "MyTable"e seu nome de campo é "MyID", em seguida, seu nome seqüência será "MyTable_MyID_seq".

Esta é a resposta é a mesma resposta de @ murugesanponappan, mas há um erro de sintaxe na sua solução. você não pode usar sub consulta (select max()...)no altercomando. Assim que ou você tem que usar valor numérico fixo ou você precisa usar uma variável no lugar de sub consulta.

Respondeu 01/09/2014 em 11:29
fonte usuário

votos
5

Algumas respostas muito graves aqui, eu estou supondo que costumava ser muito ruim em torno do momento em que isso foi solicitado, já que um monte de respostas de aqui não funciona para a versão 9.3. A documentação desde a versão 8.0 fornece uma resposta para esta pergunta muito:

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

Além disso, se você precisa para cuidar de nomes seqüência de caso-sensível, que é como você fazê-lo:

SELECT setval('"Serial"', max(id)) FROM distributors;
Respondeu 07/11/2014 em 12:34
fonte usuário

votos
0

SELECT setval... faz JDBC Bork, então aqui está uma maneira compatível com Java de fazer isso:

-- 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;';
Respondeu 21/05/2015 em 17:01
fonte usuário

votos
4

Verifique novamente todos sequência em função do esquema pública

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;
Respondeu 02/06/2015 em 10:15
fonte usuário

votos
2

Esta resposta é uma cópia de mauro.

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();
Respondeu 16/06/2016 em 19:05
fonte usuário

votos
5

Eu sugiro que esta solução encontrada no postgres wiki. Ele atualiza todas as sequências de suas tabelas.

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;

Como usar (de postgres wiki):

  • Salvar este para um arquivo, dizer 'reset.sql'
  • Execute o arquivo e salvar a sua saída de uma forma que não inclui os cabeçalhos de costume, em seguida, executar essa saída. Exemplo:

Exemplo:

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

Artigo original (também com correção para a posse seqüência) aqui

Respondeu 25/07/2016 em 19:29
fonte usuário

votos
3

Para reiniciar todos sequência para 1 uso:

-- 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';
Respondeu 01/09/2016 em 14:53
fonte usuário

votos
3

No entanto, outro plpgsql - redefine apenas se 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;
$$
;

Também comentando a linha --execute format('alter sequencevai dar a lista, na verdade não redefinir o valor

Respondeu 20/04/2017 em 12:10
fonte usuário

votos
2

Passei uma hora tentando obter a resposta de djsnowsill para trabalhar com um banco de dados usando tabelas caso misto e colunas, então, finalmente deparei com a solução graças a um comentário de Manuel Darveau, mas eu pensei que poderia torná-lo um pouco mais claro para todos:

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

Isto tem a vantagem de:

  • não assumindo coluna ID está escrito um modo particular.
  • não assumindo todas as tabelas têm uma seqüência.
  • trabalhando para caso misto nomes de tabela / coluna.
  • usando o formato a ser mais conciso.

Para explicar, o problema era que pg_get_serial_sequenceleva cordas para descobrir o que você está se referindo, por isso, se você faz:

"TableName" --it thinks it's a table or column
'TableName' --it thinks it's a string, but makes it lower case
'"TableName"' --it works!

Isto é conseguido usando ''%1$I''na string de formato, ''faz uma apóstrofe 1$significa primeiro argumento, e Isignifica entre aspas

Respondeu 05/07/2017 em 17:00
fonte usuário

votos
0

Este problema acontece comigo quando se utiliza estrutura de entidade para criar o banco de dados e, em seguida, semear o banco de dados com os dados iniciais, o que torna a incompatibilidade seqüência.

Eu resolvi isso criando um script para ser executado após a semeadura do banco de dados:

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$
Respondeu 08/04/2018 em 15:22
fonte usuário

votos
0
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
Respondeu 10/03/2019 em 16:53
fonte usuário

votos
0

Um método para atualizar todas as seqüências em seu esquema que são usados ​​como um ID:

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 $$;
Respondeu 15/05/2019 em 21:53
fonte usuário

votos
0

Basta executar o comando abaixo:

SELECT setval('my_table_seq', (SELECT max(id) FROM my_table));
Respondeu 01/10/2019 em 16:03
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more