Sütun türüne neden NULL atamam gerekiyor?


10

Benim için toplu güncellemeler yapmak için bazı kod üreten ve böyle görünen SQL üreten bir yardımcı var:

(Hem etkin hem de çekirdek alanlar türdedir 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;

Ancak:

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

Ben ::booleannull'lara ekleyerek işe alabilirim , ama bu sadece garip görünüyor, NULL neden tip olarak kabul edilir TEXT?

Ayrıca, NULL'lara hangi türün yayınlanması gerektiğini bilmesi için kodun biraz yeniden çalışmasını gerektireceği için yayınlamak biraz zor (sütunlar ve değerler listesi şu anda basit bir JSON nesneleri dizisinden otomatik olarak oluşturuluyor) .

Bu neden gereklidir ve NULL türünü bilmek için kod üretmeyi gerektirmeyen daha zarif bir çözüm var mı?

Alakalı ise, bunu yapmak için Node.JS üzerinden sequelize kullanıyorum , ancak aynı sonucu Postgres komut satırı istemcisinde alıyorum.

Yanıtlar:


16

Bu ilginç bir bulgu. Normalde, burada görebileceğiniz gibi, bir NULL varsayılan veri türüne sahip değildir:

SELECT pg_typeof(NULL);

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

Bir VALUEStablo resme geldiğinde bu değişir :

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

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

Bu davranış, kaynak kodunda https://doxygen.postgresql.org/parse__coerce_8c.html#l01373 adresinde açıklanmıştır :

 /*
  * 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.
  */

(Evet, mükemmel yorumlar sayesinde PostgreSQL kaynak kodunun anlaşılması nispeten kolaydır ve çoğu yerde bulunur.)

Ancak çıkış yolu şu olabilir. Diyelim ki her zaman VALUESbelirli bir tablonun tüm sütunlarıyla eşleşiyorsunuz (diğer durumlar için aşağıdaki ikinci nota bakın). Örneğin, küçük bir hile muhtemelen yardımcı olabilir:

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

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

Burada tablonun türüne dökülen satır ifadelerini kullanır ve sonra bunları bir tabloya geri çıkarırsınız.

Yukarıdaki dayanarak, senin UPDATEgibi görünebilir

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;

Notlar:

  • Daha iyi insan okunabilirliği için çift tırnak işaretlerini kaldırdım, ancak (sütun) adları oluştururken yardımcı oldukları gibi tutabilirsiniz.
  • yalnızca sütunların bir alt kümesine ihtiyacınız varsa, bu amaçla özel türler oluşturabilirsiniz . Onları yukarıdaki gibi kullanın (burada otomatik olarak tablo ile oluşturulan türü, ikincisinin satır yapısını tutarak).

Dbfiddle üzerinde çalışan her şeye bakın .


Teşekkürler, bu ilginç, ancak, benim için, yukarıdaki kod üretir Cannot cast type boolean to bigint in column 1(ilk alanlar ifadesi arasında :: hata noktaları)
ChrisJ

1
@ChristopherJ cevabı, çağrılan tablonun boole, boolean ve int / bigint türleriyle fields3 sütunu olduğunu varsayar (active, core, id). Tablonuzda daha fazla sütun veya farklı tür var mı veya sütunlar farklı sırayla mı tanımlanmış?
ypercubeᵀᴹ

Ah görüyorum, teşekkürler, evet daha fazla sütun var ve farklı sırada. Anladım teşekkür ederim
ChrisJ
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.