INSERT usando os resultados do CTE INSERT para fornecer valores de ID exclusivos


8

Estou escrevendo um trabalho para transformar dados de um design antigo em um novo design. Nesse processo, preciso levar o ID de uma inserção para uma tabela separada e usá-lo em uma inserção para a tabela de destino, como tal:

CREATE TABLE t1 {
  t1_id BIGSERIAL,
  col1 VARCHAR
};
CREATE TABLE t2 {
  t2_id BIGSERIAL,
  col2 VARCHAR, -- renamed from col1 to avoid confusion
  t1_id BIGINT REFERENCES t1.t1_id
};

Eu tenho o SQL definido que corresponde ao seguinte formulário:

WITH ins AS (
  INSERT INTO t1 (t1_id) VALUES (DEFAULT) RETURNING t1_id
) INSERT INTO t2
  (col1, t1_id)
SELECT
  a.val1, (SELECT * FROM ins)
FROM t3 a;

Eu queria que isso executasse o SELECT * FROM inspara todas as linhas do SELECT.. mas, em vez disso, ele é executado apenas uma vez e usa esse valor para todas as linhas no SELECT. Como posso reestruturar meu SQL para obter o comportamento desejado?

edit4

t1 acaba assim:

1,<NULL>
(1 row)

t2 acaba assim:

10,'a',1
11,'b',1 -- problem with id from t1 being 1
12,'c',1 -- problem with id from t1 being 1
.
.

Como eu quero que o t1 seja:

1,<NULL>
2,<NULL>
3,<NULL>
.
.

Como eu quero que o t2 seja:

10,'a',1
11,'b',2 -- id from t1 of 2
12,'c',3 -- id from t1 of 3
.
.

edit Para abordar o que a_horse_with_no_name disse, eu também tentei isso (com o mesmo resultado):

WITH ins AS (
  INSERT INTO t1 (t1_id) VALUES (DEFAULT) RETURNING t1_id
) INSERT INTO t2
  (col1, t1_id)
SELECT
  a.val1, b.t1_id
FROM t3 a
JOIN ins b ON TRUE;

edit2 Eu apenas tentei fazer referência direta ao apropriado SEQUENCEna minha consulta e isso funciona - mas não gosto muito dessa solução (principalmente porque não gosto de nomes de objetos codificados). Se houver QUALQUER outra solução do que referenciar diretamente o nome do que SEQUENCEeu apreciaria. :)

edit3 Suponho que outra solução seria fazer uso de a PROCEDUREpara fazer o INSERTCTE, em vez de ... mas ainda assim eu apreciaria opções / sugestões.


11
Você precisa se juntar inset3
a_horse_with_no_name

Eu tentei isso também e ele ainda calculou o valor apenas uma vez. Mas talvez eu não tenha minha junção bem correta. Vou editar meu post para mostrar o que tentei com isso.
Joishi Bodio 20/03/2014

11
Você está inserindo apenas uma linha t1e não está fornecendo nenhum valor t1.col1. Onde os dados devem chegar para essa coluna? Está t1.col1relacionado com t2.col1?
ypercubeᵀᴹ

ypercube - t1.col1 pode ser NULL e será inserido em um processo posterior. Como eu estava referenciando o CTE como um SUBSELECT nos valores reais da linha, achei que seria executado mais de uma vez - mas acontece que eu estava incorreto nessa suposição .. e é por isso que estou fazendo essa pergunta aqui. Eu já tentei pesquisar por uma resposta no google nas últimas horas e ainda não consegui encontrar o que é correto. E não, t1.col1 não está relacionado a t2.col1 .. desculpe por essa confusão.
Joishi Bodio

11
Ainda assim, INSERT INTO t1 (t1_id) VALUES (DEFAULT)insere apenas 1 linha t1. Portanto, não importa se você coloca insa FROMcláusula na e a junta t3ou não. Você pode nos mostrar como inserir 2 (ou mais) linhas t1? E mais importante, como você sabe em qual dos 2 (ou mais) t1.idvalores corresponderia as linhas inseridas t2?
ypercubeᵀᴹ

Respostas:


8

Eu não entendo por que você precisa de 2 tabelas se elas têm apenas 1-1. Mas aqui está ( pké a chave primária de t3):

WITH ins AS (
  INSERT INTO t1 (col1) 
    SELECT NULL FROM t3 
  RETURNING t1_id
) 
, r AS
( SELECT t1_id, ROW_NUMBER() OVER () AS rn
  FROM ins
) 
, t AS
( SELECT *, ROW_NUMBER() OVER () AS rn
  FROM t3
) 
INSERT INTO t2
  (col1, t1_id)
SELECT
  t.val1, r.t1_id
FROM t 
  JOIN r USING (rn) ;

Se o seu t3 for o resultado de uma SELECT em vez de uma tabela preexistente, você poderá implementá-lo dessa forma para não precisar repetir a consulta t3 duas vezes:

WITH t3 AS (
  SELECT ...
), ins AS (
  INSERT INTO t1 (col1)
    SELECT NULL FROM t3
  RETURNING t1_id
), r AS (
  SELECT t1_id, ROW_NUMBER() OVER () AS rn
  FROM ins
), t AS (
  SELECT *, ROW_NUMBER() OVER () AS rn
  FROM t3
) INSERT INTO t2
  (col1, t1_id)
SELECT
  t.val1, r.t1_id
FROM t 
  JOIN r USING (rn);

A razão pela qual eu preciso das duas tabelas é porque há outra tabela que também precisará armazenar valores em t1. (T1 terá links para t2 e t4) t1 deve ser uma tabela para informações de contato (com teclas para tabelas de endereço, email e número de telefone) e t2 e t4 são entidades em domínios diferentes que precisam ter informações de contato associadas a eles. Posso ter um pouco do meu vocabulário incorreto, mas é basicamente por isso. Obrigado pela resposta - vou testá-lo.
Joishi Bodio

Editado um pequeno erro. Use a versão mais recente.
ypercubeᵀᴹ

OK, faz sentido então. Mas você pode não precisar disso t2_id. Parece que você pode usar o t2(t1_id)como o PK de t2.
ypercubeᵀᴹ

:) Está me dando um erro de sintaxe com DEFAULT no momento - tentando descobrir o que pode ser. ERROR: syntax error at or near "DEFAULT" LINE 2: DEFAULT AS contact_detail_id
318 Joishi Bodio

Hum, parece DEFAULTque não pode ser usado dessa maneira. Não retornando ot.pk
ypercubeᵀᴹ
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.