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

时间:2014-07-18 16:12:08

标签: sql oracle stored-procedures plsql 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;

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

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

问题

这种方法适用于引用此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;   

解决方案

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

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

问题

我试图对有关参数的variablesbinding/declaringconstants的Oracle文档进行一些研究,但我的研究只给了我更多问题:

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

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

---------------------------------- EDIT --------- -----------------------------

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

DATE_IN

以下是我在尝试对此声明运行解释计划时遇到的错误。如果我删除对第13行的引用或者在该行的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; 添加冒号(:),我就可以通过此错误。

Error Encountered on Explain Plan

----------------------编辑2 ------------------- < /强>

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

EXTRACT_DT_IN

2 个答案:

答案 0 :(得分:5)

只要您执行整个块,您的匿名块就可以了。如果您尝试仅执行insert或其select作为独立命令,那么它确实会因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;
/

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

在SQL上下文中,这会出错:

select * from dual where dummy = some_var;

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

如果你这样做:

select * from dual where dummy = :some_var;

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

如果您只做了解释计划,例如与

set auto trace traceonly explain
select * from dual where dummy = :some_var;

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

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

答案 1 :(得分:2)

我不确定你读了什么,但你在这里混淆了几件事。

您的DATE_IN是一个变量。您无需在任何地方键入'VARIABLE'来声明变量,您只需要变量的名称和数据类型。 以下所有内容都是PL / SQL中的合法变量(尽管命名不佳)。

variable_1 NUMBER;
variable_2 VARCHAR2(100);
variable_3 DATE;

如果没有看到这一切,很难说出你在代码中做了什么。您是否在同一个块中声明了两个DATE_IN变量? DATE_IN是表格中列的名称吗?

如果table1或table2中有一个名为DATE_IN的列,那可能就是您的问题。 Oracle不知道您是否要使用您的变量或列,并且它始终默认为列名。您的函数将期待DATE并接收列,因此错误。