SQL错误42601:在拼接TEXT时出现语法错误

后端开发 2026-07-09

我遇到了如下错误:

[42601] ERROR: syntax error at or near "$3" in context "|| $2 AS $3", at line 1

Where: PL/pgSQL function "create_internal_tables" line 19 at SQL statement

当我尝试运行这里所示的过程来创建表时。

注:我甚至尝试将 v_tgt_tabv_src_tab 声明为 VARCHAR(50),以及使用 CONCAT 函数,但仍然收到相同的错误。

CREATE OR REPLACE PROCEDURE create_internal_tables()
AS $$
DECLARE
    v_tgt_tab TEXT;
    v_src_tab TEXT;
    CNT INT = 0;
    wh_tab_rec RECORD;
BEGIN
FOR wh_tab_rec IN
            (
                SELECT  COALESCE(a.schema_name, b.schema_name) as schema_name,
                        COALESCE(a.table_name, b.table_name) as table_name
                FROM svv_all_tables AS a RIGHT OUTER JOIN svv_all_tables b
                ON  a.schema_name = b.schema_name AND
                    a.table_name = b.table_name
                WHERE   a.database_name = 'nonprod'
                AND     b.database_name = 'warehouse'
                ORDER BY b.schema_name, b.table_name
            )
    LOOP
       SELECT 'nonprod.' || wh_tab_rec.schema_name || '.tmp_' || wh_tab_rec.table_name AS v_tgt_tab;
       SELECT 'warehouse.' || wh_tab_rec.schema_name || '.' || wh_tab_rec.table_name AS v_src_tab;
        RAISE INFO 'Creating destination internal table %', v_tgt_tab;
        EXECUTE ('CREATE TABLE %I AS SELECT * FROM %I WHERE 1=0', v_tgt_tab, v_src_tab );
        CNT := CNT + 1;
    END LOOP;
    RAISE NOTICE '% internal tables created in destination.', CNT;
END;
$$ LANGUAGE plpgsql;

CALL create_internal_tables();

基于提供的答案,我对代码进行了如下修改。看起来现在已经可以工作了。

CREATE OR REPLACE PROCEDURE create_table_as_select(
    target_table_name IN VARCHAR,
    source_table_name IN VARCHAR
)
LANGUAGE plpgsql
AS $$
BEGIN
EXECUTE 'CREATE TABLE ' || target_table_name || ' AS SELECT * FROM ' || source_table_name || ';';
END;
$$;


CREATE OR REPLACE PROCEDURE create_internal_tables()
LANGUAGE plpgsql
AS $$
DECLARE
    v_tgt_tab VARCHAR(256);
    v_src_tab VARCHAR(256);
    CNT INT = 0;
    wh_tab_rec RECORD;
BEGIN
FOR wh_tab_rec IN
            (
                SELECT  COALESCE(a.schema_name, b.schema_name) as schema_name,
                        COALESCE(a.table_name, b.table_name) as table_name
                FROM svv_all_tables AS a RIGHT OUTER JOIN svv_all_tables b
                ON  a.schema_name = b.schema_name AND
                    a.table_name = b.table_name
                WHERE   a.database_name = 'nonprod'
                AND     b.database_name = 'warehouse'
                AND     b.schema_name NOT LIKE 'pg_%'
                AND     b.schema_name NOT LIKE 'dbt_%'
                AND     b.schema_name NOT LIKE 'models%'
                AND     b.schema_name NOT IN ('information_schema', 'control')
                ORDER BY b.schema_name, b.table_name
            )
    LOOP
       v_tgt_tab := 'nonprod.' || wh_tab_rec.schema_name || '.tmp_' || wh_tab_rec.table_name;
       v_src_tab := 'warehouse.' || wh_tab_rec.schema_name || '.' || wh_tab_rec.table_name;
        --EXECUTE ('CREATE TABLE %I AS SELECT * FROM %I WHERE 1=0', v_tgt_tab, v_src_tab );
        CALL create_table_as_select(v_tgt_tab, v_src_tab);
        CNT := CNT + 1;
    END LOOP;
END;
$$;

CALL create_internal_tables();

解决方案

你不能把变量 (v_tgt_tab) 作为别名使用。PL/pgSQL在执行SQL语句时会把你的变量替换成参数,而参数不能替代像表名、列名或别名这样的标识符。

看看你的代码,似乎你根本不想要别名,而是想给变量赋一个值。应该是如下形式:

v_tgt_tab := 'nonprod.' || wh_tab_rec.schema_name || '.tmp_' || wh_tab_rec.table_name;

你的代码仍然存在许多其他问题:

  • 你正在把表引用成 databasename.schemaname.tablename,看起来你想进行跨数据库查询。按照PostgreSQL的设计,这是不可能的。如果你确实想查询不同的数据库,需要使用一个外部数据封装器(FDW)。也许把整件事在客户端实现,而不是在数据库内部,会是更好的解决方案。
  • 你把 EXECUTE 作为一个函数来使用,并且在 EXECUTEformat() 函数之间混用,似乎有些奇怪。你需要

EXECUTE format('CREATE TABLE %I ...', tablename); * 你的 CREATE TABLE 语句(正确地!)使用了标识符的占位符 %I,但这意味着最终会得到像 "schemaname.tablename" 这样的表名,而你真正想要的是 "schemaname"."tablename"

你需要写成类似于

EXECUTE format('CREATE TABLE %I.%I ...', schemaname, tablename);

如果那段代码是由聊天机器人写的,你应该找一个更靠谱的版本。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章