Warum muss ich NULL in einen Spaltentyp umwandeln?


10

Ich habe einen Helfer, der Code generiert, um Massenaktualisierungen für mich durchzuführen, und SQL generiert, das so aussieht:

(Sowohl das aktive als auch das Kernfeld sind vom Typ 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;

Es schlägt jedoch fehl mit:

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

Ich kann es durch Hinzufügen ::booleanzu den Nullen zum Laufen bringen, aber das scheint nur seltsam, warum wird NULL als Typ betrachtet TEXT?

Das Casting ist auch etwas schwierig, da es eine ziemliche Überarbeitung des Codes erfordern würde, um zu wissen, in welchen Typ NULLs umgewandelt werden sollen (die Liste der Spalten und Werte wird derzeit automatisch aus einem einfachen Array von JSON-Objekten generiert). .

Warum ist dies notwendig und gibt es eine elegantere Lösung, bei der der generierende Code nicht erforderlich ist, um den Typ der NULL-Werte zu kennen?

Wenn es relevant ist, verwende ich dazu Sequelize über Node.JS, erhalte aber auch das gleiche Ergebnis im Postgres-Befehlszeilenclient.

Antworten:


16

Dies ist ein interessanter Befund. Normalerweise hat ein NULL keinen angenommenen Datentyp, wie Sie hier sehen können:

SELECT pg_typeof(NULL);

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

Dies ändert sich, wenn eine VALUESTabelle ins Bild kommt:

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

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

Dieses Verhalten wird im Quellcode unter https://doxygen.postgresql.org/parse__coerce_8c.html#l01373 beschrieben :

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

(Ja, PostgreSQL-Quellcode ist dank hervorragender Kommentare an den meisten Orten relativ einfach zu verstehen.)

Der Ausweg könnte jedoch der folgende sein. Angenommen, Sie generieren immer VALUESeine Übereinstimmung mit allen Spalten einer bestimmten Tabelle (siehe den zweiten Hinweis unten für andere Fälle). Aus Ihrem Beispiel könnte möglicherweise ein kleiner Trick helfen:

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

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

Hier verwenden Sie Reihe Ausdrücke auf den Tisch des Typs gegossen, und sie dann wieder an einen Tisch zu extrahieren.

Basierend auf dem oben Gesagten UPDATEkönnten Sie so aussehen

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;

Anmerkungen:

  • Ich habe die doppelten Anführungszeichen entfernt, um die Lesbarkeit zu verbessern. Sie können sie jedoch beibehalten, da sie beim Generieren von (Spalten-) Namen hilfreich sind.
  • Wenn Sie nur eine Teilmenge der Spalten benötigen, können Sie zu diesem Zweck benutzerdefinierte Typen erstellen . Verwenden Sie sie auf die gleiche Weise wie oben (wobei ich den automatisch mit der Tabelle erstellten Typ verwende, der die Zeilenstruktur der letzteren enthält).

Schau dir die ganze Sache an, die an dbfiddle arbeitet .


Danke, das ist interessant, aber für mich erzeugt der obige Code Cannot cast type boolean to bigint in column 1(die Fehlerpunkte bei :: zwischen den ersten Feldern)
ChristopherJ

1
@ChristopherJ Die Antwort geht davon aus, dass die aufgerufene Tabelle fields3 Spalten (active, core, id)mit den Typen Boolean, Boolean und Int / Bigint enthält. Hat Ihre Tabelle mehr Spalten oder verschiedene Typen oder sind die Spalten in unterschiedlicher Reihenfolge definiert?
Ypercubeᵀᴹ

Ah, ich verstehe, danke, ja, es gibt mehr Spalten und in unterschiedlicher Reihenfolge. Verstanden, danke
ChristopherJ
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.