INSERT menggunakan hasil CTE INSERT untuk memberikan nilai id unik

8

Saya menulis pekerjaan untuk mengubah data dari desain lama menjadi desain baru. Dalam proses ini, saya perlu mengambil id dari sisipan ke tabel terpisah dan menggunakannya dalam sisipan ke tabel target, seperti:

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

Saya memiliki SQL yang didefinisikan yang cocok dengan formulir berikut:

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;

Saya ingin ini untuk menjalankan SELECT * FROM insuntuk setiap baris SELECT.. tetapi alih-alih hanya menjalankannya sekali dan menggunakan nilai itu untuk semua baris di SELECT. Bagaimana saya bisa merestrukturisasi SQL saya untuk mendapatkan perilaku yang diinginkan?

sunting4

t1 akhirnya tampak seperti:

1,<NULL>
(1 row)

t2 akhirnya tampak seperti:

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

Apa yang saya ingin t1 terlihat seperti:

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

Apa yang saya ingin t2 terlihat seperti:

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

sunting Untuk mengatasi apa yang dikatakan a_horse_with_no_name, saya juga mencoba ini (dengan hasil yang sama):

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;

sunting2 Saya baru saja mencoba mereferensikan yang sesuai SEQUENCEdengan permintaan saya, dan itu TIDAK berfungsi - tapi saya tidak terlalu menyukai solusi itu (kebanyakan karena saya tidak suka nama objek yang sulit dikodekan). Jika ada solusi APA PUN lainnya daripada langsung merujuk nama SEQUENCEsaya akan sangat menghargainya. :)

sunting3 Saya kira solusi lain adalah dengan menggunakan a PROCEDUREuntuk melakukan INSERTbukan CTE .. tapi saya masih menghargai opsi / saran.

Joishi Bodio
sumber
1
Anda harus bergabung insdant3
a_horse_with_no_name
Saya mencobanya juga dan masih hanya menghitung nilainya sekali saja. Tapi mungkin aku tidak benar benar. Saya akan mengedit posting saya untuk menunjukkan apa yang saya coba dengan itu.
Joishi Bodio
1
Anda hanya memasukkan satu baris ke dalam t1dan tidak memberikan nilai apa pun untuk t1.col1. Di mana seharusnya data datang untuk kolom itu? Apakah t1.col1terkait dengan t2.col1?
ypercubeᵀᴹ
ypercube - t1.col1 diizinkan menjadi NULL dan akan dimasukkan dalam proses selanjutnya. Karena saya merujuk CTE sebagai SUBSELECT dalam nilai baris aktual, saya pikir itu akan dieksekusi lebih dari sekali - tetapi ternyata saya salah dalam asumsi itu .. itulah sebabnya saya mengajukan pertanyaan ini di sini. Saya sudah mencoba meneliti jawaban di google selama beberapa jam terakhir dan belum dapat menemukan yang benar. Dan tidak, t1.col1 tidak terkait dengan t2.col1 .. maaf atas kebingungan itu.
Joishi Bodio
1
Namun, INSERT INTO t1 (t1_id) VALUES (DEFAULT)hanya memasukkan 1 baris ke dalam t1. Jadi tidak masalah jika Anda meletakkan insdi FROMklausa dan bergabung ke t3atau tidak. Bisakah Anda menunjukkan kepada kami bagaimana Anda akan memasukkan 2 (atau lebih) baris ke dalam t1? Dan yang lebih penting, bagaimana Anda tahu yang mana dari 2 (atau lebih) t1.idnilai yang akan cocok dengan baris yang dimasukkan t2?
ypercubeᵀᴹ

Jawaban:

8

Saya tidak mengerti mengapa Anda membutuhkan 2 tabel jika hanya memiliki 1-1 hubungan. Tapi ini dia ( pkadalah kunci utama 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) ;

Jika t3 Anda adalah hasil SELECT alih-alih tabel yang sudah ada, Anda bisa mengimplementasikannya sehingga Anda tidak perlu mengulangi permintaan t3 dua kali:

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ᵀᴹ
sumber
Alasan mengapa saya memerlukan kedua tabel tersebut adalah karena ada tabel lain yang juga perlu menyimpan nilai dalam t1 .. (t1 akan memiliki tautan ke t2 dan t4) t1 dimaksudkan sebagai tabel untuk informasi kontak (dengan fkeys untuk alamat, email, dan tabel nomor telepon) dan t2 dan t4 keduanya adalah entitas dalam domain yang berbeda yang perlu memiliki informasi kontak yang terkait dengan mereka .. Saya mungkin memiliki beberapa kosa kata yang salah, tetapi itulah sebabnya. Terima kasih atas jawabannya - Saya akan mengujinya.
Joishi Bodio
Mengedit kesalahan kecil. Gunakan versi terbaru.
ypercubeᵀᴹ
OK, masuk akal kalau begitu. Tetapi Anda mungkin tidak perlu t2_idsama sekali. Sepertinya Anda bisa menggunakan t2(t1_id)PK t2.
ypercubeᵀᴹ
:) Ini memberi saya kesalahan sintaks dengan DEFAULT saat ini - mencoba untuk mencari tahu apa itu mungkin. ERROR: syntax error at or near "DEFAULT" LINE 2: DEFAULT AS contact_detail_id
Joishi Bodio
Hm, sepertinya DEFAULTtidak bisa seperti itu. Atau mengembalikant.pk
ypercubeᵀᴹ