PostgreSQL:插入内的子选择

Posted

技术标签:

【中文标题】PostgreSQL:插入内的子选择【英文标题】:PostgreSQL: Sub-select inside insert 【发布时间】:2012-10-12 13:59:08 【问题描述】:

我有一张桌子叫map_tags

map_id | map_license | map_desc

还有另一个表 (widgets),其记录包含对 map_tags 记录的外键引用(1 到 1):

widget_id | map_id | widget_name

鉴于所有map_licenses 都是唯一的约束(但未设置为map_tags 上的键),那么如果我有map_licensewidget_name,我想执行插入widgets 都在同一个 SQL 语句中:

INSERT INTO
    widgets w
(
    map_id,
    widget_name
)
VALUES (
    (
        SELECT
            mt.map_id
        FROM
            map_tags mt
        WHERE
            // This should work and return a single record because map_license is unique
            mt.map_license = '12345'
    ),
    'Bupo'
)

相信我在正确的轨道上,但马上就知道这对于 Postgres 来说是不正确的 SQL。有人知道实现这样一个查询的正确方法吗?

【问题讨论】:

从来没有用PostgreSQL做过,但它不应该看起来像INSERT INTO widgets SELECT NULL, map_id, 'Bupo' FROM map_tags WHERE map_license = '12345'吗? 感谢@raina77ow (+1) - 我不确定这是否应该是这样。代替(...) VALUES(...) 语法的SELECT 语句绝对让我失望。你介意向我解释一下这应该如何工作吗?再次感谢! 好吧,我只是在 mysql 中像 this 一样使用它......我想原因是你必须构建将要插入的整组数据 - 和 VALUES ((SELECT smth), 'smth_else') 就是不这样做。 ) 您实际上不需要根据以下语法使用值:INSERT INTO table2 (column_name(s)) SELECT column_name(s) FROM table1; 【参考方案1】:
INSERT INTO widgets
(
    map_id,
    widget_name
)
SELECT
    mt.map_id, 'Bupo'
FROM
    map_tags mt
WHERE
    mt.map_license = '12345'

【讨论】:

当您要插入的表具有 SERIAL PK 或类似内容时,这是一种非常有用的语法【参考方案2】:

使用INSERT INTO SELECT 变体,包括SELECT 语句中的任何常量。

PostgreSQL INSERT 语法是:

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'

【讨论】:

为什么另一个几乎相同但不太完整的答案在一分钟后被更多人投票并接受? 因为堆栈交换正成为一个过于强大的工具。所以他们决定让它很破。【参考方案3】:

快速回答: 您没有“一条记录”您有“设置有 1 条记录” 如果这是 javascript:您有一个“具有 1 个值的数组”而不是“1 个值”。

在您的示例中,子查询中可能会返回一条记录, 但是您仍在尝试将记录的“数组”解压缩为单独的 将实际参数放到一个只需要 1 个参数的地方。

我花了几个小时来思考“为什么不”。 当我试图做一些非常相似的事情时:

这是我的笔记:

tb_table01: (no records)
+---+---+---+
| a | b | c | << column names
+---+---+---+

tb_table02:
+---+---+---+
| a | b | c | << column names
+---+---+---+
|'d'|'d'|'d'| << record #1
+---+---+---+
|'e'|'e'|'e'| << record #2
+---+---+---+
|'f'|'f'|'f'| << record #3
+---+---+---+

--This statement will fail:
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' ),

    -- This sub query has multiple
    -- rows returned. And they are NOT
    -- automatically unpacked like in 
    -- javascript were you can send an
    -- array to a variadic function.
    (
        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 );

//:RESULT:
//: console.log #1 : [1,2,3]
//: console.log #2 : [4,5,7]
//: console.log #3 : [7,8,9]

回到你的 SQL 问题:

此功能不存在的现实不会改变 只是因为您的子选择仅包含一个结果。它是 “设置一个记录”而不是“一个记录”。

【讨论】:

以上是关于PostgreSQL:插入内的子选择的主要内容,如果未能解决你的问题,请参考以下文章

Postgresql:如何为postgres中的相同时间戳选择“媒体”列中的最大值?

PostgreSQL数据库导入大量数据时如何优化

PostgreSQL数据库导入大量数据时如何优化

PostgreSQL数据库导入大量数据时如何优化

乱序插入导致索引膨胀

postgres 使用存储过程批量插入数据