Đơn giản với hstore
Nếu bạn đã hstore
cài đặt mô-đun bổ sung ( hướng dẫn trong liên kết bên dưới ), có một cách đơn giản đáng ngạc nhiên để thay thế (các) giá trị của (các) trường riêng lẻ mà không biết gì về các cột khác:
Ví dụ cơ bản: nhân đôi hàng bằng id = 2
nhưng thay thế 2
bằng 3
:
INSERT INTO people
SELECT (p #= hstore('id', '3')).* FROM people p WHERE id = 2;
Chi tiết:
Giả sử (vì nó không được xác định trong câu hỏi) đópeople.id
là mộtserial
cột có trình tự đính kèm, bạn sẽ muốn giá trị tiếp theo từ chuỗi. Chúng ta có thể xác định tên trình tự vớipg_get_serial_sequence()
. Chi tiết:
Hoặc bạn chỉ có thể mã hóa tên trình tự nếu nó sẽ không thay đổi.
Chúng tôi sẽ có truy vấn này:
INSERT INTO people
SELECT (p #= hstore('id', nextval(pg_get_serial_sequence('people', 'id'))::text)).*
FROM people p WHERE id = 2;
Cái nào hoạt động , nhưng bị một điểm yếu trong trình lập kế hoạch truy vấn Postgres: Biểu thức được ước tính riêng cho từng cột trong hàng, lãng phí số thứ tự và hiệu suất. Để tránh điều này, hãy di chuyển biểu thức thành một biểu mẫu con và chỉ phân tách hàng một lần :
INSERT INTO people
SELECT (p1).*
FROM (
SELECT p #= hstore('id', nextval(pg_get_serial_sequence('people', 'id'))::text) AS p1
FROM people p WHERE id = 2
) sub;
Có thể nhanh nhất cho một (hoặc vài) hàng (s) cùng một lúc.
json / jsonb
Nếu bạn chưa hstore
cài đặt và không thể cài đặt các mô-đun bổ sung, bạn có thể thực hiện một mẹo tương tự với json_populate_record()
hoặc jsonb_populate_record()
, nhưng khả năng đó không có giấy tờ và có thể không đáng tin cậy.
Bảng tạm thời
Một giải pháp đơn giản khác là sử dụng tạm thời như thế này:
BEGIN;
CREATE TEMP TABLE people_tmp ON COMMIT DROP AS
SELECT * FROM people WHERE id = 2;
UPDATE people_tmp SET id = nextval(pg_get_serial_sequence('people', 'id'));
INSERT INTO people TABLE people_tmp;
COMMIT;
Tôi đã thêm ON COMMIT DROP
để thả bảng tự động vào cuối giao dịch. Do đó, tôi cũng kết thúc hoạt động thành một giao dịch của riêng mình. Không phải là hoàn toàn cần thiết.
Điều này cung cấp một loạt các tùy chọn bổ sung - bạn có thể làm bất cứ điều gì với hàng trước khi chèn, nhưng nó sẽ chậm hơn một chút do chi phí tạo và thả bảng tạm thời.
Giải pháp này hoạt động cho một hàng đơn hoặc cho bất kỳ số lượng hàng nào cùng một lúc . Mỗi hàng tự động nhận một giá trị mặc định mới từ chuỗi.
Sử dụng ký hiệu ngắn (tiêu chuẩn SQL)TABLE people
.
SQL động
Đối với nhiều hàng cùng một lúc, SQL động sẽ nhanh nhất. Nối các cột từ bảng hệ thống pg_attribute
hoặc từ lược đồ thông tin và thực hiện nó một cách linh hoạt trong một DO
câu lệnh hoặc viết một hàm để sử dụng nhiều lần:
CREATE OR REPLACE FUNCTION f_row_copy(_tbl regclass, _id int, OUT row_ct int) AS
$func$
BEGIN
EXECUTE (
SELECT format('INSERT INTO %1$s(%2$s) SELECT %2$s FROM %1$s WHERE id = $1',
_tbl, string_agg(quote_ident(attname), ', '))
FROM pg_attribute
WHERE attrelid = _tbl
AND NOT attisdropped -- no dropped (dead) columns
AND attnum > 0 -- no system columns
AND attname <> 'id' -- exclude id column
)
USING _id;
GET DIAGNOSTICS row_ct = ROW_COUNT; -- directly assign OUT parameter
END
$func$ LANGUAGE plpgsql;
Gọi:
SELECT f_row_copy('people', 9);
Hoạt động cho bất kỳ bảng với một cột số nguyên có tên id
. Bạn cũng có thể dễ dàng đặt tên cột động ...
Có thể không phải là lựa chọn đầu tiên của bạn vì bạn muốn stay away from stored procedures
, nhưng một lần nữa, đó không phải là "thủ tục được lưu trữ" ...
Liên quan:
Giải pháp tiên tiến
Một serial
cột là một trường hợp đặc biệt. Nếu bạn muốn điền nhiều hơn hoặc tất cả các cột với các giá trị mặc định tương ứng của chúng, thì nó sẽ phức tạp hơn. Hãy xem xét câu trả lời liên quan này:
age
là một kiểu chống mẫu cho một cột. (Một người nên lưu trữbirthday
.)