2016-10-19 34 views
4

我剛剛發現,如果您有引發TOO_MANY_ROWS異常的SELECT INTO,則該變量仍將從查詢檢索到的第一條記錄中獲取值。那是預期的行爲?TOO_MANY_ROWS引發,但變量仍然獲得值

這裏是我的例子:

for co in my_cursor loop 
     l_sco_db_id := null; 

    begin 
     select db_id 
     into l_sco_db_id 
     from objects_tab 
     where object_name = co.object_name; 

    exception 
     when no_data_found then 
     dbms_output.put_line('No objects_tab record found for Object ' || co.object_name); 
     when too_many_rows then 
     dbms_output.put_line('Multiple objects_tab records found for Object ' || co.object_name); 
     l_sco_db_id := null; 
    end; 
end loop; 

這是一個循環裏面,所以我設置變量空之初,以確保它是空白,但我不得不再次明確地做在WHEN TOO_MANY_ROWS例外,我沒有想到。我的同事(至少,那些即時聽到的)沒有一個人期望變量具有價值。

+0

不知何故,它並沒有讓我吃驚。 Oracle在處理第一行時很高興。遇到第二行時會引發錯誤。 –

+2

它令我感到驚訝。人們會期望(我認爲)查詢將被運行,行將被計數,並且只有在該值恰好分配給一行時。從您描述的第一行開始讀取,將值分配給您的變量,然後運行時找到更多行時發出抱怨。這看起來很奇怪。 – mathguy

+0

是的,它讓我感到驚訝@mathguy – pahariayogi

回答

3

這是預期的行爲,因爲當你理解封面下面發生的事情時,它是有道理的。但當你第一次看到它時,這絕對是一種看起來很奇怪的行爲。從技術上講,這種行爲被記錄爲未定義的,因此不應該依賴並且可能在將來改變。

在內部,一個select into僅僅是

  • 打開光標語法糖
  • 取從光標一行到目標變量
  • 拋出一個no_data_found異常,如果沒有行已抓取
  • 嘗試從遊標中獲取第二行,如果第二次獲取成功,則會拋出異常too_many_rows

鑑於這種情況,目標變量會被第一次讀取寫入是有意義的。對於select into statement Oracle文檔,但是,說

PL/SQL就會拋出預定義異常TOO_MANY_ROWS和 的INTO子句中的變量是不確定的值。

因此,Oracle可以自由地將值保持不變或讓變量具有第一行或第二行的值或實際上其他任何值。而且你不應該編寫依賴於任何特定行爲的代碼。

作爲示例,如果您查看this blog post from Jeff Kemp,則該變量將採用取自第一行的值。但是,如果你犯了一個小調整到傑夫的代碼,以便您獲取到一個局部變量

CREATE or replace PROCEDURE proc2 (v OUT NUMBER) IS 
    l_v integer; 
BEGIN 
    SELECT 1 INTO l_v FROM all_objects; 
EXCEPTION 
    WHEN TOO_MANY_ROWS THEN 
     dbms_output.put_line 
     ('TOO MANY ROWS: v=' 
      || l_v); 
     v := l_v; 
END; 
/

則行爲變化和值沒有出現被覆蓋。

DECLARE 
    v NUMBER; 
BEGIN 
    proc2(v); 
    dbms_output.put_line('AFTER: v=' || v); 
END; 
/
0

良好的觀察。但是,我們完全可以按照下列提示,避免異常處理(包括NO_DATA_FOUND和two_many_rows):

  • 選擇聚合函數。根據定義,集合函數 (AVERAGE,COUNT,MIN,MAX和SUM)僅返回一行。 即使它的WHERE子句沒有匹配的行,那些行 的COUNT將返回一行 - 答案爲「0」。通常以這種方式選擇表格的MIN或MAX來確定處理限制。

  • 使用ROWNUM限制查詢。

+0

定義的值 - 值爲真,但異常處理對於提供有關數據問題的反饋非常有用。 – DCookie

+0

我覺得這個建議非常糟糕。該代碼特別期望僅在具有單行的表上工作。如果找到多於一行,它應該拋出異常,處理或以其他方式處理;它不應該採取最大(或隨機行)和**假裝**,沒有什麼好笑的事情發生。我相信世界上很多錯誤的數據完全是由於這種「例外規避」。 – mathguy

+0

不用說在這裏處理'異常'的作用。在給定的情況下,所有事情都是有原因的,有用的/沒有用的。我不同意「推薦是非常糟糕」的評論。當你知道two_may_rows可能發生時,這當然是最好的。 – pahariayogi

相關問題