Подзапрос внутри вставки (INSERT)
У меня есть таблица с названием map_tags
:
map_id | map_license | map_desc
И другая таблица (widgets
), записи которой содержат внешнюю ссылку (один к одному) на запись в map_tags
:
widget_id | map_id | widget_name
Учитывая ограничение, что все значения map_license
уникальны (хотя не установлены в качестве ключей в map_tags
), я хотел бы выполнить вставку в таблицу widgets
, имея значение map_license
и widget_name
, всё это в одном SQL-запросе:
INSERT INTO
widgets w
(
map_id,
widget_name
)
VALUES (
(
SELECT
mt.map_id
FROM
map_tags mt
WHERE
// Это должно сработать и вернуть одну запись, так как map_license уникален
mt.map_license = '12345'
),
'Bupo'
)
Я полагаю, что двигаюсь в правильном направлении, но сразу понимаю, что этот SQL-запрос неверен для Postgres. Никто не подскажет, как правильно выполнить такую вставку в одном запросе?
3 ответ(ов)
Чтобы использовать вариант INSERT INTO SELECT
в PostgreSQL, вы можете следовать следующему синтаксису:
INSERT INTO table [ ( column [, ...] ) ]
{ DEFAULT VALUES | VALUES ( { expression | DEFAULT } [, ...] ) [, ...] | query }
[ RETURNING * | output_expression [ [ AS ] output_name ] [, ...] ]
Обратите внимание на опцию query в конце второй строки синтаксиса. Это позволяет вам вставлять данные в таблицу, извлекая их из другой таблицы, возможно с добавлением констант.
Вот пример, который демонстрирует, как это работает:
INSERT INTO
widgets
(
map_id,
widget_name
)
SELECT
mt.map_id,
'Bupo'
FROM
map_tags mt
WHERE
mt.map_license = '12345'
В этом примере мы вставляем значения в таблицу widgets
, выбирая map_id
из таблицы map_tags
, где map_license
равен '12345'. Вместе с этим мы добавляем константу 'Bupo' в качестве widget_name
. Таким образом, вы можете легко комбинировать выборку из одной таблицы и константы в одной команде вставки.
Вы можете использовать следующий SQL-запрос для выполнения вставки данных в таблицу widgets
. Запрос выбирает map_id
из таблицы map_tags
, где map_license
равно '12345'
, и добавляет новую запись с именем виджета 'Bupo'
для каждого соответствующего map_id
:
INSERT INTO widgets
(
map_id,
widget_name
)
SELECT
mt.map_id, 'Bupo'
FROM
map_tags mt
WHERE
mt.map_license = '12345'
Этот запрос добавит в таблицу widgets
новую запись для каждого виджета, связанную с картой, имеющей лицензию '12345'
. Убедитесь, что в таблице widgets
отсутствуют ограничения, которые могут препятствовать вставке дубликатов map_id
с тем же именем виджета.
Краткий ответ:
У вас нет "одной записи", а есть "множество с 1 записью".
Если говорить о JavaScript: у вас есть "массив с 1 значением", а не "1 значение".
В вашем примере в подзапросе может быть возвращена одна запись, но вы все равно пытаетесь распаковать "массив" записей в отдельные фактические параметры, куда можно передать только 1 параметр.
Мне понадобилось несколько часов, чтобы понять, "почему нет". Я пытался сделать нечто очень похожее:
Вот мои заметки:
tb_table01: (нет записей)
+---+---+---+
| a | b | c | << названия колонок
+---+---+---+
tb_table02:
+---+---+---+
| a | b | c | << названия колонок
+---+---+---+
|'d'|'d'|'d'| << запись #1
+---+---+---+
|'e'|'e'|'e'| << запись #2
+---+---+---+
|'f'|'f'|'f'| << запись #3
+---+---+---+
-- Это выражение приведет к ошибке:
INSERT into tb_table01
( a, b, c )
VALUES
( 'record_1.a', 'record_1.b', 'record_1.c' ),
( 'record_2.a', 'record_2.b', 'record_2.c' ),
-- Этот подзапрос возвращает
-- несколько строк. И они не
-- распаковываются автоматически,
-- как в Javascript, где можно
-- передать массив в вариативную функцию.
(
SELECT a,b,c from tb_table02
)
;
В основном, не думайте о "VALUES" как о вариативной функции, которая может распаковать массив записей. Здесь нет распаковки аргументов, как в функции JavaScript. Например:
function takeValues( ...values ){
values.forEach((v)=>{ console.log( v ) });
};
var records = [ [1,2,3],[4,5,6],[7,8,9] ];
takeValues( records );
//:РЕЗУЛЬТАТ:
//: console.log #1 : [1,2,3]
//: console.log #2 : [4,5,6]
//: console.log #3 : [7,8,9]
Возвращаясь к вашему SQL-вопросу:
Факт того, что такой функциональности не существует, не меняется, даже если ваш подзапрос содержит только один результат. Это "множество с одной записью", а не "одна запись".
Обновление строк таблицы в Postgres с использованием подзапроса
Postgres: Как повысить пользователя до суперпользователя?
Как вставить несколько строк в SQLite?
Решения для INSERT OR UPDATE в SQL Server
Как объединить все массивы целых чисел из всех записей в один массив в PostgreSQL