【问题标题】:INSERT SELECT FROM VALUES casting从值转换中插入选择
【发布时间】:2018-05-12 04:01:27
【问题描述】:

通常希望从 SELECT 表达式中插入(例如,使用 WHERE 子句进行限定),但这会使 postgresql 对列类型感到困惑。

例子:

CREATE TABLE example (a uuid primary key, b numeric);
INSERT INTO example 
SELECT a, b 
FROM (VALUES ('d853b5a8-d453-11e7-9296-cec278b6b50a', NULL)) as data(a,b);
=> ERROR:  column "a" is of type uuid but expression is of type text

这可以通过显式转换值来解决:

INSERT INTO example 
SELECT a, b 
FROM (VALUES ('d853b5a8-d453-11e7-9296-cec278b6b50a'::uuid, NULL::numeric)) as data(a,b);

但这很麻烦,而且是维护负担。有没有办法让 postgres 理解 VALUES 表达式与表行具有相同的类型,即类似于

VALUES('d853b5a8-d453-11e7-9296-cec278b6b50a', NULL)::example%ROWTYPE

编辑:

使用 (data::example).* 的建议很简洁,但不幸的是,当与这样的 WHERE 子句结合使用时,它似乎完全搞砸了 postgres 查询计划器:

INSERT INTO example 
SELECT (data::example).* 
FROM (VALUES ('d853b5a8-d453-11e7-9296-cec278b6b50a', NULL)) as data 
WHERE NOT EXISTS (SELECT * FROM example 
                  WHERE (data::example) 
                  IS NOT DISTINCT FROM example);

对于一张大桌子,这需要几分钟时间。

【问题讨论】:

  • “搞砸刨床”到底是什么意思,我假设您的意思是“性能不佳”。该条件当然不会使用该表上的 any 索引。如果您希望where 条件使用索引,则需要比较索引列,而不是整条记录
  • 为什么是“当然”?添加(据我所见)冗余连词WHERE (data::example).a = a AND (data::example) IS NOT DISTINCT FROM example 会导致显着不同的性能行为,这似乎几乎没有声明性。
  • 因为您无法创建包含完整记录的索引。索引只能包含列 - 甚至索引中的所有列都不会与具有完整记录的索引相同
  • 为什么需要完整记录的索引?两条记录匹配当且仅当所有列都匹配。为什么postgres 不使用a 上的主键在example 中查找data.a,然后将匹配的行(如果有)与data::example 进行比较?没有其他行可能匹配,所以线性扫描让我感到困惑。
  • 因为您将完整的记录与表达式 (data::example) IS NOT DISTINCT FROM example 进行比较 - 您不是在比较单个(主键)列。另外:表达式(data::example) 不包含有关主键的信息,因为对象type 没有主键。只有一个表有一个主键。

标签: sql postgresql sql-types


【解决方案1】:

可以将记录转换为表格的行类型:

INSERT INTO example 
SELECT (data::example).*
FROM (
    VALUES 
      ('d853b5a8-d453-11e7-9296-cec278b6b50a', NULL),
      ('54514c89-f188-490a-abbb-268f9154ab2c', 42)
) as data;

data::example 将整个行转换为example 类型的记录。然后(...).* 将其转换为表类型example 中定义的列

【讨论】:

  • 不错的功能 :) 但我想这取决于子查询中的列顺序,当您有多个环境并且脚本以不同的顺序应用时,这可能会导致问题。例如 envA:example(col1, col2, col3) 和 envB:example(col1, col3, col2)。
  • 这很简洁!但不幸的是,当与 WHERE 查询结合使用时,它似乎也会带来一些严重的性能问题,我已经编辑了问题以反映这一点。
  • @as.:一旦您得到原始问题的答案,请不要将您的问题扩大到更广泛的范围。
【解决方案2】:

你可以直接使用VALUES

INSERT INTO example(a, b)
VALUES ('d853b5a8-d453-11e7-9296-cec278b6b50a', NULL);

DBFiddle Demo


或者只施展一次:

INSERT INTO example(a, b)
SELECT a::uuid, b::numeric
FROM (VALUES ('d853b5a8-d453-11e7-9296-cec278b6b50a', NULL), 
             ('bb53b5a8-d453-11e7-9296-cec278b6b50a',1) ) as data(a,b);

DBFiddle Demo2

注意,请始终明确定义列列表。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-02
    • 2021-10-07
    • 1970-01-01
    • 2019-05-22
    • 2013-05-26
    相关资源
    最近更新 更多