“错误:“EXECUTE .. USING ..;”中没有参数 $1” plpgsql 中的语句

don*_*llo 5 postgresql dynamic-sql ddl plpgsql postgresql-9.4

我有一个 plpgsql 函数来使用 PostgreSQL 中的表继承创建子表,如下所示:

CREATE TABLE parent_table (
  value integer,
  end_time timestamp without time zone
);

CREATE OR REPLACE FUNCTION mk_child(_year INTEGER, _month INTEGER)
  RETURNS text AS $$
DECLARE
  tname varchar;
  start_date date;
  end_date date;
  next_month varchar := (_month + 1)::text;
  next_year varchar := (_year + 1)::text;
BEGIN
  tname := 'child_y' || substring(_year::text from 3 for 2)
           || 'm' || lpad(_month::text, 2, '0');
  start_date := DATE (_year::text || '-' || _month::text || '-01');
  IF ( _month = 12 ) THEN
       end_date := DATE (next_year || '-01-01');
  ELSE
       end_date := DATE (_year::text || '-' || next_month || '-01');
  END IF;

  RAISE NOTICE 'Creating child table %', tname;
  EXECUTE format('CREATE TABLE %I ( CHECK ( end_time >= %L AND end_time < %L))
                  INHERITS (parent_table)', tname, start_date, end_date);
  -- EXECUTE format('CREATE TABLE %I ( CHECK ( end_time >= $1 AND end_time < $2))
  --                 INHERITS (parent_table)', tname)
  -- USING start_date, end_date;
  RETURN tname;
END
$$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

当我调用它时,它就成功了:

CREATE TABLE parent_table (
  value integer,
  end_time timestamp without time zone
);

CREATE OR REPLACE FUNCTION mk_child(_year INTEGER, _month INTEGER)
  RETURNS text AS $$
DECLARE
  tname varchar;
  start_date date;
  end_date date;
  next_month varchar := (_month + 1)::text;
  next_year varchar := (_year + 1)::text;
BEGIN
  tname := 'child_y' || substring(_year::text from 3 for 2)
           || 'm' || lpad(_month::text, 2, '0');
  start_date := DATE (_year::text || '-' || _month::text || '-01');
  IF ( _month = 12 ) THEN
       end_date := DATE (next_year || '-01-01');
  ELSE
       end_date := DATE (_year::text || '-' || next_month || '-01');
  END IF;

  RAISE NOTICE 'Creating child table %', tname;
  EXECUTE format('CREATE TABLE %I ( CHECK ( end_time >= %L AND end_time < %L))
                  INHERITS (parent_table)', tname, start_date, end_date);
  -- EXECUTE format('CREATE TABLE %I ( CHECK ( end_time >= $1 AND end_time < $2))
  --                 INHERITS (parent_table)', tname)
  -- USING start_date, end_date;
  RETURN tname;
END
$$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

但是,如果我使用该EXECUTE ... USING ...;表单(在上面的代码片段中注释掉),则会收到错误:

# select mk_child(2015,1);                                           
NOTICE:  Creating child table child_y15m01
   mk_child   
--------------
 child_y15m01
(1 row)
Run Code Online (Sandbox Code Playgroud)

PostgreSQL 文档明确指出这种格式有效并且效率更高。

那么为什么它没有按预期工作呢?

Erw*_*ter 5

您只能将值传递给 DML 语句。手册:

对参数符号的另一个限制是它们只能在 SELECTINSERTUPDATEDELETE命令中使用。在其他语句类型(通常称为实用程序语句)中,您必须以文本方式插入值,即使它们只是数据值。

因此,CREATE TABLE语句不接受 viaUSING子句参数,即使CHECK约束中的表达式看起来像可能参数化的值。

除此之外,您可以很大程度上简化您的功能:

CREATE OR REPLACE FUNCTION mk_child(_year int, _month int)
  RETURNS text AS
$func$
DECLARE
   start_date date := to_date(_year::text || _month::text, 'YYYYMM');
   tname text := to_char(start_date , '"child_y"YY"m"MM');
BEGIN
   RAISE NOTICE 'Creating child table %', tname;
   EXECUTE format('
      CREATE TABLE %I (CHECK (end_time >= %L AND end_time < %L))
      INHERITS (parent_table)'
    , tname, start_date, (start_date + interval '1 month')::date);
   RETURN tname;
END
$func$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

有关的:

如果这是关于表分区,请查看 Postgres 10 或更高版本中新的声明性表分区。带有示例代码和链接的相关答案: