4

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

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

4

2 回答 2

1

您可以查询 information_schema.columns 以按正确顺序获取列:

SELECT INTO cols array_to_string(array_agg(column_name::text), ',') 
FROM (
    SELECT column_name 
    FROM information_schema.columns 
    WHERE table_name = 'matview' 
    ORDER BY ordinal_position
) AS x;
EXECUTE 'INSERT INTO ' || matview  || ' SELECT ' || cols || ' FROM ' || entry.v_name;

您可以直接从 pg_attribute 获取列列表——只需将内部替换SELECTinformation_schema.columns

SELECT attname AS column_name
FROM pg_attribute
WHERE attrelid = 'matview'::regclass AND attisdropped = false
ORDER BY attnum;
于 2012-11-17T00:25:54.897 回答
1

将此构建到您的 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找到一行,则说明有问题。

::regclass如果表或视图不存在(或超出范围 - 不在且不符合模式),则转换为将立即引发异常search_path

如果您还想检查列的数据类型,只需添加atttypidUSING子句中。

顺便说一句:查询pg_catalog表通常比查询臃肿的视图 int 快一个数量级information_schema- information_schema 仅对 SQL 标准合规性和代码的可移植性有好处。由于您正在编写 100 % Postgres 特定的代码,因此这里两者都不相关。

于 2012-11-17T00:38:50.400 回答