我的PL / SQL函数有问题。我尝试获得单个结果,具体取决于语句中的限制。如果没有结果匹配,则结果应为null。
如果我直接在表status_history上执行select语句,我会得到正确的结果。如果我执行该函数,我会得到一个不属于veh_id范围的id。它是表中的另一个id。所以我得到了一个结果,但我预计没有结果。
这是功能:
CREATE OR REPLACE FUNCTION is_inventory (
veh_id NUMBER,
status_date DATE) RETURN NUMBER IS
cnt number;
BEGIN
SELECT sh.id
INTO cnt
FROM status_history sh
WHERE sh.veh_id = veh_id
AND sh.code >= 100
AND sh.code < 200
AND sh.id =
(SELECT id
FROM ( SELECT sh2.id, sh2.status_date, sh2.code
FROM status_history sh2
WHERE sh2.veh_id = veh_id
AND sh2.status_date <= status_date
ORDER BY sh2.status_date DESC, sh2.code DESC)
WHERE ROWNUM = 1);
if cnt > 0 THEN
RETURN cnt;
else
RETURN cnt;
END IF;
END is_inventory;
/
也许函数可以编写得更好,但我想如果我直接执行函数或SQL语句,我应该得到相同的结果。
在generell中,函数的结果不应该返回id。我首先尝试了计数,但对于debuggin,我将结果更改为id。
感谢您的帮助。
答案 0 :(得分:6)
以下where
子句没有达到预期效果:
WHERE sh2.veh_id = veh_id AND sh2.status_date <= status_date
正在做:
WHERE sh2.veh_id = sh2.veh_id AND sh2.status_date <= sh2.status_date
不插入变量。这是因为SQL的作用域规则首先在查找变量名之前查找匹配的列名。正确的解决方法是在参数和变量前加上一些区别于列名的东西。
CREATE OR REPLACE FUNCTION is_inventory (
v_veh_id NUMBER,
v_status_date DATE) RETURN NUMBER IS
v_cnt number;
BEGIN
. . .
答案 1 :(得分:2)
您已将功能的参数命名为与表格中的列相同。这几乎肯定是一个坏主意,可能是你的错误来源。
在最里面的查询中,当Oracle看到
时WHERE sh2.veh_id = veh_id
AND sh2.status_date <= status_date
veh_id
和status_date
已解析为status_history
表中的列,而不是同名参数。这有效地将这些谓词变为
WHERE sh2.veh_id = sh2.veh_id
AND sh2.status_date <= sh2.status_date
,除了status_history
或veh_id
为NULL之外,status_date
中的每一行都是如此。
这就是大多数人采用标准命名约定来区分参数,局部变量和列名的原因。像
这样的东西CREATE OR REPLACE FUNCTION is_inventory (
p_veh_id NUMBER,
p_status_date DATE)
然后将您的查询更改为
WHERE sh2.veh_id = p_veh_id
AND sh2.status_date <= p_status_date
或者,您可以完全限定查询中的参数名称,但这种情况不太常见,我发现它更容易出错(尽管其他人不同意)
WHERE sh2.veh_id = is_inventory.veh_id
AND sh2.status_date <= is_inventory.status_date