Med en selvstændig VALUES
udtryk PostgreSQL har ingen idé om, hvad datatyperne skal være. Med simple numeriske bogstaver antager systemet gerne matchende typer. Men med andre input (som NULL
) skal du caste eksplicit - som du allerede har fundet ud af.
Du kan forespørge pg_catalog
(hurtigt, men PostgreSQL-specifikt) eller information_schema
(langsom, men standard SQL) for at finde ud af og forberede din erklæring med passende typer.
Eller du kan bruge et af disse simple "tricks" (jeg gemte det bedste til sidst ):
0. Vælg række med LIMIT 0
, tilføj rækker 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ørste undervalg af underforespørgslen:
(SELECT x, y, pkid FROM foo LIMIT 0)
får navne og typer til kolonnerne, men LIMIT 0
forhindrer den i at tilføje en faktisk række. Efterfølgende rækker tvinges til den nu veldefinerede rækketype - og tjekkes med det samme, om de matcher typen. Bør være en subtil yderligere forbedring i forhold til din originale formular.
Mens du angiver værdier for alle kolonner i tabellen kan denne korte syntaks bruges til den første række:
(TABLE foo LIMIT 0)
Større begrænsning :Postgres kaster inputliteralerne af de fritstående VALUES
udtryk til en "best-effort"-type med det samme. Når den senere forsøger at caste til de givne typer af den første SELECT
, kan det allerede være for sent for nogle typer, hvis der ikke er en registreret tildeling cast mellem den antagne type og måltypen. Eksempler:text
-> timestamp
eller text
-> json
.
Pro:
- Minimum overhead.
- Læsbar, enkel og hurtig.
- Du behøver kun at kende relevante kolonnenavne for tabellen.
Con:
- Typeopløsning kan mislykkes for nogle typer.
1. Vælg række med LIMIT 0
, tilføj rækker 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;
Pro:
- Like 0. , men undgår fejlagtig typeopløsning.
Con:
UNION ALL SELECT
er langsommere endVALUES
udtryk for lange lister med rækker, som du fandt i din test.- Overboed syntaks pr. række.
2. VALUES
udtryk med per-kolonne type
...
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 modsætning til 0. dette undgår for tidlig typeopløsning.
Den første række i VALUES
udtryk er en række af NULL
værdier, som definerer typen for alle efterfølgende rækker. Denne førende støjrække er filtreret af WHERE f.pkid = t.pkid
senere, så den aldrig ser dagens lys. Til andre formål kan du fjerne den tilføjede første række med OFFSET 1
i en underforespørgsel.
Pro:
- Typisk hurtigere end 1. (eller endda 0. )
- Kort syntaks for tabeller med mange kolonner, og kun få er relevante.
- Du behøver kun at kende relevante kolonnenavne for tabellen.
Con:
- Overboed syntaks for kun få rækker
- Mindre læsbar (IMO).
3. VALUES
udtryk med rækketype
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 kender tydeligvis bordets navn. Hvis du også kender antallet af kolonner og deres rækkefølge, kan du arbejde med dette.
For hver tabel i PostgreSQL registreres en rækketype automatisk. Hvis du matcher antallet af kolonner i dit udtryk, kan du caste til rækketypen i tabellen ('(1,50,)'::foo
) og derved tildele kolonnetyper implicit. Sæt intet bag et komma for at indtaste en NULL
værdi. Tilføj et komma for hver irrelevant efterfølgende kolonne.
I næste trin kan du få adgang til individuelle kolonner med den demonstrerede syntaks. Mere om Feltvalg i manualen.
Eller du kan tilføje en række med NULL-værdier og brug ensartet syntaks til faktiske data:
...
VALUES
((NULL::foo)) -- row of NULL values
, ('(1,20,)') -- uniform ROW value syntax for all
, ('(2,50,)')
...
Pro:
- Hurtigste (i hvert fald i mine test med få rækker og kolonner).
- Korteste syntaks for få rækker eller tabeller, hvor du har brug for alle kolonner.
- Du behøver ikke at stave kolonner i tabellen - alle kolonner har automatisk det matchende navn.
Con:
- Ikke så velkendt syntaks til feltvalg fra post / række / sammensat type.
- Du skal kende antallet og placeringen af relevante kolonner i standardrækkefølge.
4. VALUES
udtryk med nedbrudt rækketype
Ligesom 3. , men med dekomponerede rækker i standardsyntaks:
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 førende række af NULL-værdier igen:
...
VALUES
((NULL::foo).*) -- row of NULL values
, (1, 20, NULL) -- uniform syntax for all
, (2, 50, NULL)
...
Fordele og ulemper som 3. , men med mere almindeligt kendt syntaks.
Og du skal stave kolonnenavne (hvis du har brug for dem).
5. VALUES
udtryk med typer hentet fra rækketype
Ligesom Unril kommenterede, kan vi kombinere fordelene ved 2. og 4. for kun at angive et undersæt af kolonner:
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;
Fordele og ulemper som 4. , men vi kan arbejde med enhver undergruppe af kolonner og behøver ikke at kende hele listen.
Viser også kort syntaks for UPDATE
i sig selv, hvilket er praktisk til sager med mange kolonner. Relateret:
- Masseopdatering af alle kolonner
4. og 5. er mine favoritter.
db<>spil her - demonstrerer alle