Usando o PostgreSQL v9.1. Eu tenho as seguintes tabelas:
CREATE TABLE foo
(
id BIGSERIAL NOT NULL UNIQUE PRIMARY KEY,
type VARCHAR(60) NOT NULL UNIQUE
);
CREATE TABLE bar
(
id BIGSERIAL NOT NULL UNIQUE PRIMARY KEY,
description VARCHAR(40) NOT NULL UNIQUE,
foo_id BIGINT NOT NULL REFERENCES foo ON DELETE RESTRICT
);
Digamos que a primeira tabela foo
seja preenchida assim:
INSERT INTO foo (type) VALUES
( 'red' ),
( 'green' ),
( 'blue' );
Existe alguma maneira de inserir linhas bar
facilmente fazendo referência à foo
tabela? Ou devo fazê-lo em duas etapas, primeiro pesquisando o foo
tipo que desejo e, em seguida, inserindo uma nova linha bar
?
Aqui está um exemplo de pseudo-código mostrando o que eu esperava que pudesse ser feito:
INSERT INTO bar (description, foo_id) VALUES
( 'testing', SELECT id from foo WHERE type='blue' ),
( 'another row', SELECT id from foo WHERE type='red' );
postgresql
foreign-key
postgresql-9.1
insert
Stéphane
fonte
fonte
INSERT Simples
O uso de um em
LEFT [OUTER] JOIN
vez de[INNER] JOIN
significa que as linhas deval
não são descartadas quando nenhuma correspondência é encontradafoo
. Em vez disso,NULL
é inserido parafoo_id
.A
VALUES
expressão na subconsulta faz o mesmo que o CTE do @ ypercube . As expressões de tabela comum oferecem recursos adicionais e são mais fáceis de ler em grandes consultas, mas também representam barreiras de otimização. Portanto, as subconsultas geralmente são um pouco mais rápidas quando nenhuma das opções acima é necessária.id
como o nome da coluna é um anti-padrão generalizado. Deve serfoo_id
ebar_id
ou qualquer coisa descritiva. Ao ingressar em várias tabelas, você acaba com várias colunas, todas nomeadasid
...Considere simples
text
ou emvarchar
vez devarchar(n)
. Se você realmente precisar impor uma restrição de comprimento, adicione umaCHECK
restrição:Pode ser necessário adicionar conversões de tipo explícitas. Como a
VALUES
expressão não está diretamente anexada a uma tabela (como emINSERT ... VALUES ...
), os tipos não podem ser derivados e os tipos de dados padrão são usados sem declaração explícita de tipo, o que pode não funcionar em todos os casos. É o suficiente para fazê-lo na primeira linha, o resto ficará alinhado.INSERIR linhas FK ausentes ao mesmo tempo
Se você deseja criar entradas inexistentes em tempo
foo
real, em uma única instrução SQL , os CTEs são instrumentais:Observe as duas novas linhas fictícias a serem inseridas. Ambos são roxos , o que ainda não existe
foo
. Duas linhas para ilustrar a necessidadeDISTINCT
na primeiraINSERT
instrução.Explicação passo a passo
O 1º CTE
sel
fornece várias linhas de dados de entrada. A subconsultaval
com aVALUES
expressão pode ser substituída por uma tabela ou subconsulta como origem. ImediatamenteLEFT JOIN
parafoo
anexar as linhasfoo_id
pré-existentestype
. Todas as outras linhas ficamfoo_id IS NULL
assim.O 2º CTE
ins
insere novos tipos distintos (foo_id IS NULL
)foo
e retorna os recém-geradosfoo_id
- junto com otype
para se juntar novamente para inserir linhas.O exterior final
INSERT
agora pode inserir um foo.id para cada linha: o tipo pré-existente ou foi inserido na etapa 2.Estritamente falando, as duas inserções acontecem "em paralelo", mas como essa é uma declaração única , as
FOREIGN KEY
restrições padrão não irão reclamar. A integridade referencial é imposta no final da instrução por padrão.SQL Fiddle para Postgres 9.3. (Funciona da mesma maneira em 9.1.)
Há uma pequena condição de corrida se você executar várias dessas consultas simultaneamente. Leia mais em perguntas relacionadas aqui e aqui e aqui . Realmente só acontece sob carga simultânea pesada, se é que alguma vez. Em comparação com soluções de cache como anunciadas em outra resposta, a chance é super pequena .
Função para uso repetido
Para uso repetido, eu criaria uma função SQL que pegasse uma matriz de registros como parâmetro e usasse
unnest(param)
no lugar daVALUES
expressão.Ou, se a sintaxe para matrizes de registros estiver muito bagunçada para você, use uma string separada por vírgula como parâmetro
_param
. Por exemplo do formulário:Em seguida, use isso para substituir a
VALUES
expressão na instrução acima:Função com UPSERT no Postgres 9.5
Crie um tipo de linha personalizado para a passagem de parâmetros. Poderíamos ficar sem ele, mas é mais simples:
Função:
Ligar:
Rápido e sólido para ambientes com transações simultâneas.
Além das consultas acima, isso ...
... aplica-se
SELECT
ouINSERT
ativafoo
: qualquer umtype
que ainda não exista na tabela FK é inserido. Supondo que a maioria dos tipos preexista. Para ter certeza absoluta e descartar as condições de corrida, as linhas existentes de que precisamos são bloqueadas (para que transações simultâneas não possam interferir). Se isso for muito paranóico para o seu caso, você pode substituir:com
... aplica-se
INSERT
ouUPDATE
(verdadeiro "UPSERT") embar
: Se odescription
já existir,type
é atualizado:Mas somente se
type
realmente mudar:... passa valores como tipos de linha conhecidos com um
VARIADIC
parâmetro Observe o máximo padrão de 100 parâmetros! Comparar:Existem muitas outras maneiras de passar várias linhas ...
Palavras-chave:
fonte
INSERT missing FK rows at the same time
exemplo, colocar isso em uma transação reduziria o risco de condições de corrida no SQL Server?SELECT
dentro de umaWITH
cláusula). Fonte: documentação da MS.INSERT ... RETURNING \gset
empsql
seguida, usar os valores retornados como psql:'variables'
, mas isso só funciona para inserções de linhas individuais.Olho para cima. Você basicamente precisa dos IDs de foo para inseri-los na barra.
Não é específico do postgres, btw. (e você não marcou dessa maneira) - geralmente é assim que o SQL funciona. Não há atalhos aqui.
No que diz respeito à aplicação, você pode ter um cache de itens foo na memória. Minhas tabelas geralmente têm até 3 campos exclusivos:
Exemplo:
Obviamente, quando você deseja vincular algo a uma conta - primeiro é necessário, tecnicamente, obter o ID - mas, dado que o Identificador e o Código nunca mudam quando estão lá, um cache positivo na memória pode impedir que a maioria das pesquisas atinja o banco de dados.
fonte