Tại sao tôi cần truyền NULL sang loại cột?


10

Tôi đã có một người trợ giúp tạo ra một số mã để thực hiện cập nhật hàng loạt cho tôi và tạo SQL giống như thế này:

(Cả hai trường hoạt động và cốt lõi đều thuộc loại boolean)

UPDATE fields as t set "active" = new_values."active","core" = new_values."core"
FROM (values 
(true,NULL,3419),
(false,NULL,3420)
) as new_values("active","core","id") WHERE new_values.id = t.id;

Tuy nhiên, nó thất bại với:

ERROR: column "core" is of type boolean but expression is of type text

Tôi có thể làm cho nó hoạt động bằng cách thêm ::booleanvào null, nhưng điều đó có vẻ kỳ lạ, tại sao NULL được coi là loại TEXT?

Ngoài ra, có một chút khó khăn khi sử dụng vì nó sẽ yêu cầu khá nhiều mã phải làm lại để biết nên sử dụng kiểu NULL nào (danh sách các cột và giá trị hiện đang được tự động tạo từ một mảng các đối tượng JSON đơn giản) .

Tại sao điều này lại cần thiết và có một giải pháp thanh lịch hơn không yêu cầu mã tạo để biết loại NULL?

Nếu nó có liên quan, tôi đang sử dụng sequelize qua Node.js để làm điều này, nhưng cũng đang nhận được kết quả tương tự trong dòng lệnh client Postgres.

Câu trả lời:


16

Đây là một phát hiện thú vị. Thông thường, NULL không có kiểu dữ liệu giả định, như bạn có thể thấy ở đây:

SELECT pg_typeof(NULL);

 pg_typeof 
───────────
 unknown

Điều này thay đổi khi một VALUESbảng đi vào hình ảnh:

SELECT pg_typeof(core) FROM (
    VALUES (NULL)
) new_values (core);

 pg_typeof 
───────────
 text

Hành vi này được mô tả trong mã nguồn tại https://doxygen.postgresql.org/parse__coerce_8c.html#l01373 :

 /*
  * If all the inputs were UNKNOWN type --- ie, unknown-type literals ---
  * then resolve as type TEXT.  This situation comes up with constructs
  * like SELECT (CASE WHEN foo THEN 'bar' ELSE 'baz' END); SELECT 'foo'
  * UNION SELECT 'bar'; It might seem desirable to leave the construct's
  * output type as UNKNOWN, but that really doesn't work, because we'd
  * probably end up needing a runtime coercion from UNKNOWN to something
  * else, and we usually won't have it.  We need to coerce the unknown
  * literals while they are still literals, so a decision has to be made
  * now.
  */

(Có, mã nguồn PostgreSQL tương đối dễ hiểu và hầu hết các nơi, nhờ vào các bình luận tuyệt vời.)

Cách ra, tuy nhiên, có thể là sau đây. Giả sử bạn luôn tạo ra VALUESkhớp với tất cả các cột của một bảng nhất định (xem ghi chú thứ hai bên dưới để biết các trường hợp khác). Từ ví dụ của bạn, một mẹo nhỏ có thể có thể giúp:

SELECT (x).* FROM (VALUES ((TRUE, NULL, 1234)::fields)) t(x);

 active  core   id  
────────┼──────┼──────
 t             1234

Ở đây bạn sử dụng các biểu thức hàng được đúc theo kiểu của bảng và sau đó trích xuất chúng trở lại bảng.

Dựa trên những điều trên, bạn UPDATEcó thể trông giống như

UPDATE fields AS t set active = (x).active, core = (x).core
FROM ( VALUES
           ((true, NULL, 3419)::fields),
           ((false, NULL, 3420)::fields)
     ) AS new_values(x) WHERE (x).id = t.id;

Ghi chú:

  • Tôi đã xóa các trích dẫn kép để dễ đọc hơn cho con người, nhưng bạn có thể giữ chúng khi chúng giúp khi tạo tên (cột).
  • nếu bạn chỉ cần một tập hợp con của các cột, bạn có thể tạo các loại tùy chỉnh cho mục đích này. Sử dụng chúng theo cách tương tự như bạn ở trên (nơi tôi sử dụng loại được tạo tự động với bảng, giữ cấu trúc hàng của cái sau).

Nhìn vào toàn bộ hoạt động trên dbfiddle .


Cảm ơn, điều này thật thú vị, tuy nhiên, đối với tôi, đoạn mã trên tạo ra Cannot cast type boolean to bigint in column 1(các điểm lỗi tại :: giữa câu lệnh trường đầu tiên)
ChristopherJ

1
@ChristopherJ câu trả lời giả định rằng bảng được gọi fieldscó 3 cột, (active, core, id)với các kiểu boolean, boolean và int / bigint. Bảng của bạn có nhiều cột hoặc các loại khác nhau hay các cột được xác định theo thứ tự khác nhau?
ypercubeᵀᴹ

Ah tôi hiểu, cảm ơn, vâng có nhiều cột hơn và theo thứ tự khác nhau. Cảm ơn bạn
ChristopherJ
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.