Corrigindo a estrutura da tabela para evitar `Erro: o valor duplicado da chave viola a restrição exclusiva`

15

Eu tenho uma tabela que é criada desta maneira:

--
-- Table: #__content
--
CREATE TABLE "jos_content" (
  "id" serial NOT NULL,
  "asset_id" bigint DEFAULT 0 NOT NULL,
   ...
  "xreference" varchar(50) DEFAULT '' NOT NULL,
  PRIMARY KEY ("id")
);

Posteriormente, algumas linhas são inseridas especificando o ID:

INSERT INTO "jos_content" VALUES (1,36,'About',...)

Em um ponto mais tarde alguns registros são inseridos sem ID e eles falhar com o erro: Error: duplicate key value violates unique constraint.

Aparentemente, o id foi definido como uma sequência:

insira a descrição da imagem aqui

Cada inserção com falha aumenta o ponteiro na sequência até aumentar para um valor que não existe mais e as consultas tiverem êxito.

SELECT nextval('jos_content_id_seq'::regclass)

O que há de errado com a definição da tabela? Qual é a maneira inteligente de corrigir isso?

Valentin Despa
fonte
No PostgreSQL, você não precisa citar os nomes das colunas e das tabelas, se todas estiverem em minúsculas.
Rodrigo

Respostas:

19

Nada está errado com sua definição de tabela.
(Exceto o chapéu que eu usaria jos_content_idou algo assim, em vez do nome não descritivo da coluna id.
E provavelmente usaria em textvez devarchar(50) .

Sua INSERTafirmação é o problema.

Com sua idcoluna definida como serial, você não deve inserir valores manuais para id. Aqueles podem colidir com o próximo valor da sequência associada.

Forneça uma lista explícita de colunas de destino (o que quase sempre é uma boa idéia para INSERTinstruções persistentes ) e omita completamente as colunas seriais .

INSERT INTO jos_content(asset_id, some_column, ...)
VALUES (36,'About',...);

Se você precisar dos valores das colunas geradas automaticamente imediatamente, use a RETURNINGcláusula :

INSERT ...
RETURNING id;  -- possibly more

Mais detalhes nesta resposta relacionada ao SO:

Se você tiver entradas manuais em serialcolunas que podem entrar em conflito posteriormente, configure sua sequência para o máximo atual idpara corrigir isso uma vez :

SELECT setval('jos_content_id_seq', max(id))
FROM   jos_content;

Onde jos_content_id_seqé o nome padrão para uma sequência de propriedade jos_content.id, que você já encontrou na coluna padrão. Parece estar xhzt8_content_id_seqno seu caso;


Atualização: Um problema semelhante apareceu no SO e eu vim com uma nova solução:

Erwin Brandstetter
fonte
O texto não é mais lento que o varchar (50)?
Rodrigo
2
@ Rodrigo: Não está no Postgres. Há um link acima para obter mais explicações: dba.stackexchange.com/a/21496/3684 . Ou aqui. dba.stackexchange.com/a/89433/3684
Erwin Brandstetter
O último teste aqui < depesz.com/2010/03/02/charx-vs-varcharx-vs-varchar-vs-text > me convenceu de que varchar (n) é mais rápido na maioria dos campos em que é conveniente uma restrição de tamanho (pessoas nomes, e-mails, endereços, nomes de espécies, etc). O texto é mais rápido (ou o mesmo) se você não verificar o tamanho, ao que parece.
Rodrigo