INSERT utilizzando i risultati di CTE INSERT per fornire valori ID univoci


8

Sto scrivendo un lavoro per trasformare i dati da un vecchio design in un nuovo design. In questo processo, devo prendere l'id da un inserto in una tabella separata e usarlo in un inserto nella tabella di destinazione, come tale:

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

Ho definito l'SQL che corrisponde al seguente modulo:

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;

Volevo che questo eseguisse il SELECT * FROM insper ogni riga del SELECT.. ma invece lo esegue solo una volta e usa quel valore per tutte le righe nel SELECT. Come posso ristrutturare il mio SQL per ottenere il comportamento desiderato?

Edit4

t1 finisce per apparire come:

1,<NULL>
(1 row)

T2 finisce come:

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

Cosa voglio che assomigli a t1:

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

Cosa voglio che t2 assomigli a:

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

modifica Per rispondere a ciò che ha detto a_horse_with_no_name, ho provato anche questo (con lo stesso risultato):

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 Ho appena provato a fare riferimento direttamente all'appropriato SEQUENCEnella mia query, e quello FUNZIONA - ma non mi piace molto quella soluzione (soprattutto perché non mi piacciono i nomi degli oggetti con codifica rigida). Se esiste QUALSIASI soluzione che fare riferimento diretto al nome SEQUENCEdell'io lo apprezzerei. :)

edit3 Suppongo che un'altra soluzione sarebbe quella di utilizzare un PROCEDUREper fare il INSERTinvece di un CTE .. ma continuerei ad apprezzare opzioni / suggerimenti.


1
Devi iscriverti inset3
a_horse_with_no_name

Ho provato anche quello e ha ancora calcolato il valore solo una volta. Ma forse non ho avuto il mio join abbastanza corretto. Modificherò il mio post per mostrare cosa ho provato con quello.
Joishi Bodio,

1
Stai inserendo solo una riga in t1e non stai fornendo alcun valore per t1.col1. Dove dovrebbero essere i dati per quella colonna? È t1.col1legato a t2.col1?
ypercubeᵀᴹ

ypercube - t1.col1 può essere NULL e verrà inserito in un processo successivo. Dato che stavo facendo riferimento al CTE come SUBSELECT nei valori di riga effettivi, ho pensato che sarebbe stato eseguito più di una volta, ma risulta che non ero corretto in quell'assunto ... motivo per cui sto ponendo questa domanda qui. Ho già provato a cercare una risposta su Google nelle ultime ore e non sono stato ancora in grado di trovare ciò che è corretto. E no, t1.col1 non è correlato a t2.col1 .. scusate per quella confusione.
Joishi Bodio,

1
Tuttavia, INSERT INTO t1 (t1_id) VALUES (DEFAULT)inserisce solo 1 riga in t1. Quindi non importa se inserisci insla FROMclausola e la unisci t3o meno. Puoi mostrarci come inserire 2 (o più) righe in t1? E ancora più importante, come fai a sapere quale dei 2 (o più) t1.idvalori sarebbe abbinato alle righe inserite t2?
ypercubeᵀᴹ

Risposte:


8

Non capisco perché hai bisogno di 2 tabelle se hanno solo una relazione 1-1. Ma eccolo ( pkè la chiave primaria di 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 il tuo t3 è il risultato di un SELECT invece di una tabella preesistente, puoi implementarlo come tale in modo da non dover ripetere due volte la query t3:

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

Il motivo per cui ho bisogno delle due tabelle è perché c'è un'altra tabella che dovrà anche memorizzare i valori in t1 .. (t1 avrà collegamenti sia a t2 che a t4) t1 è pensato per essere una tabella per le informazioni di contatto (con i tasti per indirizzo, e-mail e tabelle dei numeri di telefono) e t2 e t4 sono entrambe entità in domini diversi che dovranno avere informazioni di contatto associate a loro .. Potrei avere alcuni dei miei vocaboli errati, ma è essenzialmente per questo. Grazie per la risposta - andrò a testarlo.
Joishi Bodio,

A cura di un piccolo errore. Usa l'ultima versione.
ypercubeᵀᴹ

OK, allora ha senso. Ma potresti non averne bisogno t2_id. Sembra che tu possa usare il t2(t1_id)come PK di t2.
ypercubeᵀᴹ

:) Al momento mi sta dando un errore di sintassi con DEFAULT - cercando di capire quale potrebbe essere. ERROR: syntax error at or near "DEFAULT" LINE 2: DEFAULT AS contact_detail_id
Joishi Bodio,

Hm, sembra che DEFAULTnon possa essere usato in questo modo. Né restituendo ilt.pk
ypercubeᵀᴹ
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.