sql – 列安全`INSERT INTO t1 SELECT * FROM …`?

是否有某种方法可以执行INSERT INTO t1 SELECT * FROM …如果列名不重合则会失败?

我正在使用Postgresql 9.x列名称不是事先知道的.

动机:我正在通过(非常标准的)PL / pgSQL程序定期刷新物化视图:

CREATE OR REPLACE FUNCTION matview_refresh(name) RETURNS void AS 
$BODY$
DECLARE 
    matview ALIAS FOR $1;
    entry matviews%ROWTYPE;
BEGIN
    SELECT * INTO entry FROM matviews WHERE mv_name = matview;
    IF NOT FOUND THEN
        RAISE EXCEPTION 'Materialized view % does not exist.', matview;
    END IF;

    EXECUTE 'TRUNCATE TABLE ' || matview;
    EXECUTE 'INSERT INTO ' || matview  || ' SELECT * FROM ' || entry.v_name;

    UPDATE matviews SET last_refresh=CURRENT_TIMESTAMP WHERE mv_name=matview;
    RETURN;
END

我更喜欢TRUNCATE后跟SELECT * INTO而不是DROP / CREATE,因为它看起来更轻,并且友好.如果有人在视图中添加/删除列(然后我会执行DROP / CREATE),它会失败,但是,无关紧要,在这种情况下,刷新将无法完成,我们很快就会发现问题.重要的是今天发生了什么:有人改变了视图的两列(相同类型)的顺序,并刷新了插入的虚假数据.

最佳答案 将其构建到plpgsql函数中以验证视图和表是否在同一序列中完全共享相同的列名:

IF EXISTS (
    SELECT 1
    FROM (
       SELECT *
       FROM   pg_attribute
       WHERE  attrelid = matview::regclass
       AND    attisdropped = FALSE
       AND    attnum > 0
       ) t
    FULL OUTER JOIN (
       SELECT *
       FROM   pg_attribute
       WHERE  attrelid = entry.v_name::regclass
       AND    attisdropped = FALSE
       AND    attnum > 0
       ) v USING (attnum, attname) -- atttypid to check for type, too
    WHERE t.attname IS NULL
    OR    v.attname IS NULL
   ) THEN 
   RAISE EXCEPTION 'Mismatch between table and view!';
END IF;

对于列名列表之间的任何不匹配,FULL OUTER JOIN会为NULL值添加一行.因此,如果EXISTS找到一行,则表示某些内容已关闭.

如果表或视图不存在(或超出范围 – 不在search_path而不是模式限定),则转换为:: regclass会立即引发异常.

如果您还想检查列的数据类型,只需将atttypid添加到USING子句即可.

顺便说一句:查询pg_catalog表通常比查询膨胀的视图更快一个数量级.information_schema – information_schema仅适用于SQL标准兼容性和代码的可移植性.由于您正在编写100%Postgres特定代码,因此这两者都不相关.

点赞