Med en fristående VALUES
expression PostgreSQL har ingen aning om vilka datatyper som ska vara. Med enkla numeriska bokstaver antar systemet gärna matchningstyper. Men med annan input (som NULL
) du skulle behöva casta uttryckligen - som du redan har fått reda på.
Du kan fråga pg_catalog
(snabb, men PostgreSQL-specifik) eller information_schema
(långsam, men standard SQL) för att ta reda på och förbereda ditt uttalande med lämpliga typer.
Eller så kan du använda ett av dessa enkla "tricks" (jag sparade det bästa till sista ):
0. Välj rad med LIMIT 0
, lägg till rader med 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;
Det första undervalet av underfrågan:
(SELECT x, y, pkid FROM foo LIMIT 0)
får namn och typer för kolumnerna, men LIMIT 0
hindrar den från att lägga till en faktisk rad. Efterföljande rader tvingas till den nu väldefinierade radtypen - och kontrolleras omedelbart om de matchar typen. Bör vara en subtil ytterligare förbättring jämfört med din ursprungliga form.
Medan du tillhandahåller värden för alla kolumner i tabellen denna korta syntax kan användas för den första raden:
(TABLE foo LIMIT 0)
Stor begränsning :Postgres kastar ingångsliteralerna för de fristående VALUES
uttryck till en "bästa ansträngning"-typ omedelbart. När den senare försöker casta till de givna typerna av den första SELECT
, kan det redan vara för sent för vissa typer om det inte finns någon registrerad tilldelning mellan den antagna typen och måltypen. Exempel:text
-> timestamp
eller text
-> json
.
Proffs:
- Minsta omkostnader.
- Läsbart, enkelt och snabbt.
- Du behöver bara känna till relevanta kolumnnamn för tabellen.
Kon:
- Typupplösning kan misslyckas för vissa typer.
1. Välj rad med LIMIT 0
, lägg till rader med 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;
Proffs:
- Gilla 0. , men undviker felaktig typupplösning.
Kon:
UNION ALL SELECT
är långsammare änVALUES
uttryck för långa listor med rader, som du hittade i ditt test.- Verbosed syntax per rad.
2. VALUES
uttryck med typ per kolumn
...
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.
...
I motsats till 0. detta undviker för tidig typupplösning.
Den första raden i VALUES
uttryck är en rad med NULL
värden som definierar typen för alla efterföljande rader. Denna ledande brusrad filtreras av WHERE f.pkid = t.pkid
senare, så den ser aldrig dagens ljus. För andra ändamål kan du eliminera den första raden som lagts till med OFFSET 1
i en underfråga.
Proffs:
- Vanligtvis snabbare än 1. (eller till och med 0. )
- Kort syntax för tabeller med många kolumner och endast få är relevanta.
- Du behöver bara känna till relevanta kolumnnamn för tabellen.
Kon:
- Verbosed syntax för endast några rader
- Mindre läsbar (IMO).
3. VALUES
uttryck med radtyp
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;
Du känner uppenbarligen till bordets namn. Om du också känner till antalet kolumner och deras ordning kan du arbeta med detta.
För varje tabell i PostgreSQL registreras en radtyp automatiskt. Om du matchar antalet kolumner i ditt uttryck kan du casta till tabellens radtyp ('(1,50,)'::foo
) och tilldelar därigenom kolumntyper implicit. Sätt ingenting bakom ett kommatecken för att ange en NULL
värde. Lägg till ett kommatecken för varje irrelevant avslutande kolumn.
I nästa steg kan du komma åt enskilda kolumner med den visade syntaxen. Mer om Fältval i manualen.
Eller så kan du lägga till en rad med NULL-värden och använd enhetlig syntax för faktiska data:
...
VALUES
((NULL::foo)) -- row of NULL values
, ('(1,20,)') -- uniform ROW value syntax for all
, ('(2,50,)')
...
Proffs:
- Snabbast (åtminstone i mina tester med få rader och kolumner).
- Kortaste syntax för några rader eller tabeller där du behöver alla kolumner.
- Du behöver inte stava ut kolumner i tabellen - alla kolumner har automatiskt det matchande namnet.
Kon:
- Inte så välkänd syntax för fältval från post / rad / sammansatt typ.
- Du måste veta antalet och positionen för relevanta kolumner i standardordning.
4. VALUES
uttryck med nedbrutet radtyp
Gilla 3. , men med nedbrutna rader i standardsyntax:
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
Eller, med en ledande rad med NULL-värden igen:
...
VALUES
((NULL::foo).*) -- row of NULL values
, (1, 20, NULL) -- uniform syntax for all
, (2, 50, NULL)
...
För- och nackdelar som 3. , men med mer allmänt känd syntax.
Och du måste stava kolumnnamn (om du behöver dem).
5. VALUES
uttryck med typer hämtade från radtyp
Som Unril kommenterade kan vi kombinera fördelarna med 2. och 4. för att endast tillhandahålla en undergrupp av kolumner:
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;
För- och nackdelar som 4. , men vi kan arbeta med vilken delmängd som helst av kolumner och behöver inte känna till hela listan.
Visar också kort syntax för UPDATE
i sig som är bekvämt för fall med många kolumner. Relaterat:
- Massuppdatering av alla kolumner
4. och 5. är mina favoriter.
db<>spela här - demonstrera alla