是否有某种方法可以执行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特定代码,因此这两者都不相关.