Със самостоятелен VALUES
израз PostgreSQL няма представа какви трябва да бъдат типовете данни. С прости числови литерали системата е щастлива да приеме съвпадащи типове. Но с друг вход (като NULL
) ще трябва да го изпълните изрично - както вече разбрахте.
Можете да направите заявка за pg_catalog
(бърз, но специфичен за PostgreSQL) или information_schema
(бавен, но стандартен SQL), за да разберете и подготвите изявлението си с подходящи типове.
Или можете да използвате един от тези прости „трикове“ (запазих най-доброто за последно ):
0. Изберете ред с LIMIT 0
, добавете редове с 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;
Първият под-селекция на подзаявката:
(SELECT x, y, pkid FROM foo LIMIT 0)
получава имена и типове за колоните, но LIMIT 0
предотвратява добавянето на действителен ред. Следващите редове се принуждават към вече добре дефинирания тип ред - и незабавно се проверяват дали съвпадат с типа. Би трябвало да е фино допълнително подобрение спрямо оригиналния ви формуляр.
Докато предоставя стойности за всички колони на таблицата този кратък синтаксис може да се използва за първия ред:
(TABLE foo LIMIT 0)
Основно ограничение :Postgres предава входните литерали на свободно стоящите VALUES
израз към тип "най-добри усилия" незабавно. Когато по-късно се опита да прехвърли към дадените типове на първия SELECT
, може вече да е твърде късно за някои типове, ако няма регистрирано прехвърляне на присвояване между предполагаемия тип и целевия тип. Примери:text
-> timestamp
или text
-> json
.
Професионално:
- Минимални режийни разходи.
- Четим, лесен и бърз.
- Трябва да знаете само съответните имена на колони в таблицата.
Против:
- Разделителната способност на типа може да е неуспешна за някои типове.
1. Изберете ред с LIMIT 0
, добавете редове с 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;
Професионално:
- Като 0. , но избягва неуспешното разделяне на типа.
Против:
UNION ALL SELECT
е по-бавно отVALUES
израз за дълги списъци с редове, както открихте във вашия тест.- Подробен синтаксис на ред.
2. VALUES
израз с тип за колона
...
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.
...
Противно на 0. това избягва преждевременното разделяне на типа.
Първият ред в VALUES
изразът е ред от NULL
стойности, които определят типа за всички следващи редове. Този водещ ред с шум се филтрира от WHERE f.pkid = t.pkid
по-късно, така че никога не вижда бял свят. За други цели можете да премахнете добавения първи ред с OFFSET 1
в подзаявка.
Професионално:
- Обикновено по-бързо от 1. (или дори 0. )
- Кратък синтаксис за таблици с много колони и само няколко са подходящи.
- Трябва да знаете само съответните имена на колони в таблицата.
Против:
- Подробен синтаксис само за няколко реда
- По-малко четими (IMO).
3. VALUES
израз с тип ред
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;
Явно знаете името на таблицата. Ако знаете и броя на колоните и реда им, можете да работите с това.
За всяка таблица в PostgreSQL автоматично се регистрира тип ред. Ако съвпадате с броя на колоните във вашия израз, можете да прехвърляте към типа ред на таблицата ('(1,50,)'::foo
), като по този начин имплицитно присвоява типове колони. Не поставяйте нищо зад запетая, за да въведете NULL
стойност. Добавете запетая за всяка неуместна колона в края.
В следващата стъпка можете да получите достъп до отделни колони с демонстрирания синтаксис. Повече за Избор на поле в ръководството.
Или можете да добавите ред с NULL стойности и използвайте единен синтаксис за действителни данни:
...
VALUES
((NULL::foo)) -- row of NULL values
, ('(1,20,)') -- uniform ROW value syntax for all
, ('(2,50,)')
...
Професионално:
- Най-бърз (поне в моите тестове с няколко реда и колони).
- Най-краткият синтаксис за няколко реда или таблици, където имате нужда от всички колони.
- Не е нужно да изписвате колони от таблицата – всички колони автоматично имат съответстващото име.
Против:
- Не толкова добре познат синтаксис за избор на поле от запис/ред/композитен тип.
- Трябва да знаете броя и позицията на съответните колони в ред по подразбиране.
4. VALUES
израз с разложен тип ред
Като 3. , но с разложени редове в стандартен синтаксис:
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
Или отново с водещ ред от стойности NULL:
...
VALUES
((NULL::foo).*) -- row of NULL values
, (1, 20, NULL) -- uniform syntax for all
, (2, 50, NULL)
...
Плюсове и минуси като 3. , но с по-известен синтаксис.
И трябва да изпишете имената на колони (ако имате нужда от тях).
5. VALUES
израз с типове, извлечени от тип ред
Както коментира Unril, можем да комбинираме достойнствата на 2. и 4. за да предоставите само подмножество от колони:
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;
Плюсове и минуси като 4. , но можем да работим с всяко подмножество от колони и не е нужно да знаем пълния списък.
Също така показва кратък синтаксис за UPDATE
това е удобно за случаи с много колони. Свързано:
- Групова актуализация на всички колони
4. и 5. са моите любими.
db<>цигулка тук - демонстриране на всички