我刚刚回答了一个问题的答案,我遇到的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语句中调用任何函数或其他存储过程。我认为这与范围有关,但我想更好地解释为什么这个冒号是函数查看变量所必需的。
问题
我试图对有关参数的variables,binding/declaring和constants的Oracle文档进行一些研究,但我的研究只给了我更多问题:
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;
添加冒号(:),我就可以通过此错误。
----------------------编辑2 ------------------- < /强>
这是aeli $ .cov_pdtodt的函数签名。 (出于安全原因,我已经更换了所有者)。
EXTRACT_DT_IN
答案 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并接收列,因此错误。