为 EXECUTE IMMEDIATE 解析 PL/SQL 语句中的占位符

Posted

技术标签:

【中文标题】为 EXECUTE IMMEDIATE 解析 PL/SQL 语句中的占位符【英文标题】:Parsing placeholders in PL/SQL statement for EXECUTE IMMEDIATE 【发布时间】:2018-01-12 11:11:48 【问题描述】:

我刚刚开始学习这门语言。 我试图使用一个包含 PL/SQL 块的字符串和一个占位符字符串,该字符串有两个字段,我想用从 SELECT 语句中检索到的一些数据替换它们。

我已正确创建并填充了表 employees

问题是我需要“替换”那些占位符(变量cmd2 中的:name:salary)但是当我EXECUTE IMMEDIATE 使用检索到的值时出现此错误:ORA-01006: bind variable does not exist

这是代码sn-p:

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

【问题讨论】:

即使您接受了理想情况下不适合您的问题的答案,我还是想说,绑定变量应用程序仅适用于 DBMS_OUTPUT 语句。 @XING 为您的答案添加了引用。 如果您接受任何可以以某种方式解决您的问题但可能会误导概念点的答案,那么问题的重要性就会降低。如果您添加任何引号无关紧要,因为这样的人看的是答案而不是问题 "绑定变量仅在 PLSQL 块内的 SQL 语句中使用。"虽然是错误的,但它们也用于过程调用以传递参数 - 请参阅许多示例 here in Oracle docs。而DBMS_OUTPUT.PUT_LINE 是一个过程调用,就像其他的一样! 在动态过程调用中在串联字符串中使用绑定变量可能是不寻常的,但它完全有效(如果必须的话),所以我认为警告是不正确的。 【参考方案1】:

绑定变量在字符串中,因此它们不被视为绑定。

试试

cmd2 VARCHAR2(200) := q'[BEGIN DBMS_OUTPUT.PUT_LINE(:name || ' has a salary of ' || :salary); END;]';

【讨论】:

发帖前有没有试过。它是否适用于您的情况。这将引发错误, 我试过了,但在这里输入时有错字 - 应该是 q'[【参考方案2】:

问题在于您的 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 不可用时才真正使用 - 例如。因为表名、列名或过程名不固定。请参阅here in the Oracle docs 的一些示例。

【讨论】:

不幸的是,我不得不在阅读您的评论后投反对票,即使您没有很好地解释为什么会提出问题ORA-01008: not all variables bound,我也可以做出这样的决定。不管它实际上并没有为 OP 引发该错误。 以牙还牙! :-) 不..我的意思是..我的内容丰富。如果他收到错误ORA-01008: not all variables bound,当您说 OP 时,您的回答绝对是错误的。另一方面,您的反对票并不能有效地证明原因。只是按照你选择投反对票的话,这对我来说至少没有意义。 好吧,如果你愿意,那就做个聪明人吧,它并没有给我留下太多印象。 OP 非常清楚他们想要进行第一个不出错的调用,而不是第二个出错的调用。但是我会更新我的答案,以便您可以删除您的反对票! 当你和@xing 争论绑定变量的正确使用时,其他人却用几乎最稀少的答案在Accepted 勾选了。 :) 我想起了我曾经看过的关于锹形甲虫的自然纪录片。【参考方案3】:

绑定变量最好用在PLSQL 块内的SQL 语句中。你不应该在DBMS_OUTPUT 语句中绑定变量。

你的情况

cmd2 VARCHAR2(200) := 'BEGIN DBMS_OUTPUT.PUT_LINE('':name 有薪水 :薪水;''); 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> 

【讨论】:

@ishando q'[BEGIN DBMS_OUTPUT.PUT_LINE(:name || ' has a salary of ' || :salary); END;]'; 的解决方案效果很好! @RinoDrummer 我知道在他更正之后应该可以工作,但绑定变量的应用不正确。 谢谢。我会避免使用这种情况! 我正要这样做!我投反对票是因为您的回答说“不要这样做”,而不是展示如何做到这一点(以符合 OP 的要求)。没有规则说您不能或不应该将变量绑定到动态 PL/SQL 块中的 DBMS_OUTPUT 语句中,只要您的语法正确!你是否真的想在现实生活中是另一回事...... “绑定变量仅在 PLSQL 块内的 SQL 语句中使用” 不正确。它们可能是最佳通常仅在SQL语句中使用,但没有限制。

以上是关于为 EXECUTE IMMEDIATE 解析 PL/SQL 语句中的占位符的主要内容,如果未能解决你的问题,请参考以下文章

Oracle 11 PL SQL 使用 Execute Immediate 为变量赋值

PL/SQL里 execute immediate的用法 谁给解释下

在 PL\SQL 块中使用 EXECUTE IMMEDIATE

EXECUTE IMMEDIATE PL/SQL 块返回类型

ORACLE EXECUTE IMMEDIATE 小结

Oracle中Execute Immediate用法