Oracle函数结果与select语句的结果不同

时间:2014-03-24 15:53:43

标签: sql oracle function exists

我的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。

感谢您的帮助。

2 个答案:

答案 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_idstatus_date已解析为status_history表中的列,而不是同名参数。这有效地将这些谓词变为

WHERE sh2.veh_id = sh2.veh_id 
  AND sh2.status_date <= sh2.status_date

,除了status_historyveh_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