Estou usando o Python para escrever em um banco de dados postgres:
sql_string = "INSERT INTO hundred (name,name_slug,status) VALUES ("
sql_string += hundred + ", '" + hundred_slug + "', " + status + ");"
cursor.execute(sql_string)
Mas como algumas das minhas linhas são idênticas, recebo o seguinte erro:
psycopg2.IntegrityError: duplicate key value
violates unique constraint "hundred_pkey"
Como posso escrever uma instrução SQL 'INSERT, a menos que essa linha já exista'?
Eu já vi declarações complexas como esta recomendadas:
IF EXISTS (SELECT * FROM invoices WHERE invoiceid = '12345')
UPDATE invoices SET billed = 'TRUE' WHERE invoiceid = '12345'
ELSE
INSERT INTO invoices (invoiceid, billed) VALUES ('12345', 'TRUE')
END IF
Mas, em primeiro lugar, é um exagero para o que eu preciso e, em segundo lugar, como posso executar um deles como uma sequência simples?
postgresql
sql-insert
upsert
AP257
fonte
fonte
Respostas:
O Postgres 9.5 (lançado desde 07-01-2016) oferece um comando "upsert" , também conhecido como cláusula ON CONFLICT, para INSERT :
Ele resolve muitos dos problemas sutis que você pode encontrar ao usar a operação simultânea, que algumas outras respostas propõem.
fonte
INSERT INTO distributors (did, dname) VALUES (7, 'Redline GmbH') ON CONFLICT (did) DO NOTHING;
(2) INSERT se não existir mais ATUALIZAÇÃO -INSERT INTO distributors (did, dname) VALUES (5, 'Gizmo Transglobal'), (6, 'Associated Computing, Inc') ON CONFLICT (did) DO UPDATE SET dname = EXCLUDED.dname;
Estes exemplos são do manual - postgresql.org/docs/9.5/static/sql-insert.htmlExiste uma boa maneira de fazer INSERT condicional no PostgreSQL:
CAVEAT Essa abordagem não é 100% confiável para operações de gravação simultâneas . Existe uma condição de raça muito pequena entre
SELECT
oNOT EXISTS
anti-semi-join e oINSERT
próprio. Ele pode falhar sob tais condições.fonte
RETURNS id
por exemplo, paraid
saber se foi inserido ou não?RETURNING id
no e da consulta e ele retornará um novo ID de linha ou nada, se nenhuma linha tiver sido inserida.Uma abordagem seria criar uma tabela não restrita (sem índices exclusivos) para inserir todos os seus dados e fazer uma seleção distinta daquela para fazer sua inserção na sua tabela de cem.
Tão alto nível seria. Suponho que todas as três colunas sejam distintas no meu exemplo, portanto, para a etapa 3, altere a junção NOT EXITS para ingressar apenas nas colunas exclusivas da tabela de cem.
Crie tabela temporária. Veja os documentos aqui .
INSERIR Dados na tabela temporária.
Adicione quaisquer índices à tabela temporária.
Faça a inserção da tabela principal.
fonte
SELECT name,name_slug,status
ou*
SELECT DISTINCT name, name_slug, status FROM temp_data
?Infelizmente,
PostgreSQL
não suporta nemMERGE
nemON DUPLICATE KEY UPDATE
, então você terá que fazer isso em duas instruções:Você pode envolvê-lo em uma função:
e apenas chame:
fonte
INSERT INTO hundred (name, name_slug, status) SELECT 'Chichester', 'chichester', NULL WHERE 'Chichester' NOT IN (SELECT NAME FROM hundred);
várias vezes e ele continua inserindo a linha.CREATE TABLE hundred (name TEXT, name_slug TEXT, status INT); INSERT INTO hundred (name, name_slug, status) SELECT 'Chichester', 'chichester', NULL WHERE 'Chichester' NOT IN (SELECT NAME FROM hundred); INSERT INTO hundred (name, name_slug, status) SELECT 'Chichester', 'chichester', NULL WHERE 'Chichester' NOT IN (SELECT NAME FROM hundred); SELECT * FROM hundred
. Há um registro.Você pode usar VALUES - disponível no Postgres:
fonte
Eu sei que essa pergunta é de um tempo atrás, mas achei que isso poderia ajudar alguém. Eu acho que a maneira mais fácil de fazer isso é através de um gatilho. Por exemplo:
Execute esse código em um prompt do psql (ou como você deseja executar consultas diretamente no banco de dados). Então você pode inserir normalmente do Python. Por exemplo:
Observe que, como o @Thomas_Wouters já mencionado, o código acima aproveita os parâmetros em vez de concatenar a string.
fonte
Existe uma boa maneira de fazer INSERT condicional no PostgreSQL usando a consulta WITH:
fonte
Este é exatamente o problema que enfrento e minha versão é 9.5
E eu resolvo isso com a consulta SQL abaixo.
Espero que ajude alguém que tenha o mesmo problema com a versão> = 9.5.
Obrigado pela leitura.
fonte
INSERIR .. ONDE NÃO EXISTE é uma boa abordagem. E as condições de corrida podem ser evitadas pela transação "envelope":
fonte
É fácil com as regras:
Mas falha com gravações simultâneas ...
fonte
A abordagem com os mais votados (de John Doe) funciona de alguma forma para mim, mas, no meu caso, das 422 linhas esperadas, recebo apenas 180. Não consegui encontrar nada errado e não há erros, por isso procurei uma solução diferente. abordagem simples.
Usar
IF NOT FOUND THEN
depois deSELECT
apenas funciona perfeitamente para mim.(descrito na documentação do PostgreSQL )
Exemplo da documentação:
fonte
A classe de cursor psycopgs possui o atributo rowcount .
Portanto, você pode tentar UPDATE primeiro e INSERT apenas se o número de linhas for 0.
Mas, dependendo dos níveis de atividade em seu banco de dados, você pode atingir uma condição de corrida entre UPDATE e INSERT, onde outro processo pode criar esse registro nesse ínterim.
fonte
Sua coluna "cem" parece ser definida como chave primária e, portanto, deve ser única, o que não é o caso. O problema não é com os seus dados.
Sugiro que você insira um ID como tipo de série para manusear a chave primária
fonte
Se você disser que muitas de suas linhas são idênticas, você terminará a verificação várias vezes. Você pode enviá-los e o banco de dados determinará se o inserirá ou não com a cláusula ON CONFLICT da seguinte maneira
fonte
Eu estava procurando uma solução semelhante, tentando encontrar SQL que funcionasse no PostgreSQL e no HSQLDB. (Foi o HSQLDB que tornou isso difícil.) Usando o seu exemplo como base, este é o formato que encontrei em outro lugar.
fonte
Aqui está uma função python genérica que, com um nome de tabela, colunas e valores, gera o equivalente de upsert para o postgresql.
json de importação
fonte
A solução é simples, mas não imediata.
Se você quiser usar esta instrução, faça uma alteração no db:
após essas alterações, "INSERIR" funcionará corretamente.
fonte