Ответ 1
С автономным выражением VALUES
PostgreSQL не знает, какие типы данных должны быть. С помощью простых числовых литералов система с удовольствием принимает соответствующие типы. Но с другим вводом (например, NULL
) вам нужно будет явно указать - как вы уже узнали.
Вы можете запросить pg_catalog
(быстрый, но специфичный для PostgreSQL) или information_schema
(медленный, но стандартный SQL), чтобы узнать и подготовить свой оператор к соответствующим типам.
Или вы можете использовать один из этих простых "трюков":
1. Выберите строку с LIMIT 0
, добавьте строки с UNION ALL
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
Первый подвыбор подзапроса:
(SELECT x, y, pkid FROM foo LIMIT 0)
получает имена и типы для столбцов, но LIMIT 0
не позволяет добавить фактическую строку. Последующие строки принудительно применяются к теперь четко определенному типу строк - и сразу же проверяются, если они соответствуют типу. Должно быть тонкое дополнительное улучшение по сравнению с вашей оригинальной формой.
Основное ограничение : с отдельными линиями SELECT
, Postgres сразу же вводит литералы ввода в "наилучшее усилие". Позже, когда он попытается применить к указанным типам первый SELECT
, для некоторых типов может быть слишком поздно, если не существует зарегистрированного присваивания между предполагаемым типом и целевым типом. Пример text
→ timestamp
.
Pro:
- Минимальные накладные расходы.
- Читаемость, простота и быстрота для нескольких строк.
- Вам нужно знать только соответствующие имена столбцов таблицы.
Con:
- Разрешение типа может быть неудачным для некоторых типов.
- 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.
...
Первая строка в выражении VALUES
представляет собой строку значений NULL
, которая определяет тип для всех последующих строк.
Pro:
- Быстрее, чем 1.
- Самый короткий синтаксис для таблиц со многими столбцами и только несколько релевантных.
- Вам нужно знать только соответствующие имена столбцов таблицы.
Con:
- Подробный синтаксис для нескольких строк
- Менее читаемый (ИМО).
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,)')
...
Добавленная строка исключается предложением WHERE
в вашем UPDATE
.
Для других целей вы можете исключить добавленную первую строку с OFFSET 1
в подзапрос.
Pro:
- Самый быстрый (по крайней мере, в моих тестах с несколькими строками и столбцами).
- Самый короткий синтаксис для нескольких строк или таблиц, где вам нужны все столбцы.
- Вам не нужно указывать столбцы таблицы - все столбцы автоматически имеют соответствующее имя.
Con:
- Не так хорошо известен синтаксис для выбора поля из записи/строки/составного типа.
- Вы должны знать номер и позицию соответствующих столбцов в порядке по умолчанию.
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;
Или снова с ведущей строкой значений NULL:
...
VALUES
((NULL::foo).*) -- row of NULL values
, (1,20,NULL) -- uniform syntax for all
, (2,50,NULL)
...
Плюсы и минусы похожи на 3., но с более распространенным синтаксисом.
И вам нужно указать имена столбцов (если они вам понадобятся).
Последний мой любимый.