INSERT sử dụng kết quả của CTE INSERT để cung cấp các giá trị id duy nhất


8

Tôi đang viết một công việc để chuyển đổi dữ liệu từ một thiết kế cũ sang một thiết kế mới. Trong quá trình này, tôi cần lấy id từ một tệp chèn vào một bảng riêng biệt và sử dụng mã đó trong một tệp chèn vào bảng đích, như sau:

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

Tôi có SQL được định nghĩa khớp với mẫu sau:

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;

Tôi muốn điều này để chạy SELECT * FROM inscho mọi hàng của SELECT.. nhưng thay vào đó, nó chỉ chạy một lần và sử dụng giá trị đó cho tất cả các hàng trong SELECT. Làm cách nào tôi có thể cấu trúc lại SQL của mình để có được hành vi mong muốn?

chỉnh sửa4

t1 kết thúc giống như:

1,<NULL>
(1 row)

t2 kết thúc giống như:

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

Những gì tôi muốn t1 trông giống như:

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

Những gì tôi muốn t2 trông giống như:

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

chỉnh sửa Để giải quyết những gì a_horse_with_no_name đã nói, tôi cũng đã thử điều này (với kết quả tương tự):

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 Tôi vừa thử trực tiếp tham chiếu thích hợp SEQUENCEtrong truy vấn của mình và điều đó KHÔNG hoạt động - nhưng tôi hoàn toàn không thích giải pháp đó (chủ yếu là vì tôi không thích tên đối tượng mã hóa cứng.) Nếu có giải pháp nào khác hơn là trực tiếp tham khảo tên của SEQUENCEtôi sẽ đánh giá cao nó. :)

chỉnh sửa3 Tôi cho rằng một giải pháp khác là sử dụng một PROCEDUREđể làm INSERTthay vì CTE .. nhưng tôi vẫn đánh giá cao các tùy chọn / đề xuất.


1
Bạn cần tham gia inst3
a_horse_with_no_name 20/03/2016

Tôi cũng đã thử điều đó và nó vẫn chỉ tính giá trị một lần. Nhưng có lẽ tôi đã không tham gia khá chính xác. Tôi sẽ chỉnh sửa bài viết của mình để hiển thị những gì tôi đã cố gắng với điều đó.
Joishi Bodio

1
Bạn chỉ chèn một hàng vào t1và không cung cấp bất kỳ giá trị nào cho t1.col1. Dữ liệu nên đến đâu cho cột đó? Có t1.col1liên quan đến t2.col1?
ypercubeᵀᴹ

ypercube - t1.col1 được phép là NULL và sẽ được chèn vào trong một quy trình sau. Bởi vì tôi đã tham chiếu CTE như là một SUBSELECT trong các giá trị hàng thực tế, tôi đoán rằng nó sẽ được thực thi nhiều lần - nhưng hóa ra tôi đã không chính xác trong giả định đó .. đó là lý do tại sao tôi hỏi câu hỏi này ở đây. Tôi đã thử nghiên cứu để có câu trả lời trên google trong vài giờ qua và chưa thể tìm thấy điều gì chính xác. Và không, t1.col1 không liên quan đến t2.col1 .. xin lỗi vì sự nhầm lẫn đó.
Joishi Bodio

1
Tuy nhiên, INSERT INTO t1 (t1_id) VALUES (DEFAULT)chỉ chèn 1 hàng vào t1. Vì vậy, không có vấn đề gì nếu bạn đặt instrong FROMmệnh đề và tham gia t3hay không. Bạn có thể chỉ cho chúng tôi cách bạn sẽ chèn 2 (hoặc nhiều) hàng vào t1không? Và quan trọng hơn, làm thế nào bạn biết t1.idgiá trị nào trong số 2 (hoặc nhiều hơn) sẽ được khớp với các hàng được chèn vào t2?
ypercubeᵀᴹ

Câu trả lời:


7

Tôi không hiểu tại sao bạn cần 2 bàn nếu họ chỉ có mối quan hệ 1-1. Nhưng đây là ( pklà khóa chính của 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) ;

Nếu t3 của bạn là kết quả của một CHỌN thay vì bảng có sẵn, bạn có thể triển khai nó để bạn không phải lặp lại truy vấn t3 hai lần:

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

Lý do tại sao tôi cần hai bảng là vì có một bảng khác cũng sẽ cần lưu trữ giá trị trong t1 .. (t1 sẽ có liên kết đến cả t2 và t4) t1 có nghĩa là bảng dành cho thông tin liên hệ (có fkey để địa chỉ, email và bảng số điện thoại) và t2 và t4 là cả hai thực thể trong các miền khác nhau sẽ cần phải có thông tin liên hệ với chúng .. Tôi có thể có một số từ vựng không chính xác, nhưng về cơ bản là tại sao. Cảm ơn bạn đã trả lời - Tôi sẽ đi kiểm tra nó.
Joishi Bodio

Chỉnh sửa một lỗi nhỏ. Sử dụng phiên bản mới nhất.
ypercubeᵀᴹ

OK, nó có ý nghĩa sau đó. Nhưng bạn có thể không cần t2_idtất cả. Có vẻ như bạn có thể sử dụng t2(t1_id)như PK của t2.
ypercubeᵀᴹ

:) Nó đang cho tôi một lỗi cú pháp với DEFAULT tại thời điểm này - cố gắng tìm hiểu xem nó có thể là gì. ERROR: syntax error at or near "DEFAULT" LINE 2: DEFAULT AS contact_detail_id
Joishi Bodio

Hừm, dường như DEFAULTkhông thể sử dụng theo cách đó. Cũng không trả lạit.pk
ypercubeᵀᴹ 20/03/2016
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.