为 EXECUTE IMMEDIATE 解析 PL/SQL 语句中的占位符
Parsing placeholders in PL/SQL statement for EXECUTE IMMEDIATE
我最近几天正在学习这门语言。
我试图使用一个包含 PL/SQL 块的字符串和一个占位符字符串,它有两个字段,我想用从 SELECT 语句中检索到的一些数据替换。
我已经正确创建并填充了 table employees
。
问题是我需要 "replace" 那些占位符(变量 cmd2
中的 :name
和 :salary
)但是当我 EXECUTE IMMEDIATE
使用这些值时检索到此错误:ORA-01006: bind variable does not exist
.
这是代码片段:
DECLARE
cmd1 VARCHAR2(200) := 'SELECT * FROM employees';
cmd2 VARCHAR2(200) := 'BEGIN DBMS_OUTPUT.PUT_LINE('':name has a salary of :salary;''); END;';
str VARCHAR2(200);
c1 SYS_REFCURSOR;
emp employees%ROWTYPE;
BEGIN
OPEN c1 FOR cmd1;
LOOP
FETCH c1 INTO emp;
EXIT WHEN c1%NOTFOUND;
-- It doesn't work
EXECUTE IMMEDIATE cmd2 USING emp.name, emp.salary;
-- It works, but just prints ':name has a salary of :salary;'
EXECUTE IMMEDIATE cmd2;
END LOOP;
END;
预期结果应该是:
Name1 has a salary of 300;
Name2 has a salary of 700;
-- ...and so on
问题出在您 PL/SQL 定义 cmd2:
cmd2 VARCHAR2(200) :=
'BEGIN DBMS_OUTPUT.PUT_LINE('':name has a salary of :salary;''); END;';
您不能在字符串值中引用变量名称 - 它们只是那里的文本。此更改将使它起作用;
cmd2 VARCHAR2(200) :=
'BEGIN DBMS_OUTPUT.PUT_LINE(:name||'' has a salary of ''||:salary); END;';
现在第一次执行会成功,但第二次会失败:
ORA-01008: not all variables bound
所以去掉第二次执行就万事大吉了!
备注
您的示例不是动态 PL/SQL 的典型用例,因为静态 PL/SQL:
也可以实现同样的效果
BEGIN
FOR r IN SELECT * FROM employees
LOOP
DBMS_OUTPUT.PUT_LINE(r.name || ' has a salary of ' || r.salary');
END LOOP;
END;
动态 SQL 和 PL/SQL 只应在静态 SQL 不可能时真正使用 - 例如因为 table 名称、列名称或过程名称不固定。查看一些示例 here in the Oracle docs.
绑定变量最好用在 PLSQL
块内的 SQL
语句中。您不应在 DBMS_OUTPUT
语句中绑定变量。
你的情况
cmd2 VARCHAR2(200) := 'BEGIN DBMS_OUTPUT.PUT_LINE('':name has a salary
of :salary;''); END;';
此处BIND
变量的应用不正确。这是不允许的。
请参阅下面使用绑定变量的简单示例。
SQL> DECLARE
2 cmd1 VARCHAR2(200) := 'SELECT * FROM EMP';
3 cmd2 VARCHAR2(200) := 'SELECT * FROM EMP WHERE ENAME = :name and sal =:salary'; --<--See how bind variables are used
4 str VARCHAR2(200);
5
6 c1 SYS_REFCURSOR;
7
8 emp1 emp%ROWTYPE;
9 BEGIN
10 OPEN c1 FOR cmd1;
11 LOOP
12 FETCH c1 INTO emp1;
13 EXIT WHEN c1%NOTFOUND;
14
15 -- It doesn't work
16 EXECUTE IMMEDIATE cmd2 USING emp1.ename, emp1.sal;
17
18
19 END LOOP;
20 END;
21 /
PL/SQL procedure successfully completed.
SQL>
绑定变量在一个字符串中,因此它们不被视为绑定。
尝试
cmd2 VARCHAR2(200) := q'[BEGIN DBMS_OUTPUT.PUT_LINE(:name || ' has a salary of ' || :salary); END;]';
我最近几天正在学习这门语言。 我试图使用一个包含 PL/SQL 块的字符串和一个占位符字符串,它有两个字段,我想用从 SELECT 语句中检索到的一些数据替换。
我已经正确创建并填充了 table employees
。
问题是我需要 "replace" 那些占位符(变量 cmd2
中的 :name
和 :salary
)但是当我 EXECUTE IMMEDIATE
使用这些值时检索到此错误:ORA-01006: bind variable does not exist
.
这是代码片段:
DECLARE
cmd1 VARCHAR2(200) := 'SELECT * FROM employees';
cmd2 VARCHAR2(200) := 'BEGIN DBMS_OUTPUT.PUT_LINE('':name has a salary of :salary;''); END;';
str VARCHAR2(200);
c1 SYS_REFCURSOR;
emp employees%ROWTYPE;
BEGIN
OPEN c1 FOR cmd1;
LOOP
FETCH c1 INTO emp;
EXIT WHEN c1%NOTFOUND;
-- It doesn't work
EXECUTE IMMEDIATE cmd2 USING emp.name, emp.salary;
-- It works, but just prints ':name has a salary of :salary;'
EXECUTE IMMEDIATE cmd2;
END LOOP;
END;
预期结果应该是:
Name1 has a salary of 300;
Name2 has a salary of 700;
-- ...and so on
问题出在您 PL/SQL 定义 cmd2:
cmd2 VARCHAR2(200) :=
'BEGIN DBMS_OUTPUT.PUT_LINE('':name has a salary of :salary;''); END;';
您不能在字符串值中引用变量名称 - 它们只是那里的文本。此更改将使它起作用;
cmd2 VARCHAR2(200) :=
'BEGIN DBMS_OUTPUT.PUT_LINE(:name||'' has a salary of ''||:salary); END;';
现在第一次执行会成功,但第二次会失败:
ORA-01008: not all variables bound
所以去掉第二次执行就万事大吉了!
备注
您的示例不是动态 PL/SQL 的典型用例,因为静态 PL/SQL:
也可以实现同样的效果BEGIN
FOR r IN SELECT * FROM employees
LOOP
DBMS_OUTPUT.PUT_LINE(r.name || ' has a salary of ' || r.salary');
END LOOP;
END;
动态 SQL 和 PL/SQL 只应在静态 SQL 不可能时真正使用 - 例如因为 table 名称、列名称或过程名称不固定。查看一些示例 here in the Oracle docs.
绑定变量最好用在 PLSQL
块内的 SQL
语句中。您不应在 DBMS_OUTPUT
语句中绑定变量。
你的情况
cmd2 VARCHAR2(200) := 'BEGIN DBMS_OUTPUT.PUT_LINE('':name has a salary of :salary;''); END;';
此处BIND
变量的应用不正确。这是不允许的。
请参阅下面使用绑定变量的简单示例。
SQL> DECLARE
2 cmd1 VARCHAR2(200) := 'SELECT * FROM EMP';
3 cmd2 VARCHAR2(200) := 'SELECT * FROM EMP WHERE ENAME = :name and sal =:salary'; --<--See how bind variables are used
4 str VARCHAR2(200);
5
6 c1 SYS_REFCURSOR;
7
8 emp1 emp%ROWTYPE;
9 BEGIN
10 OPEN c1 FOR cmd1;
11 LOOP
12 FETCH c1 INTO emp1;
13 EXIT WHEN c1%NOTFOUND;
14
15 -- It doesn't work
16 EXECUTE IMMEDIATE cmd2 USING emp1.ename, emp1.sal;
17
18
19 END LOOP;
20 END;
21 /
PL/SQL procedure successfully completed.
SQL>
绑定变量在一个字符串中,因此它们不被视为绑定。
尝试
cmd2 VARCHAR2(200) := q'[BEGIN DBMS_OUTPUT.PUT_LINE(:name || ' has a salary of ' || :salary); END;]';