【问题标题】:Dynamically passing db and table names using stored procedure in PostgreSQL使用 PostgreSQL 中的存储过程动态传递数据库和表名
【发布时间】:2020-11-08 21:05:17
【问题描述】:

我正在PostgreSQL 中创建一个存储过程,它将首先根据'ID' 检查给定表中是否存在数据。如果是,则将其移动到其他表并在给定表名中插入最新记录。我编写了一个存储过程,我在其中使用硬编码值进行了尝试,它可以按我的需要工作,但是当我试图使其具有通用性时,即创建变量然后在查询中传递这些变量时,它会引发错误。我在下面提到了SO linksofficial documentation link,并且能够修改我的存储过程:

  1. First SO link
  2. Second SO link

下面是我的存储过程:

CREATE OR REPLACE PROCEDURE compareDups(ab integer, b json, tablename varchar)
AS $$
DECLARE 
  actualTableName varchar := 'testing.'||tablename;
  histTableName varchar:= actualTableName ||'_hist';
  job_id Integer:=0;
BEGIN --<<<< HERE
  EXECUTE 'SELECT id FROM '||actualTableName||' WHERE id =$1' INTO job_id USING ab;
  -- if there is data for id in the table then perform below operations
  IF job_id IS NOT NULL THEN
      EXECUTE FORMAT('INSERT INTO %I as select * from %L where id = $1',histTableName,actualTableName) USING ab;
      EXECUTE FORMAT('DELETE FROM %I where id = $1',actualTableName) USING ab;
      EXECUTE FORMAT('INSERT INTO %I values($1,$2)',actualTableName) USING ab,b;
  -- if id is not present then create a new record in the actualTable
  ELSE    
      EXECUTE FORMAT('INSERT INTO %I values($1,$2)',actualTableName) USING ab,b;
  END IF;

END; --<<<< END HERE
$$
LANGUAGE plpgsql;

所以,虽然creating variables 我只使用了EXECUTE 选项,而calling queries 我使用了EXECUTE FORMAT(...) 选项。

当我尝试调用它时,出现以下错误:

ERROR:  syntax error at or near "select"
LINE 1: INSERT INTO "testing.sampletesting_hist" as select * from 't...
                                                    ^
QUERY:  INSERT INTO "testing.sampletesting_hist" as select * from 'testing.sampletesting' where id = $1
CONTEXT:  PL/pgSQL function comparedups(integer,json,character varying) line 10 at EXECUTE
SQL state: 42601 

我在这里错过了什么?

【问题讨论】:

  • 您缺少的是“testing.sampletesting_hist”应该是“testing”。“sampletesting_hist” 而且这个“testing.sampletesting”是无效的。标识符需要双引号。
  • 是的,我明白了:)

标签: postgresql stored-procedures


【解决方案1】:

所以,我想回答我自己的问题,也许它可以帮助像我这样的人。我也可以通过修改我设置schema nameactualtablename 字符串来修复上述代码。因此,我在过程中添加了一个SET 语句,用于设置需要进行预期操作的模式名称,它对我有用。

CREATE OR REPLACE PROCEDURE compareDups(ab integer, b json, tablename varchar)
AS $$
DECLARE 
  actualTableName varchar := tablename;
  histTableName varchar:= actualTableName ||'_hist';
  job_id Integer:=0;
BEGIN --<<<< HERE
  SET search_path to testing; -- Set the schema name
  EXECUTE 'SELECT id FROM '||actualTableName||' WHERE id =$1' INTO job_id USING ab;
  -- if there is data for id in the table then perform below operations
  IF job_id IS NOT NULL THEN
      EXECUTE FORMAT('INSERT INTO %I select * from %I where id = $1',histTableName,actualTableName) USING ab;
      EXECUTE FORMAT('DELETE FROM %I where id = $1',actualTableName) USING ab;
      EXECUTE FORMAT('INSERT INTO %I values($1,$2)',actualTableName) USING ab,b;
  -- if id is not present then create a new record in the actualTable
  ELSE    
      EXECUTE FORMAT('INSERT INTO %I values($1,$2)',actualTableName) USING ab,b;
  END IF;

END; --<<<< END HERE
$$
LANGUAGE plpgsql;  

不知何故,这个过程被存储在public 模式下。因此,在调用它时,我必须使用以下命令:

set search_path to public;
call compareDups(12,'{"name":"CTTT"}','sampletesting');

【讨论】:

  • 好吧,您没有对PROCEDURE 名称进行架构限定,因此它是在search_path 的第一个架构中创建的。在过程中设置 search_path 仅在过程内部起作用。如果您希望将对象分配给模式,那么显式比隐式更好,然后以这种方式命名它,例如my_schema.compareDups。或者在 CREATE 之前立即执行 SET search_path
  • 所以你的意思是我应该在创建过程之前指定 set search_path 命令,以便在该模式下创建它?
  • 是的。虽然更好的选择是做CREATE OR REPLACE PROCEDURE testing.compareDups(...) 然后就不用猜了。
  • 是的,让我试试这个。谢谢@Adrian ;)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-07-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多