sql >> Databáze >  >> RDS >> PostgreSQL

Odesílání typu NULL při aktualizaci více řádků

Se samostatným VALUES výraz PostgreSQL nemá ponětí, jaké by měly být datové typy. S jednoduchými číselnými literály systém rád předpokládá odpovídající typy. Ale s jiným vstupem (jako NULL ) budete muset vrhnout explicitně - jak jste již zjistili.

Můžete se zeptat na pg_catalog (rychlé, ale specifické pro PostgreSQL) nebo information_schema (pomalé, ale standardní SQL), abyste zjistili a připravili svůj příkaz s vhodnými typy.

Nebo můžete použít jeden z těchto jednoduchých „triků“ (to nejlepší jsem si nechal naposled ):

0. Vyberte řádek s LIMIT 0 , připojte k řádkům řetězec UNION ALL VALUES

UPDATE foo f
SET    x = t.x
     , y = t.y
FROM  (
  (SELECT pkid, x, y FROM foo LIMIT 0) -- parenthesis needed with LIMIT
   UNION ALL
   VALUES
      (1, 20, NULL)  -- no type casts here
    , (2, 50, NULL)
   ) t               -- column names and types are already defined
WHERE  f.pkid = t.pkid;

První dílčí výběr poddotazu:

(SELECT x, y, pkid  FROM foo LIMIT 0)

získá názvy a typy sloupců, ale LIMIT 0 zabrání přidání skutečného řádku. Následující řádky jsou vynuceny na nyní dobře definovaný typ řádku - a okamžitě kontrolovány, zda odpovídají typu. Mělo by jít o další jemné vylepšení oproti původní podobě.

Při zadávání hodnot pro vše sloupce tabulky tuto krátkou syntaxi lze použít pro první řádek:

(TABLE foo LIMIT 0)

Hlavní omezení :Postgres přenáší vstupní literály volně stojícího VALUES výraz okamžitě na typ "nejlepší úsilí". Když se později pokusí přetypovat na dané typy prvního SELECT , může být pro některé typy již příliš pozdě, pokud mezi předpokládaným typem a cílovým typem není žádné registrované přiřazení. Příklady:text -> timestamp nebo text -> json .

Pro:

  • Minimální režie.
  • Čtivé, jednoduché a rychlé.
  • Potřebujete znát pouze relevantní názvy sloupců tabulky.

Con:

  • U některých typů může rozlišení typu selhat.

1. Vyberte řádek s LIMIT 0 , připojte řádky pomocí UNION ALL SELECT

UPDATE foo f
SET    x = t.x
     , y = t.y
FROM  (
  (SELECT pkid, x, y FROM foo LIMIT 0) -- parenthesis needed with LIMIT
   UNION ALL SELECT 1, 20, NULL
   UNION ALL SELECT 2, 50, NULL
   ) t               -- column names and types are already defined
WHERE  f.pkid = t.pkid;

Pro:

  • Jako 0. , ale zabrání selhání rozlišení typu.

Con:

  • UNION ALL SELECT je pomalejší než VALUES výraz pro dlouhé seznamy řádků, jak jste našli ve svém testu.
  • Podrobná syntaxe na řádek.

2. VALUES výraz s typem na sloupec

...
FROM  (
   VALUES 
     ((SELECT pkid FROM foo LIMIT 0)
    , (SELECT x    FROM foo LIMIT 0)
    , (SELECT y    FROM foo LIMIT 0))  -- get type for each col individually
   , (1, 20, NULL)
   , (2, 50, NULL)
   ) t (pkid, x, y)  -- columns names not defined yet, only types.
...

Na rozdíl od 0. tím se zabrání předčasnému rozlišení typu.

První řádek v VALUES výraz je řádek NULL hodnoty, které definují typ pro všechny následující řádky. Tento úvodní řádek šumu je filtrován podle WHERE f.pkid = t.pkid později, takže nikdy nespatří světlo světa. Pro jiné účely můžete odstranit přidaný první řádek pomocí OFFSET 1 v dílčím dotazu.

Pro:

  • Obvykle rychlejší než 1. (nebo dokonce 0. )
  • Krátká syntaxe pro tabulky s mnoha sloupci, z nichž je relevantních pouze několik.
  • Potřebujete znát pouze relevantní názvy sloupců tabulky.

Con:

  • Úplná syntaxe pouze pro několik řádků
  • Méně čitelné (IMO).

3. VALUES výraz s typem řádku

UPDATE foo f
SET x = (t.r).x         -- parenthesis needed to make syntax unambiguous
  , y = (t.r).y
FROM (
   VALUES
      ('(1,20,)'::foo)  -- columns need to be in default order of table
     ,('(2,50,)')       -- nothing after the last comma for NULL
   ) t (r)              -- column name for row type
WHERE  f.pkid = (t.r).pkid;

Jméno stolu evidentně znáte. Pokud znáte také počet sloupců a jejich pořadí, můžete s tím pracovat.

Pro každou tabulku v PostgreSQL je automaticky registrován typ řádku. Pokud odpovídáte počtu sloupců ve výrazu, můžete přetypovat na typ řádku tabulky ('(1,50,)'::foo ), čímž se implicitně přiřadí typy sloupců. Chcete-li zadat NULL, za čárku nedávejte nic hodnota. Přidejte čárku pro každý irelevantní koncový sloupec.
V dalším kroku můžete přistupovat k jednotlivým sloupcům s demonstrovanou syntaxí. Další informace o Výběru polí v návodu.

Nebo můžete přidat řádek hodnot NULL a pro skutečná data použijte jednotnou syntaxi:

...
  VALUES
      ((NULL::foo))  -- row of NULL values
    , ('(1,20,)')    -- uniform ROW value syntax for all
    , ('(2,50,)')
...

Pro:

  • Nejrychlejší (alespoň v mých testech s několika řádky a sloupci).
  • Nejkratší syntaxe pro několik řádků nebo tabulek, kde potřebujete všechny sloupce.
  • Sloupce tabulky nemusíte hláskovat – všechny sloupce mají automaticky odpovídající název.

Con:

  • Ne příliš známá syntaxe pro výběr polí ze záznamu / řádku / složeného typu.
  • Musíte znát počet a pozici příslušných sloupců ve výchozím pořadí.

4. VALUES výraz s rozloženým typ řádku

Jako 3. , ale s rozloženými řádky ve standardní syntaxi:

UPDATE foo f
SET    x = t.x
     , y = t.y
FROM (
   VALUES
      (('(1,20,)'::foo).*)  -- decomposed row of values
    , (2, 50, NULL)
   ) t(pkid, x, y)  -- arbitrary column names (I made them match)
WHERE  f.pkid = t.pkid;     -- eliminates 1st row with NULL values

Nebo opět s úvodní řadou hodnot NULL:

...
   VALUES
      ((NULL::foo).*)  -- row of NULL values
    , (1, 20, NULL)    -- uniform syntax for all
    , (2, 50, NULL)
...

Pro a proti jako 3. , ale s běžněji známou syntaxí.
A musíte vyhláskovat názvy sloupců (pokud je potřebujete).

5. VALUES výraz s typy načtenými z typu řádku

Jak uvedl Unril, můžeme kombinovat přednosti 2. a 4. poskytnout pouze podmnožinu sloupců:

UPDATE foo f
SET   (  x,   y)
    = (t.x, t.y)  -- short notation, see below
FROM (
   VALUES
      ((NULL::foo).pkid, (NULL::foo).x, (NULL::foo).y)  -- subset of columns
    , (1, 20, NULL)
    , (2, 50, NULL)
   ) t(pkid, x, y)       -- arbitrary column names (I made them match)
WHERE  f.pkid = t.pkid;

Pro a proti jako 4. , ale můžeme pracovat s jakoukoli podmnožinou sloupců a nemusíme znát úplný seznam.

Také se zobrazuje krátká syntaxe pro UPDATE to je vhodné pro případy s mnoha sloupci. Související:

  • Hromadná aktualizace všech sloupců

4. a 5. jsou mé oblíbené.

db<>zde hrajte - předvedení všech



  1. Jak vytvořit slabý referenční kurzor PL/SQL v databázi Oracle

  2. Jak zálohovat funkce pouze v Postgresu

  3. Pochopení rozdílů mezi Table a Transaction API

  4. Export databáze MySQL nebo MariaDB