2011-09-02 37 views
18

1).Hello,我有可能會生鏽,從你們的角度以下Oracle PL/SQL代碼:的Oracle PL/SQL字符串比較問題

DECLARE 
str1 varchar2(4000); 
str2 varchar2(4000); 
BEGIN 
    str1:=''; 
    str2:='sdd'; 
    IF(str1<>str2) THEN 
    dbms_output.put_line('The two strings is not equal'); 
    END IF; 
END; 
/

這是很明顯,兩個字符串str1和STR2不是平等的,但爲什麼'兩個字符串不相等'沒有打印出來? Oracle有另外一種常用的方法來比較兩個字符串嗎?

+7

請記住,在Oracle中,一個空字符串相當於'NULL',它可能無法按預期在不等式比較語句中工作 – Phil

+0

您能否給我關於如何解決這個問題的一些建議? –

+2

這是兩個截然不同的問題。你應該問問他們,因爲這是SO的工作原理。話雖如此,你的第二個問題是已經被多次回答的老栗子。請在搜索前詢問:http://stackoverflow.com/questions/tagged/oracle+string-aggregation – APC

回答

40

正如菲爾所說,空字符串被視爲NULL,並且NULL不等於或不等於任何東西。如果你希望空字符串或NULL值,則需要用NVL()來處理這些:

DECLARE 
str1 varchar2(4000); 
str2 varchar2(4000); 
BEGIN 
    str1:=''; 
    str2:='sdd'; 
-- Provide an alternate null value that does not exist in your data: 
    IF(NVL(str1,'X') != NVL(str2,'Y')) THEN 
    dbms_output.put_line('The two strings are not equal'); 
    END IF; 
END; 
/

關於空比較:

按照Oracle 12c documentation on NULLS,使用空比較IS NULLIS NOT NULL做評估爲TRUEFALSE。然而,所有其他比較評估爲UNKNOWN,而不是FALSE。該文檔還指出:

評估爲UNKNOWN的條件行爲幾乎與FALSE相同。例如,WHERE子句中條件爲UNKNOWN的SELECT語句不返回任何行。但是,評估爲UNKNOWN的條件與FALSE的不同之處在於,對未知條件評估的進一步操作將評估爲UNKNOWN。因此,NOT FALSE評估爲TRUE,但NOT UNKNOWN評估爲UNKNOWN。

參考表由Oracle提供:

Condition  Value of A Evaluation 
---------------------------------------- 
a IS NULL  10   FALSE 
a IS NOT NULL 10   TRUE   
a IS NULL  NULL   TRUE 
a IS NOT NULL NULL   FALSE 
a = NULL  10   UNKNOWN 
a != NULL  10   UNKNOWN 
a = NULL  NULL   UNKNOWN 
a != NULL  NULL   UNKNOWN 
a = 10   NULL   UNKNOWN 
a != 10   NULL   UNKNOWN 

我也瞭解到,我們不應該寫PL/SQL假設空字符串將始終評估爲NULL:

Oracle數據庫目前將長度爲零的字符值視爲null。但是,這在未來的版本中可能不會繼續存在,並且Oracle建議您不要將空字符串視爲空值。

+5

雖然實際上正確的定義是,「涉及NULL值的任何比較總是返回FALSE」(即使與另一個NULL值進行比較)。 So: (NULL = NULL)= False AND (NULL <> NULL) = FALSE – Kerbocat

+1

@Kerbocat我在過去幾年一直在思考這個評論。你有沒有參考Oracle如何評估空比較? Oracle [文檔](http://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements005.htm#g194888)我發現狀態爲null的比較實際評估爲「UNKNOWN」。 – Wolf

+0

我現在沒有這方面的參考資料,很可能是我錯了。我發現UNKNOWN作爲一個概念的實際用途很少,儘管如此:(來自您的源)「評估爲UNKNOWN的條件行爲幾乎與FALSE一樣。例如,在WHERE子句中有條件的SELECT語句評估UNKNOWN返回沒有行,但是,評估爲UNKNOWN的條件與FALSE的不同之處在於,對UNKNOWN條件評估的進一步操作評估爲UNKNOWN,因此NOT FALSE評估爲TRUE,但NOT UNKNOWN評估爲UNKNOWN。 – Kerbocat

-5

關於第一個問題:

大概郵件將不能打印出來,因爲你有輸出關閉。使用這些命令來打開它:

set serveroutput on 
exec dbms_output.enable(1000000); 

關於第二個問題:

我PLSQL是相當生疏,所以我不能給你一個完整的片段,但你需要在循環遍歷結果集的SQL查詢和CONCAT所有的字符串在一起。

+0

我已經真正設置了服務器輸出,但它仍然不能顯示 –

2

我比較字符串使用=而不是<>。我發現在這種情況下,=似乎以比<>更合理的方式工作。我已經指定兩個空(或NULL)字符串是相等的。真正的實現返回PL/SQL布爾值,但是在這裏我將其更改爲pls_integer(0爲false且1爲true)以便能夠輕鬆演示該函數。

create or replace function is_equal(a in varchar2, b in varchar2) 
return pls_integer as 
begin 
    if a is null and b is null then 
    return 1; 
    end if; 

    if a = b then 
    return 1; 
    end if; 

    return 0; 
end; 
/
show errors 

begin 
    /* Prints 0 */ 
    dbms_output.put_line(is_equal('AAA', 'BBB')); 
    dbms_output.put_line(is_equal('AAA', null)); 
    dbms_output.put_line(is_equal(null, 'BBB')); 
    dbms_output.put_line(is_equal('AAA', '')); 
    dbms_output.put_line(is_equal('', 'BBB')); 

    /* Prints 1 */ 
    dbms_output.put_line(is_equal(null, null)); 
    dbms_output.put_line(is_equal(null, '')); 
    dbms_output.put_line(is_equal('', '')); 
    dbms_output.put_line(is_equal('AAA', 'AAA')); 
end; 
/
11

讓我們填寫在代碼的間隙,通過在邏輯添加其他分支,並看看會發生什麼:

SQL> DECLARE 
    2 str1 varchar2(4000); 
    3 str2 varchar2(4000); 
    4 BEGIN 
    5  str1:=''; 
    6  str2:='sdd'; 
    7  IF(str1<>str2) THEN 
    8  dbms_output.put_line('The two strings is not equal'); 
    9  ELSIF (str1=str2) THEN 
10  dbms_output.put_line('The two strings are the same'); 
11  ELSE 
12  dbms_output.put_line('Who knows?'); 
13  END IF; 
14 END; 
15/
Who knows? 

PL/SQL procedure successfully completed. 

SQL> 

所以這兩個字符串既不相同,也不是不相同?咦?

歸結到這一點。 Oracle將空字符串視爲NULL。如果我們嘗試比較NULL和另一個字符串,結果不是TRUE也不是FALSE,它是NULL。即使其他字符串也是NULL,情況仍然如此。

1

要解決核心問題「我應該如何檢測這兩個變量在其中一個爲空時沒有相同的值?」,我不喜歡nvl(my_column, 'some value that will never, ever, ever appear in the data and I can be absolutely sure of that')的方法,因爲您不能總是保證價值不會出現......特別是NUMBER。

我已經使用了以下內容:

if (str1 is null) <> (str2 is null) or str1 <> str2 then 
    dbms_output.put_line('not equal'); 
end if; 

免責聲明:我不是一個Oracle嚮導,我想出了這個一個自己並沒有看到其他地方,所以有可能會出現一些微妙的原因,這是一個餿主意。但它確實避免了APC提到的陷阱,即將null與其他值進行比較既不會給出TRUE也不會給出FALSE而是NULL。由於條款(str1 is null)將始終返回TRUE或FALSE,因此不能爲空。

(請注意,PL/SQL執行短路評價,如所指出的here。)

0

我已經創建了一個存儲函數爲這個文本比較目的:

CREATE OR REPLACE FUNCTION TextCompare(vOperand1 IN VARCHAR2, vOperator IN VARCHAR2, vOperand2 IN VARCHAR2) RETURN NUMBER DETERMINISTIC AS 
BEGIN 
    IF vOperator = '=' THEN 
    RETURN CASE WHEN vOperand1 = vOperand2 OR vOperand1 IS NULL AND vOperand2 IS NULL THEN 1 ELSE 0 END; 
    ELSIF vOperator = '<>' THEN 
    RETURN CASE WHEN vOperand1 <> vOperand2 OR (vOperand1 IS NULL) <> (vOperand2 IS NULL) THEN 1 ELSE 0 END; 
    ELSIF vOperator = '<=' THEN 
    RETURN CASE WHEN vOperand1 <= vOperand2 OR vOperand1 IS NULL THEN 1 ELSE 0 END; 
    ELSIF vOperator = '>=' THEN 
    RETURN CASE WHEN vOperand1 >= vOperand2 OR vOperand2 IS NULL THEN 1 ELSE 0 END; 
    ELSIF vOperator = '<' THEN 
    RETURN CASE WHEN vOperand1 < vOperand2 OR vOperand1 IS NULL AND vOperand2 IS NOT NULL THEN 1 ELSE 0 END; 
    ELSIF vOperator = '>' THEN 
    RETURN CASE WHEN vOperand1 > vOperand2 OR vOperand1 IS NOT NULL AND vOperand2 IS NULL THEN 1 ELSE 0 END; 
    ELSIF vOperator = 'LIKE' THEN 
    RETURN CASE WHEN vOperand1 LIKE vOperand2 OR vOperand1 IS NULL AND vOperand2 IS NULL THEN 1 ELSE 0 END; 
    ELSIF vOperator = 'NOT LIKE' THEN 
    RETURN CASE WHEN vOperand1 NOT LIKE vOperand2 OR (vOperand1 IS NULL) <> (vOperand2 IS NULL) THEN 1 ELSE 0 END; 
    ELSE 
    RAISE VALUE_ERROR; 
    END IF; 
END; 

在例如:

SELECT * FROM MyTable WHERE TextCompare(MyTable.a, '>=', MyTable.b) = 1;