ORACLE PL/SQL变量函数范围 - 需要说明

Dan*_*anK 1 sql oracle plsql stored-procedures stored-functions

我刚刚找到一个问题的答案,我遇到的问题是PL/SQL变量没有被函数识别,我希望有人可以向我解释为什么我的解决方案有效以及发生了什么"在幕后".

背景

作为优化项目的一部分,我正在尝试收集存储过程中各个SQL脚本的度量标准.我正在解析的存储过程具有一个In-type日期参数,我需要定义它以运行每个单独的SQL脚本:

CREATE OR REPLACE myStoredProc (DATE_IN DATE, ERROR_OUT OUT VARCHAR2)
IS
BEGIN
    --Truncate Temp Tables
    --6 Individual SQL Scripts
EXCEPTION
    --Error Handling
END;
Run Code Online (Sandbox Code Playgroud)

为了单独运行每个脚本,我决定将每个SQL语句放入PL/SQL块并将DATE_IN参数作为变量提供:

DECLARE
    DATE_IN DATE := TO_DATE('16-JUL-2014','DD-MON-RR'); 
BEGIN
    --Place individual script here
END;
Run Code Online (Sandbox Code Playgroud)

问题

这种方法适用于引用此DATE_IN变量的几个查询,但是一个查询引用了外部函数,该函数DATE_IN作为参数开始抛出ORA-00904错误:

DECLARE  
    DATE_IN DATE := TO_DATE('16-JUL-2014','DD-MON-RR'); 
BEGIN
    insert into temp_table
    SELECT table1.field1,
           table1.field2,
           table2.fieldA,
           MyFunction(table1.field1, DATE_IN) --This was the problem line
      FROM
           table1,
           table2
     WHERE EXISTS (inner query)
       AND table1.keys = table2.keys
       AND table2.date <= DATE_IN
END;   
Run Code Online (Sandbox Code Playgroud)

在另一个开发人员的建议下,我能够通过在DATE_IN我传递给函数的变量前面添加一个冒号(:)来解决这个错误,以便读取问题行MyFunction(table1.field1, :DATE_IN).一旦我这样做,我的错误消失了,我能够毫无问题地运行查询.

我对结果很满意,但是其他开发人员无法解释为什么需要它,只是需要从PL/SQL语句中调用任何函数或其他存储过程.我认为这与范围有关,但我想更好地解释为什么这个冒号对于函数查看变量是必要的.

问题

我试图做一些关于参数,变量,绑定/声明常量的 Oracle文档的研究,但我的研究只给了我更多的问题:

  • 在阅读了变量之后,我现在质疑这是否是我一直使用的正确术语(因为我实际上并没有使用VARIABLE命令而且我正在传递一个日期 - 这不是允许的数据类型).如果我的DATE_IN DATE :=陈述不是变量,那么它是什么?
  • 为什么我的引用的其余部分DATE_IN被编译器识别但是将值传递给函数超出了范围?
  • 冒号(:)到底在做什么?这会把它变成绑定变量吗?

提前致谢.我感谢您提供的任何指导!

- - - - - - - - - - - - - - - - - 编辑 - - - - - - - - -----------------------

我被要求提供更多信息.我的Db版本是11G,11.2.0.2.0.我能够重现此错误的查询如下.

DECLARE  
    EXTRACT_DT_IN DATE := TO_DATE('16-JUL-2014','DD-MON-RR'); 
BEGIN
    --This begins the pre-optimized query that I'm testing
    insert into AELI_COV_TMP_2_OPT
    SELECT /*+ ordered use_nl(CM MAMT) INDEX (CM CSMB_CSMB2_UK) INDEX (MAMT     (MBAM_CSMB_FK_I) */
           CM.CASE_MBR_KEY
          ,CM.pyrl_no
          ,MAMT.AMT
          ,MAMT.FREQ_CD
          ,MAMT.HOURS
          ,aeli$cov_pdtodt(CM.CASE_MBR_KEY, EXTRACT_DT_IN)
      FROM
           CASE_MEMBERS CM
          ,MEMBER_AMOUNTS MAMT
      WHERE EXISTS (select /*+ INDEX(SDEF SLRY_BCAT_FK_I) */
                      'x'
                     from SALARY_DEF SDEF
                    where SDEF.CASE_KEY = CM.CASE_KEY
                      AND SDEF.TYP_CD = '04'
                      AND SDEF.SLRY_KEY = MAMT.SLRY_KEY)
       AND CM.CASE_MBR_KEY = MAMT.CASE_MBR_KEY
       AND MAMT.STAT_CD = '00'
       AND (MAMT.xpir_dt is null or MAMT.xpir_dt > EXTRACT_DT_IN)
       AND MAMT.eff_dt <= EXTRACT_DT_IN;
    --This ends the pre-optimized query that I'm testing
END;   
Run Code Online (Sandbox Code Playgroud)

这是我在尝试在此语句上运行解释计划时遇到的错误.如果我删除对第13行的引用或者EXTRACT_DT_IN在该行上添加冒号(:),我能够通过此错误.

解释计划遇到错误

----------------------编辑2 -------------------

这是aeli $ .cov_pdtodt的函数签名.(出于安全原因,我已经更换了所有者).

CREATE OR REPLACE function __owner__.aeli$cov_pdtodt
(CASE_MBR_KEY_IN IN NUMBER, EXTRACT_EFF_DT_IN DATE)
  RETURN DATE IS
    PDTODT  DATE;
Run Code Online (Sandbox Code Playgroud)

Ale*_*ole 5

只要您执行整个块,您的匿名块就可以了.如果您尝试仅执行insertselect作为独立命令执行,那么它确实会因ORA-00904而失败.

这不是一个范围问题,这是一个背景问题.您试图在SQL上下文中引用PL/SQL变量,这永远不会起作用.

在PL/SQL上下文中,这将起作用:

declare
  some_var dual.dummy%type := 'X';
begin
  insert into some_table
  select dummy from dual where dummy = some_var;
end;
/
Run Code Online (Sandbox Code Playgroud)

...因为插入可以访问PL/SQL some_var.

在SQL上下文中,这将是错误:

select * from dual where dummy = some_var;
Run Code Online (Sandbox Code Playgroud)

...因为它正在寻找一个名为的列SOME_VAR,并且没有一个.

如果你这样做:

select * from dual where dummy = :some_var;
Run Code Online (Sandbox Code Playgroud)

... some_var现在是客户端管理的绑定变量.如果执行该操作,系统将提示您输入绑定值,或者提供非全部变量绑定错误,或者绑定变量未声明或类似错误,具体取决于您的客户端.

如果你只做一个解释计划,例如,用

set auto trace traceonly explain
select * from dual where dummy = :some_var;
Run Code Online (Sandbox Code Playgroud)

...然后,不一定必须填充绑定变量才能计算计划.有些客户可能仍然抱怨并想要一个绑定值,但是解析器可以用它 - 足以产生一个计划.虽然不能利用绑定变量偷看或直方图等.

例如,如果两个引用都转换为绑定变量,只是insert ...选择了块的一部分,并按下解释计划(F10),SQL Developer会愉快地为原始样本查询生成一个计划.