Chèn một hàng vào bảng là điều bạn nghĩ đến khi nghĩ đến câu lệnh INSERT trong PostgreSQL. Tuy nhiên, nó có thêm một vài thủ thuật nữa! Đọc tiếp để khám phá một số điều thú vị hơn mà bạn có với INSERT.
Sao chép hàng loạt
Giả sử bạn muốn định kỳ chụp ảnh nhanh của bảng - tất cả các hàng trong bảng phải được sao chép sang một bảng khác, với cột dấu thời gian bổ sung hiển thị khi ảnh chụp nhanh được chụp. Đây là cách bạn có thể tạo và điền vào bảng lần đầu tiên:
demo=# SELECT * FROM mytable;
ticker | quote
--------+-------
FOO | $4.01
BAR | $1.42
(2 rows)
demo=# CREATE TABLE snaps_of_mytable AS
demo-# SELECT current_timestamp AS snapped_at, *
demo-# FROM mytable;
SELECT 2
demo=#
demo=# SELECT * FROM snaps_of_mytable ;
snapped_at | ticker | quote
-----------------------------+--------+-------
2018-10-09 04:16:22.3613+00 | FOO | $4.01
2018-10-09 04:16:22.3613+00 | BAR | $1.42
(2 rows)
Và từ đó trở đi, bạn có thể sử dụng INSERT..SELECT
dạng câu lệnh INSERT để quét các hàng từ một bảng và chèn vào một bảng khác. Bạn cũng có thể điền thêm các giá trị vào hàng của bảng đích.
demo=# INSERT INTO snaps_of_mytable
demo-# SELECT current_timestamp AS snapped_at, *
demo-# FROM mytable;
INSERT 0 2
demo=#
demo=# SELECT * FROM snaps_of_mytable ;
snapped_at | ticker | quote
-------------------------------+--------+-------
2018-10-09 04:16:22.3613+00 | FOO | $4.01
2018-10-09 04:16:22.3613+00 | BAR | $1.42
2018-10-09 04:18:53.432224+00 | BAR | $1.42
2018-10-09 04:18:53.432224+00 | FOO | $4.10
(4 rows)
Upserts
Trong PostgreSQL 9.5, ON CONFLICT
mệnh đề đã được thêm vào INSERT. Điều này cho phép các nhà phát triển ứng dụng viết ít mã hơn và làm được nhiều việc hơn trong SQL.
Đây là bảng các cặp khóa, giá trị:
demo=# SELECT * FROM kv;
key | value
------+-----------
host | 127.0.0.1
port | 5432
(2 rows)
Một trường hợp sử dụng phổ biến là chỉ chèn một hàng nếu nó không tồn tại - và nếu sai, đừng ghi đè. Điều này được thực hiện với ON CONFLICT..DO NOTHING
mệnh đề của câu lệnh INSERT:
demo=# INSERT INTO kv (key, value) VALUES ('port', '3306')
demo-# ON CONFLICT (key) DO NOTHING;
INSERT 0 0
demo=# SELECT * FROM kv;
key | value
------+-----------
host | 127.0.0.1
port | 5432
(2 rows)
Một cách sử dụng phổ biến khác là chèn một hàng nếu nó không tồn tại và cập nhật giá trị, nếu nó có. Điều này có thể được thực hiện với ON CONFLICT..DO UPDATE
mệnh đề.
demo=# INSERT INTO kv (key, value) VALUES ('host', '10.0.10.1')
demo-# ON CONFLICT (key) DO UPDATE SET value=EXCLUDED.value;
INSERT 0 1
demo=# INSERT INTO kv (key, value) VALUES ('ssl', 'off')
demo-# ON CONFLICT (key) DO UPDATE SET value=EXCLUDED.value;
INSERT 0 1
demo=# SELECT * FROM kv;
key | value
------+-----------
host | 10.0.10.1
port | 5432
ssl | off
(3 rows)
Trong trường hợp đầu tiên, giá trị của "host" ở trên đã bị ghi đè bằng giá trị mới và trong trường hợp thứ hai, giá trị của "ssl" đã được chèn vào hàng thứ ba.
Thậm chí có thể thực hiện các trường hợp sử dụng phức tạp hơn với DO UPDATE
. Hãy xem xét bảng bên dưới, trong đó ngoài khóa và giá trị, còn có một cột được gọi là "tích lũy". Đối với các hàng trong đó tích lũy là đúng, các giá trị có nghĩa là được tích lũy dưới dạng một chuỗi được phân tách bằng dấu phẩy. Đối với các hàng khác, các giá trị có giá trị đơn.
demo=# CREATE TABLE kv2 (
demo(# key text PRIMARY KEY,
demo(# accumulate boolean NOT NULL DEFAULT false,
demo(# value text
demo(# );
CREATE TABLE
demo=# INSERT INTO kv2 VAlUES
demo-# ('port', false, '5432'),
demo-# ('listen', true, NULL);
INSERT 0 2
demo=# SELECT * FROM kv2;
key | accumulate | value
--------+------------+-------
port | f | 5432
listen | t |
(2 rows)
WHERE
mệnh đề có thể được sử dụng để ghi đè cột "giá trị", hoặc thêm vào đó, tùy thuộc vào giá trị của "tích lũy", như sau:
demo=# INSERT INTO kv2 AS t (key, value) VALUES ('port', '3306')
demo-# ON CONFLICT (key) DO UPDATE SET value = concat_ws(',', t.value, EXCLUDED.value)
demo-# WHERE t.accumulate;
INSERT 0 0
demo=# INSERT INTO kv2 AS t (key, value) VALUES ('listen', '127.0.0.1')
demo-# ON CONFLICT (key) DO UPDATE SET value = concat_ws(',', t.value, EXCLUDED.value)
demo-# WHERE t.accumulate;
INSERT 0 1
demo=# INSERT INTO kv2 AS t (key, value) VALUES ('listen', '10.0.10.1')
demo-# ON CONFLICT (key) DO UPDATE SET value = concat_ws(',', t.value, EXCLUDED.value)
demo-# WHERE t.accumulate;
INSERT 0 1
demo=# SELECT * FROM kv2;
key | accumulate | value
--------+------------+---------------------
port | f | 5432
listen | t | 127.0.0.1,10.0.10.1
(2 rows)
Câu lệnh đầu tiên không tích lũy giá trị của ‘3306’ vào ‘port’ vì ‘accumulate ’đã bị tắt cho hàng đó. Hai câu lệnh tiếp theo đã thêm các giá trị ‘127.0.0.1 ’và ‘10 .0.10.1’ vào giá trị của ‘nghe’ vì ‘tích lũy’ là đúng.
Trả lại các giá trị đã tạo
Các giá trị được tạo bởi PostgreSQL trong quá trình chèn, như giá trị mặc định hoặc giá trị SERIAL được tăng cường tự động có thể được trả về bằng cách sử dụng RETURNING
mệnh đề của câu lệnh INSERT.
Giả sử bạn cần tạo UUID ngẫu nhiên làm khóa cho các hàng trong bảng. Bạn có thể để PostgreSQL thực hiện công việc tạo các UUID và để nó trả về giá trị đã tạo cho bạn như sau:
demo=# INSERT INTO kv (key, value) VALUES (gen_random_uuid(), 'foo') RETURNING key;
key
--------------------------------------
d93ceaa5-30a8-4285-83c5-7defa79e2f90
(1 row)
INSERT 0 1
demo=# INSERT INTO kv (key, value) VALUES (gen_random_uuid(), 'bar') RETURNING key;
key
--------------------------------------
caf9c5d9-9a79-4b26-877f-a75a083b0c79
(1 row)
INSERT 0 1
demo=# SELECT * FROM kv;
key | value
--------------------------------------+-------
d93ceaa5-30a8-4285-83c5-7defa79e2f90 | foo
caf9c5d9-9a79-4b26-877f-a75a083b0c79 | bar
(2 rows)
Hàng Di chuyển có Điều khoản CTE
Bạn thậm chí có thể di chuyển các hàng giữa các bảng bằng INSERT, sử dụng WITH
Đây là hai bảng với danh sách việc cần làm cho các năm khác nhau.
demo=# SELECT * FROM todos_2018;
what | done
----------------+------
thing to do #1 | t
thing to do #2 | t
thing to do #3 | f
(3 rows)
demo=# SELECT * FROM todos_2019;
what | done
------+------
(0 rows)
Để di chuyển các mục công việc chưa hoàn thành trong năm 2018 sang năm 2019, bạn có thể xóa cơ bản các hàng như vậy khỏi bảng 2018 và chèn chúng vào bảng 2019 trong một lần:
demo=# WITH items AS (
demo(# DELETE FROM todos_2018
demo(# WHERE NOT done
demo(# RETURNING *
demo(# )
demo-# INSERT INTO todos_2019 SELECT * FROM items;
INSERT 0 1
demo=# SELECT * FROM todos_2018;
what | done
----------------+------
thing to do #1 | t
thing to do #2 | t
(2 rows)
demo=# SELECT * FROM todos_2019;
what | done
----------------+------
thing to do #3 | f
(1 row)
Để tìm hiểu thêm về câu lệnh INSERT nhỏ thông minh, hãy xem tài liệu và thử nghiệm!