J'ai un assistant qui génère du code pour faire des mises à jour en masse pour moi et génère du SQL qui ressemble à ceci:
(Les champs actifs et principaux sont de type 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;
Cependant, il échoue avec:
ERROR: column "core" is of type boolean but expression is of type text
Je peux le faire fonctionner en ajoutant ::boolean
aux valeurs nulles, mais cela semble étrange, pourquoi NULL est-il considéré de type TEXT
?
De plus, il est un peu difficile à convertir car cela nécessiterait un peu de remaniement du code pour qu'il sache dans quel type il devrait convertir des valeurs NULL (la liste des colonnes et des valeurs est actuellement générée automatiquement à partir d'un simple tableau d'objets JSON) .
Pourquoi est-ce nécessaire et existe-t-il une solution plus élégante qui ne nécessite pas le code de génération pour connaître le type de NULL?
Si cela est pertinent, j'utilise sequelize sur Node.JS pour ce faire, mais j'obtiens également le même résultat dans le client de ligne de commande Postgres.
C'est une découverte intéressante. Normalement, un NULL n'a pas de type de données supposé, comme vous pouvez le voir ici:
SELECT pg_typeof(NULL);
pg_typeof
───────────
unknown
Cela change quand une table VALUES
apparaît dans l'image:
SELECT pg_typeof(core) FROM (
VALUES (NULL)
) new_values (core);
pg_typeof
───────────
text
Ce comportement est décrit dans le code source à https://doxygen.postgresql.org/parse__coerce_8c.html#l0137 :
/*
* 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.
*/
(Oui, le code source de PostgreSQL est relativement facile à comprendre et la plupart des endroits, grâce à d'excellents commentaires.)
Cependant, la sortie pourrait être la suivante. Supposons que vous générez toujours VALUES
qui correspondent à toutes les colonnes d'une table donnée (voir la deuxième note ci-dessous pour les autres cas). D'après votre exemple, une petite astuce pourrait éventuellement aider:
SELECT (x).* FROM (VALUES ((TRUE, NULL, 1234)::fields)) t(x);
active │ core │ id
────────┼──────┼──────
t │ │ 1234
Ici, vous utilisez expressions de ligne transtypées en type de table, puis les extrayez dans une table.
Sur la base de ce qui précède, votre UPDATE
pourrait ressembler à
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;
Regardez le tout travailler sur dbfiddle .