【发布时间】:2014-07-18 16:12:07
【问题描述】:
我刚刚解决了一个问题的答案,我遇到了一个函数无法识别 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 变量前面添加一个冒号 (:) 来解决此错误,以便问题行读取为 MyFunction(table1.field1, :DATE_IN)。一旦我这样做了,我的错误就消失了,我能够毫无问题地运行查询。
我对结果很满意,但其他开发人员无法解释为什么需要它,只是需要从 PL/SQL 语句调用任何函数或其他存储过程。我认为这与范围有关,但我想更好地解释为什么这个冒号对于函数查看变量是必需的。
问题
我曾尝试对 Oracle 参数文档进行一些研究,variables、binding/declaring 和 constants,但我的研究只给了我更多问题:
- 阅读完变量后,我现在质疑这是否是我一直在使用的正确术语(因为我实际上没有使用 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;
这是我在尝试对此语句运行解释计划时遇到的错误。如果我删除对第 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;
【问题讨论】:
-
假设您插入的末尾有一个分号,那么您所拥有的应该可以工作。如果您尚未声明它,则引用
:date_in应该会出错(或提示),例如与variable。似乎在 11gR2 中工作正常;您使用的是什么版本,以及哪个客户端(和版本)?不知道为什么您认为这是范围问题,因为您没有子块或重复名称。 -
恐怕 OP 中的最小示例并不能说明问题。见How to create a Minimal, Complete, and Verifiable example。
-
我无法在 10.2.0.5 (SQL*Plus)、11.2.0.2 (SQL Fiddle) 或 11.2.0.3 (SQL Developer) 中重新创建此行为。你说的没有道理,除非你能创建一个可重现的例子,否则我们还不清楚我们要解释什么
-
一个简单的解释就是那个 SQL 插入语句会得到 ORA-00904;你不能运行整个匿名块的解释计划。您的问题表明您在执行匿名块时遇到错误,不是这样吗 - 您只能让它独立运行插入(或选择)部分,无论是作为命令还是解释计划?
-
看起来错误来自尝试对查询运行解释计划(而不是尝试执行整个匿名块)。由于解释计划仅适用于插入语句,因此它无法访问变量声明,这就是您收到错误的原因。当你把冒号放在变量名前面时,你就是把它变成了一个绑定变量,这就是它起作用的原因。
标签: sql oracle stored-procedures plsql stored-functions