INSERT verwendet die Ergebnisse von CTE INSERT, um eindeutige ID-Werte bereitzustellen

8

Ich schreibe einen Job, um Daten von einem alten Design in ein neues Design umzuwandeln. In diesem Prozess muss ich die ID aus einer Einfügung in eine separate Tabelle übernehmen und diese in einer Einfügung in die Zieltabelle als solche verwenden:

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

Ich habe die SQL definiert, die der folgenden Form entspricht:

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;

Ich wollte, dass dies SELECT * FROM insfür jede Zeile des SELECT.. ausgeführt wird, aber stattdessen wird es nur einmal ausgeführt und verwendet diesen Wert für alle Zeilen im SELECT. Wie kann ich mein SQL umstrukturieren, um das gewünschte Verhalten zu erzielen?

edit4

t1 sieht am Ende so aus:

1,<NULL>
(1 row)

t2 sieht am Ende so aus:

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

Wie soll t1 aussehen:

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

Wie soll t2 aussehen:

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

edit Um zu adressieren, was a_horse_with_no_name gesagt hat, habe ich auch Folgendes versucht (mit dem gleichen Ergebnis):

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 Ich habe gerade versucht, SEQUENCEin meiner Abfrage direkt auf das entsprechende zu verweisen, und das funktioniert - aber ich mag diese Lösung überhaupt nicht sehr (hauptsächlich, weil ich keine hartcodierten Objektnamen mag.) Wenn es eine andere Lösung gibt als direkt auf den Namen des zu verweisen, SEQUENCEwürde ich es schätzen. :) :)

EDIT3 Ich nehme eine andere Lösung wäre die Verwendung eines machen PROCEDUREdas zu tun , INSERTanstelle eines CTE .. aber ich würde immer noch Anerkennung Optionen / Vorschläge.

Joishi Bodio
quelle
1
Sie müssen beitreten insundt3
a_horse_with_no_name
Ich habe das auch versucht und es hat den Wert immer noch nur einmal berechnet. Aber vielleicht hatte ich meine Verbindung nicht ganz richtig. Ich werde meinen Beitrag bearbeiten, um zu zeigen, was ich damit versucht habe.
Joishi Bodio
1
Sie fügen nur eine Zeile in ein t1und geben keinen Wert für ein t1.col1. Woher sollen die Daten für diese Spalte kommen? Ist t1.col1verwandt mit t2.col1?
Ypercubeᵀᴹ
ypercube - t1.col1 darf NULL sein und wird in einem späteren Prozess eingefügt. Da ich den CTE in den tatsächlichen Zeilenwerten als SUBSELECT bezeichnet habe, dachte ich, dass er mehr als einmal ausgeführt werden würde - aber es stellt sich heraus, dass ich in dieser Annahme falsch war. Deshalb stelle ich diese Frage hier. Ich habe bereits in den letzten Stunden versucht, auf Google nach einer Antwort zu suchen, und konnte noch nicht herausfinden, was richtig ist. Und nein, t1.col1 ist nicht mit t2.col1 verwandt. Entschuldigen Sie diese Verwirrung.
Joishi Bodio
1
Fügt dennoch INSERT INTO t1 (t1_id) VALUES (DEFAULT)nur 1 Zeile in ein t1. Es spielt also keine Rolle, ob Sie das insin die FROMKlausel einfügen und es verbinden t3oder nicht. Können Sie uns zeigen, wie Sie 2 (oder mehr) Zeilen einfügen würden t1? Und was noch wichtiger ist: Woher wissen Sie, welcher der 2 (oder mehr) t1.idWerte mit den eingefügten Zeilen übereinstimmt t2?
Ypercubeᵀᴹ

Antworten:

7

Ich verstehe nicht, warum Sie 2 Tabellen benötigen, wenn sie nur 1-1 Beziehung haben. Aber hier ist es ( pkist der Primärschlüssel von 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) ;

Wenn Ihr t3 das Ergebnis eines SELECT anstelle einer bereits vorhandenen Tabelle ist, können Sie es als solches implementieren, sodass Sie die t3-Abfrage nicht zweimal wiederholen müssen:

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);
ypercubeᵀᴹ
quelle
Der Grund, warum ich die beiden Tabellen benötige, ist, dass es eine andere Tabelle gibt, in der auch Werte in t1 gespeichert werden müssen. (T1 hat Links zu t2 und t4.) T1 ist eine Tabelle für Kontaktinformationen (mit fkeys to) Adress-, E-Mail- und Telefonnummertabellen) sowie t2 und t4 sind Entitäten in verschiedenen Domänen, denen Kontaktinformationen zugeordnet werden müssen. Möglicherweise ist ein Teil meines Wortschatzes falsch, aber das ist im Wesentlichen der Grund. Vielen Dank für die Antwort - ich werde es ausprobieren.
Joishi Bodio
Einen kleinen Fehler bearbeitet. Verwenden Sie die neueste Version.
Ypercubeᵀᴹ
OK, dann macht es Sinn. Aber vielleicht brauchen Sie das t2_idüberhaupt nicht. Scheint, Sie können die t2(t1_id)als PK von verwenden t2.
Ypercubeᵀᴹ
:) Es gibt mir im Moment einen Syntaxfehler mit DEFAULT - ich versuche herauszufinden, was es sein könnte. ERROR: syntax error at or near "DEFAULT" LINE 2: DEFAULT AS contact_detail_id
Joishi Bodio
Hm, es scheint DEFAULTnicht so zu sein. Noch diet.pk
ypercubeᵀᴹ