0

Подзапрос внутри вставки (INSERT)

18

У меня есть таблица с названием 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 ответ(ов)

0

Чтобы использовать вариант 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. Таким образом, вы можете легко комбинировать выборку из одной таблицы и константы в одной команде вставки.

0

Вы можете использовать следующий 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 с тем же именем виджета.

0

Краткий ответ: У вас нет "одной записи", а есть "множество с 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-вопросу:

Факт того, что такой функциональности не существует, не меняется, даже если ваш подзапрос содержит только один результат. Это "множество с одной записью", а не "одна запись".

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь