2014-03-24 38 views
0

我對PL/SQL函數有問題。我試圖得到一個結果,這取決於聲明中的限制。如果沒有結果匹配,結果應該爲空。Oracle函數結果不同於select語句的結果

如果我直接在表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。我第一次嘗試計數,但對於調試,我將結果更改爲id。

謝謝你的幫助。

+1

用示例數據構建一個小例子,那麼我們將能夠提供幫助! –

+0

你是否從函數中取出EXACT語句並單獨執行它?要麼你的輸入是不同的,要麼sql是不同的。 – OldProgrammer

回答

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 
. . . 
2

您已經將函數的參數命名爲與表中的列相同的參數。這幾乎肯定是一個壞主意,可能是你的bug的來源。

在你的內心查詢,當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_history每一行,除非任何veh_idstatus_date爲NULL。

這就是爲什麼大多數人採用區分參數,局部變量和列名稱的標準命名約定。像

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