我想创建一个 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;
Run Code Online (Sandbox Code Playgroud)
该函数已成功创建,但是当我尝试使用此代码使用它时:
BEGIN DBMS_OUTPUT.PUT_LINE(CHECK_EXISTS('EMPLOYEES' ,'DEPARTMENT_ID=50')); END;
Run Code Online (Sandbox Code Playgroud)
我收到异常:ORA-00903:表名无效。
您不能对表名或列名或完整条件使用绑定变量。这些必须在解析语句时知道,这是在分配绑定变量之前 - 否则您将失去绑定变量的一项好处。您只能绑定变量的值。
当您的字符串被解析时,表名被逐字解释为: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;
Run Code Online (Sandbox Code Playgroud)
考虑到您将看到的有关使用绑定变量避免 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;
/
Run Code Online (Sandbox Code Playgroud)
... 将串联推入一个匿名块;执行然后使用 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;
Run Code Online (Sandbox Code Playgroud)