我想创建一个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:表名无效。
答案 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;