Postgresql

“錯誤:沒有參數 $1”在“EXECUTE .. USING ..;”中plpgsql 中的語句

  • September 16, 2020

我有一個 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;

當我呼叫它時,它是成功的:

# select mk_child(2015,1);                                           
NOTICE:  Creating child table child_y15m01
  mk_child   
--------------
child_y15m01
(1 row)

但是,如果我使用EXECUTE ... USING ...;表單(在上面的程式碼段中註釋掉),我會收到一個錯誤:

# select mk_child(2015,2);
NOTICE:  Creating child table child_y15m02
ERROR:  there is no parameter $1
CONTEXT:  SQL statement "CREATE TABLE child_y15m02 ( CHECK ( end_time >= $1 AND end_time < $2 ) ) INHERITS (parent_table)"
PL/pgSQL function mk_child(integer,integer) line 21 at EXECUTE statement

PostgreSQL 文件明確表示這種格式有效且效率更高。

那麼為什麼它沒有按預期工作呢?

您只能將值傳遞給 DML 語句。手冊:

參數符號的另一個限制是它們僅適用於 SELECTINSERTUPDATEDELETE命令。在其他語句類型(通常稱為實用程序語句)中,您必須以文本方式插入值,即使它們只是數據值。

所以CREATE TABLE語句不通過USING子句接受參數,即使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;

有關的:

如果這是關於表分區的,請查看 Postgres 10 或更高版本中新的聲明性表分區。帶有範常式式碼和連結的相關答案:

引用自:https://dba.stackexchange.com/questions/121453