Mengapa saya perlu memberikan NULL ke jenis kolom?


10

Saya punya pembantu yang menghasilkan beberapa kode untuk melakukan pembaruan massal untuk saya dan menghasilkan SQL yang terlihat seperti ini:

(Kedua bidang aktif dan inti adalah tipe 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;

Namun gagal dengan:

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

Saya bisa membuatnya bekerja dengan menambahkan ::booleanke nol, tapi itu hanya terasa aneh, mengapa NULL dianggap tipe TEXT?

Juga agak sulit untuk dilemparkan karena akan memerlukan sedikit pengerjaan ulang kode untuk mengetahui jenis apa yang harus dilemparkan NULLs (daftar kolom dan nilai saat ini sedang diautogasi secara otomatis dari array sederhana objek JSON) .

Mengapa ini perlu dan apakah ada solusi yang lebih elegan yang tidak memerlukan kode pembangkit untuk mengetahui jenis NULLs?

Jika itu relevan, saya menggunakan sekuel Node.JS untuk melakukan ini, tetapi saya juga mendapatkan hasil yang sama pada klien baris perintah Postgres.

Jawaban:


16

Ini adalah temuan yang menarik. Biasanya, NULL tidak memiliki tipe data yang diasumsikan, seperti yang Anda lihat di sini:

SELECT pg_typeof(NULL);

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

Ini berubah ketika sebuah VALUEStabel muncul dalam gambar:

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

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

Perilaku ini dijelaskan dalam kode sumber di 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.
  */

(Ya, kode sumber PostgreSQL relatif mudah dipahami dan sebagian besar tempat, berkat komentar yang sangat baik.)

Namun, jalan keluarnya mungkin sebagai berikut. Katakanlah Anda selalu membuat VALUESyang cocok dengan semua kolom dari tabel yang diberikan (lihat catatan kedua di bawah untuk kasus lain). Dari contoh Anda, trik kecil mungkin dapat membantu:

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

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

Di sini Anda menggunakan ekspresi baris yang dibuat untuk tipe tabel, dan kemudian mengekstraksinya kembali ke tabel.

Berdasarkan hal di atas, Anda UPDATEbisa terlihat seperti

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;

Catatan:

  • Saya menghapus tanda kutip ganda untuk keterbacaan manusia yang lebih baik, tetapi Anda dapat menyimpannya karena membantu ketika menghasilkan nama (kolom).
  • jika Anda hanya membutuhkan sebagian dari kolom, Anda dapat membuat tipe khusus untuk tujuan ini. Gunakan mereka dengan cara yang sama seperti yang Anda lakukan di atas (di mana saya menggunakan jenis yang secara otomatis dibuat dengan tabel, memegang struktur baris yang terakhir).

Lihatlah semuanya bekerja pada dbfiddle .


Terima kasih, ini menarik, namun, bagi saya, kode di atas menghasilkan Cannot cast type boolean to bigint in column 1(titik kesalahan di :: antara pernyataan bidang pertama)
ChristopherJ

1
@ChristopherJ jawabannya mengasumsikan bahwa tabel yang dipanggil fieldsmemiliki 3 kolom, (active, core, id)dengan tipe boolean, boolean dan int / bigint. Apakah tabel Anda memiliki lebih banyak kolom atau jenis yang berbeda atau apakah kolom tersebut ditentukan dalam urutan yang berbeda?
ypercubeᵀᴹ

Ah saya mengerti, terima kasih, ya ada lebih banyak kolom dan dalam urutan yang berbeda. Terima kasih
ChristopherJ
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.