it-swarm.com.de

FÜGEN Sie Zeilen in mehrere Tabellen in einer einzelnen Abfrage ein und wählen Sie aus einer beteiligten Tabelle aus

Ich habe zwei Tabellen des folgenden Formulars (d. H. Jedes Foo ist mit genau einem Takt verbunden).

CREATE TABLE foo (
    id INTEGER PRIMARY KEY,
    x INTEGER NOT NULL,
    y INTEGER NOT NULL,
    ...,
    bar_id INTEGER UNIQUE NOT NULL,
    FOREIGN key (bar_id) REFERENCES bar(id)
);

CREATE TABLE bar (
    id INTEGER PRIMARY KEY,
    z INTEGER NOT NULL,
    ...
);

Es ist einfach, Zeilen in foo zu kopieren, die mit einer verschachtelten Abfrage eine bestimmte Bedingung erfüllen:

INSERT INTO foo (...) (SELECT ... FROM foo WHERE ...)

Ich kann jedoch nicht herausfinden, wie ich eine Kopie der zugehörigen Zeile in bar für jede Zeile in foo erstellen und die ID von bar in die neue foo-Zeile einfügen kann. Gibt es eine Möglichkeit, dies in einer einzelnen Abfrage zu tun?

Konkretes Beispiel für das gewünschte Ergebnis:

-- Before query:

foo(id=1,x=3,y=4,bar_id=100)  .....  bar(id=100,z=7)
foo(id=2,x=9,y=6,bar_id=101)  .....  bar(id=101,z=16)
foo(id=3,x=18,y=0,bar_id=102) .....  bar(id=102,z=21)


-- Query copies all pairs of foo/bar rows for which x>3:

-- Originals
foo(id=1,x=3,y=4,bar_id=101)  .....  bar(id=101,z=7)
foo(id=2,x=9,y=6,bar_id=102)  .....  bar(id=102,z=16)
foo(id=3,x=18,y=0,bar_id=103) .....  bar(id=103,z=21)

-- "Copies" of foo(id=2,...) and foo(id=3,...), with matching copies of
-- bar(id=102,...) and bar(id=103,...)
foo(id=4,x=9,y=6,bar_id=104)  .....  bar(id=104,z=16)
foo(id=5,x=18,y=0,bar_id=105) .....  bar(id=105,z=21)
17
foldl

Endgültige Version

... nach ein paar Infos von OP. Betrachten Sie diese Demo:

-- DROP TABLE foo; DROP TABLE bar;

CREATE TEMP TABLE bar (
 id serial PRIMARY KEY  -- using a serial column!
,z  integer NOT NULL
);

CREATE TEMP TABLE foo (
 id     serial PRIMARY KEY  -- using a serial column!
,x      integer NOT NULL
,y      integer NOT NULL
,bar_id integer UNIQUE NOT NULL REFERENCES bar(id)
);

Fügen Sie zuerst die Werte barein.
Es wäre sehr hilfreich, wenn Sie in Ihrer Frage Testdaten wie diese angeben würden!

INSERT INTO bar (id,z) VALUES
 (100, 7)
,(101,16)
,(102,21);

INSERT INTO foo (id, x, y, bar_id) VALUES
 (1, 3,4,100)
,(2, 9,6,101)
,(3,18,0,102);

Setzen Sie die Sequenzen auf die aktuellen Werte, oder wir erhalten doppelte Schlüsselverletzungen:

SELECT setval('foo_id_seq', 3);
SELECT setval('bar_id_seq', 102);

Schecks:

-- SELECT nextval('foo_id_seq')
-- SELECT nextval('bar_id_seq')
-- SELECT * from bar;
-- SELECT * from foo;

Abfrage:

WITH a AS (
    SELECT f.x, f.y, bar_id, b.z
    FROM   foo f
    JOIN   bar b ON b.id = f.bar_id
    WHERE  x > 3
    ),b AS (
    INSERT INTO bar (z)
    SELECT z
    FROM   a
    RETURNING z, id AS bar_id
    )
INSERT INTO foo (x, y, bar_id)
SELECT a.x, a.y, b.bar_id
FROM   a
JOIN   b USING (z);

Dies sollte tun, was Ihr letztes Update beschreibt.

Die Abfrage geht davon aus, dass zUNIQUENAME _ ist. Wenn znicht eindeutig ist, wird es komplexer. In Query 2 in dieser verwandten Antwort finden Sie eine fertige Lösung, die in diesem Fall die Fensterfunktion row_number() verwendet.

Erwägen Sie außerdem, die 1: 1-Beziehung zwischen foound bardurch eine einzelne vereinte Tabelle zu ersetzen.


Datenmodifizierender CTE

Zweite Antwort nach mehr Info.

Wenn Sie Zeilen zu foo und barin einer einzelnen Abfrage hinzufügen möchten, können Sie ein data modified CTE seit PostgreSQL - verwenden. 9.1:

WITH x AS (
    INSERT INTO bar (col1, col2)
    SELECT f.col1, f.col2
    FROM   foo f
    WHERE  f.id BETWEEN 12 AND 23 -- some filter
    RETURNING col1, col2, bar_id  -- assuming bar_id is a serial column
    )
INSERT INTO foo (col1, col2, bar_id)
SELECT col1, col2, bar_id
FROM   x;

Ich zeichne Werte aus fooname__, füge sie in barein, lasse sie zusammen mit einem automatisch generierten bar_id zurückgeben und füge das in fooein. Sie können auch andere Daten verwenden.

Hier ist eine - Arbeitsdemo zum Spielen auf sqlfiddle .


Grundlagen

Originalantwort mit Basisinformationen vor Abklärungen.
Die Grundform ist:

INSERT INTO foo (...)
SELECT ... FROM foo WHERE ...

Keine Klammern erforderlich. Sie können dasselbe mit jedem Tisch machen

INSERT INTO foo (...)
SELECT ... FROM bar WHERE ...

Und Sie können der Tabelle beitreten, in die Sie in SELECT einfügen:

INSERT INTO foo (...)
SELECT f.col1, f.col2, .. , b.bar_id
FROM   foo f
JOIN   bar b USING (foo_id);  -- present in foo and bar

Es ist nur ein SELECT wie jedes andere, das die Tabelle enthalten kann, in die Sie einfügen. Die Zeilen werden zuerst gelesen und dann eingefügt.

29

wenn id von bar seriell ist und den Standardwert nextval('bar_id_seq'::regclass) hat, können Sie diese Funktion manuell aufrufen, um neue IDs in cte zu erhalten

with
s_bar as (
  SELECT id, z, nextval('bar_id_seq'::regclass) new_id
  FROM   bar
  WHERE  ...
),
s_foo as (
  SELECT x, y, bar_id
  FROM   foo
  WHERE  ...
),
i_bar as (
  INSERT INTO bar (id, z)
  SELECT new_id, z
  FROM   s_bar
),
i_foo as (
  INSERT INTO foo (x, y, bar_id)
  SELECT f.x, f.y, b.new_id
  FROM   s_foo f
  JOIN   s_bar b on b.id = f.bar_id
)
SELECT 1
0
Matveev Dmitriy