如何使用变量表名和条件执行立即数

时间:2016-04-25 17:56:24

标签: oracle plsql

我想创建一个PL / SQL函数,该函数传递一个表名和一个条件,并返回该条件在该表上遇到的行数。

我创建了这个函数:

CREATE OR REPLACE FUNCTION CHECK_EXISTS 
      (TABLE_NAME VARCHAR2, CONDITION VARCHAR2) RETURN NUMBER AS 
      VAL NUMBER;
      SQL_CODE VARCHAR2(200):='SELECT COUNT (*) INTO VAL FROM :TABLE_NAME WHERE
      :CONDITION';
BEGIN 
      EXECUTE IMMEDIATE SQL_CODE USING TABLE_NAME ,CONDITION;
      RETURN VAL;
EXCEPTION
      WHEN NO_DATA_FOUND THEN
      RETURN 0;
END;

该功能已成功创建,但当我尝试使用此代码时使用它:

BEGIN DBMS_OUTPUT.PUT_LINE(CHECK_EXISTS('EMPLOYEES' ,'DEPARTMENT_ID=50')); END;

我得到异常:ORA-00903:表名无效。

2 个答案:

答案 0 :(得分:4)

您不能对表名或列名使用绑定变量,也不能使用完整的条件。解析语句时必须知道这些,这是在绑定变量分配之前 - 否则你将失去绑定变量的一个好处。您只能绑定变量的值。

解析字符串时,表名称按字面解释为:TABLE_NAME,冒号使表名无效。 使用您传入函数的值。

所以你需要连接名称和条件;你的INTO条款也在错误的地方:

CREATE OR REPLACE FUNCTION CHECK_EXISTS 
      (TABLE_NAME VARCHAR2, CONDITION VARCHAR2) RETURN NUMBER AS 
      VAL NUMBER;
      SQL_CODE VARCHAR2(200):='SELECT COUNT (*) FROM '
        || TABLE_NAME || ' WHERE ' || CONDITION;
BEGIN 
      EXECUTE IMMEDIATE SQL_CODE INTO VAL;
      RETURN VAL;
EXCEPTION
      WHEN NO_DATA_FOUND THEN
      RETURN 0;
END;

考虑到有关使用绑定变量避免SQL注入的所有建议,这可能看起来很奇怪。这仍然适用,你不能在这里为表名做。

但这确实意味着你可能对SQL注入开放,所以你应该清理你得到的输入,这对于表名来说相当简单 - 你可以看看它是否存在于all_tables中,但是变量条件将更难检查。幸运的是,您只能使用动态SQL执行单个语句,因此很难做任何过于讨厌的事情,除非您可以放置​​在有效条件下的副作用。

如果由于某种原因你确实想要为你的execute immediate电话使用绑定变量,你可以做一些令人费解的事情,如:

CREATE OR REPLACE FUNCTION CHECK_EXISTS 
      (TABLE_NAME VARCHAR2, CONDITION VARCHAR2) RETURN NUMBER AS 
      VAL NUMBER;
      SQL_CODE VARCHAR2(200):=q'[BEGIN EXECUTE IMMEDIATE 'SELECT COUNT (*) FROM ' || :TABLE_NAME || ' WHERE ' || :CONDITION INTO :VAL; END;]';
BEGIN 
      EXECUTE IMMEDIATE SQL_CODE USING TABLE_NAME, CONDITION, OUT VAL;
      RETURN VAL;
EXCEPTION
      WHEN NO_DATA_FOUND THEN
      RETURN 0;
END;
/

...将串联推入匿名块;然后执行使用OUT绑定变量来计算结果而不是INTO。我不确定这样做有什么好处。

正如@AvrajitRoy所提到的那样,由于你正在进行聚合count(),查询将始终返回一个结果(除非它从一个不存在的表中出错或当然是格式错误的,你想知道的约),所以你永远无法达到异常处理程序。虽然它没有造成任何伤害,但可以删除它:

CREATE OR REPLACE FUNCTION CHECK_EXISTS (TABLE_NAME VARCHAR2, CONDITION VARCHAR2)
RETURN NUMBER AS 
    VAL NUMBER;
    SQL_CODE VARCHAR2(200):='SELECT COUNT (*) FROM '
        || TABLE_NAME || ' WHERE ' || CONDITION;
BEGIN 
    EXECUTE IMMEDIATE SQL_CODE INTO VAL;
    RETURN VAL;
END;

答案 1 :(得分:0)

如上所述,Alex只是添加make使异常处理通用,因为当我们执行COUNT(*)时,不会引发NO_DATA_FOUND。

CREATE OR REPLACE FUNCTION CHECK_EXISTS(
    TABLE_NAME VARCHAR2,
    CONDITION  VARCHAR2)
  RETURN NUMBER
AS
  VAL      NUMBER;
  SQL_CODE VARCHAR2(2000 CHAR):= 'SELECT COUNT (*) FROM '||TABLE_NAME||' WHERE '|| CONDITION;
BEGIN
  EXECUTE IMMEDIATE SQL_CODE INTO val;
  RETURN VAL;
EXCEPTION
WHEN OTHERS THEN --no data found may not get raised as we are doing count(*) here
  RETURN 0;
END;