Với VALUES
độc lập biểu thức PostgreSQL không biết các kiểu dữ liệu phải là gì. Với các ký tự số đơn giản, hệ thống rất vui khi giả định các kiểu khớp. Nhưng với đầu vào khác (như NULL
) bạn sẽ cần truyền một cách rõ ràng - như bạn đã biết.
Bạn có thể truy vấn pg_catalog
(nhanh, nhưng dành riêng cho PostgreSQL) hoặc information_schema
(SQL chậm, nhưng chuẩn) để tìm ra và chuẩn bị câu lệnh của bạn với các kiểu thích hợp.
Hoặc bạn có thể sử dụng một trong những "thủ thuật" đơn giản này (Tôi đã lưu điều tốt nhất cho cuối cùng ):
0. Chọn hàng có LIMIT 0
, nối các hàng với UNION ALL VALUES
UPDATE foo f
SET x = t.x
, y = t.y
FROM (
(SELECT pkid, x, y FROM foo LIMIT 0) -- parenthesis needed with LIMIT
UNION ALL
VALUES
(1, 20, NULL) -- no type casts here
, (2, 50, NULL)
) t -- column names and types are already defined
WHERE f.pkid = t.pkid;
Lựa chọn phụ đầu tiên của truy vấn phụ:
(SELECT x, y, pkid FROM foo LIMIT 0)
lấy tên và kiểu cho các cột, nhưng LIMIT 0
ngăn nó thêm một hàng thực tế. Các hàng tiếp theo bị ép buộc về loại hàng hiện đã được xác định rõ - và được kiểm tra ngay lập tức xem chúng có khớp với loại hay không. Sẽ là một cải tiến bổ sung tinh tế so với biểu mẫu ban đầu của bạn.
Trong khi cung cấp các giá trị cho tất cả các cột của bảng có thể sử dụng cú pháp ngắn gọn này cho hàng đầu tiên:
(TABLE foo LIMIT 0)
Hạn chế chính :Postgres truyền các ký tự đầu vào của VALUES
độc lập biểu thức thành loại "nỗ lực cao nhất" ngay lập tức. Khi nó sau đó cố gắng truyền đến các loại nhất định của SELECT
đầu tiên , có thể đã quá muộn đối với một số kiểu nếu không có phép gán đã đăng ký ép kiểu giữa kiểu giả định và kiểu đích. Ví dụ:text
-> timestamp
hoặc text
-> json
.
Chuyên gia:
- Chi phí tối thiểu.
- Dễ đọc, đơn giản và nhanh chóng.
- Bạn chỉ cần biết các tên cột có liên quan của bảng.
Con:
- Độ phân giải loại có thể không thành công đối với một số loại.
1. Chọn hàng có LIMIT 0
, nối các hàng với UNION ALL SELECT
UPDATE foo f
SET x = t.x
, y = t.y
FROM (
(SELECT pkid, x, y FROM foo LIMIT 0) -- parenthesis needed with LIMIT
UNION ALL SELECT 1, 20, NULL
UNION ALL SELECT 2, 50, NULL
) t -- column names and types are already defined
WHERE f.pkid = t.pkid;
Chuyên gia:
- Thích 0. , nhưng tránh việc phân giải loại không thành công.
Con:
-
UNION ALL SELECT
chậm hơnVALUES
biểu thức cho danh sách dài các hàng, như bạn đã tìm thấy trong thử nghiệm của mình. - Cú pháp dài dòng trên mỗi hàng.
2. VALUES
biểu thức với loại mỗi cột
...
FROM (
VALUES
((SELECT pkid FROM foo LIMIT 0)
, (SELECT x FROM foo LIMIT 0)
, (SELECT y FROM foo LIMIT 0)) -- get type for each col individually
, (1, 20, NULL)
, (2, 50, NULL)
) t (pkid, x, y) -- columns names not defined yet, only types.
...
Trái ngược với 0. điều này tránh việc phân giải loại sớm.
Hàng đầu tiên trong VALUES
biểu thức là một hàng NULL
giá trị xác định kiểu cho tất cả các hàng tiếp theo. Hàng nhiễu hàng đầu này được lọc bởi WHERE f.pkid = t.pkid
sau đó, vì vậy nó không bao giờ nhìn thấy ánh sáng của ngày. Đối với các mục đích khác, bạn có thể loại bỏ hàng đầu tiên đã thêm bằng OFFSET 1
trong một truy vấn con.
Chuyên gia:
- Thường nhanh hơn 1. (hoặc thậm chí 0 )
- Cú pháp ngắn gọn cho các bảng có nhiều cột và chỉ một số ít có liên quan.
- Bạn chỉ cần biết các tên cột có liên quan của bảng.
Con:
- Cú pháp dài dòng chỉ cho một vài hàng
- Ít đọc hơn (IMO).
3. VALUES
biểu thức với loại hàng
UPDATE foo f
SET x = (t.r).x -- parenthesis needed to make syntax unambiguous
, y = (t.r).y
FROM (
VALUES
('(1,20,)'::foo) -- columns need to be in default order of table
,('(2,50,)') -- nothing after the last comma for NULL
) t (r) -- column name for row type
WHERE f.pkid = (t.r).pkid;
Bạn rõ ràng biết tên bảng. Nếu bạn cũng biết số lượng cột và thứ tự của chúng, bạn có thể làm việc với điều này.
Đối với mỗi bảng trong PostgreSQL, một loại hàng được đăng ký tự động. Nếu bạn khớp với số cột trong biểu thức của mình, bạn có thể truyền sang loại hàng của bảng ('(1,50,)'::foo
) do đó gán các kiểu cột một cách ngầm định. Không đặt gì sau dấu phẩy để nhập NULL
giá trị. Thêm dấu phẩy cho mỗi cột theo sau không liên quan.
Trong bước tiếp theo, bạn có thể truy cập các cột riêng lẻ bằng cú pháp được trình bày. Tìm hiểu thêm về Lựa chọn trường trong sách hướng dẫn.
Hoặc bạn có thể thêm một hàng giá trị NULL và sử dụng cú pháp thống nhất cho dữ liệu thực tế:
...
VALUES
((NULL::foo)) -- row of NULL values
, ('(1,20,)') -- uniform ROW value syntax for all
, ('(2,50,)')
...
Chuyên gia:
- Nhanh nhất (ít nhất là trong các thử nghiệm của tôi với ít hàng và cột).
- Cú pháp ngắn nhất cho một số hàng hoặc bảng mà bạn cần tất cả các cột.
- Bạn không cần phải viết chính tả các cột trong bảng - tất cả các cột đều tự động có tên phù hợp.
Con:
- Cú pháp không quá nổi tiếng để chọn trường từ loại bản ghi / hàng / kết hợp.
- Bạn cần biết số lượng và vị trí của các cột có liên quan theo thứ tự mặc định.
4. VALUES
biểu thức có bị phân hủy loại hàng
Thích 3. , nhưng với các hàng được phân tách theo cú pháp chuẩn:
UPDATE foo f
SET x = t.x
, y = t.y
FROM (
VALUES
(('(1,20,)'::foo).*) -- decomposed row of values
, (2, 50, NULL)
) t(pkid, x, y) -- arbitrary column names (I made them match)
WHERE f.pkid = t.pkid; -- eliminates 1st row with NULL values
Hoặc, với hàng giá trị NULL đứng đầu một lần nữa:
...
VALUES
((NULL::foo).*) -- row of NULL values
, (1, 20, NULL) -- uniform syntax for all
, (2, 50, NULL)
...
Ưu và nhược điểm như 3. , nhưng với cú pháp thông dụng hơn.
Và bạn cần viết chính tả tên cột (nếu cần).
5. VALUES
biểu thức với các loại được tìm nạp từ loại hàng
Giống như Unril đã nhận xét, chúng ta có thể kết hợp các đức tính của 2. và 4. để chỉ cung cấp một tập hợp con các cột:
UPDATE foo f
SET ( x, y)
= (t.x, t.y) -- short notation, see below
FROM (
VALUES
((NULL::foo).pkid, (NULL::foo).x, (NULL::foo).y) -- subset of columns
, (1, 20, NULL)
, (2, 50, NULL)
) t(pkid, x, y) -- arbitrary column names (I made them match)
WHERE f.pkid = t.pkid;
Ưu và nhược điểm như 4. , nhưng chúng tôi có thể làm việc với bất kỳ tập hợp con nào của các cột và không cần phải biết danh sách đầy đủ.
Đồng thời hiển thị cú pháp ngắn cho UPDATE
bản thân nó thuận tiện cho các trường hợp có nhiều cột. Có liên quan:
- Cập nhật hàng loạt tất cả các cột
4. và 5. là mục yêu thích của tôi.
db <> fiddle here - thể hiện tất cả