web-dev-qa-db-ja.com

Oracle PL / SQL文字列比較の問題

1)。こんにちは、皆さんの観点からさびている可能性がある次の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;
 /

これは、2つの文字列str1とstr2が等しくないことは非常に明らかですが、「2つの文字列が等しくない」が出力されなかったのはなぜですか? Oracleには2つの文字列を比較する別の一般的な方法がありますか?

22
C.c

Philが指摘したように、空の文字列は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;
 /

null比較に関して:

NULLSのOracle 12cドキュメント によると、IS NULLまたはIS NOT NULLを使用したnull比較は、TRUEまたはFALSEに評価されます。ただし、他のすべての比較はUNKNOWNnotFALSEに評価されます。ドキュメントにはさらに次のように記載されています。

UNKNOWNと評価される条件は、ほとんどFALSEのように機能します。たとえば、UNKNOWNと評価されるWHERE句に条件を持つSELECTステートメントは行を返しません。ただし、UNKNOWNに評価される条件は、UNKNOWN条件評価の以降の操作がUNKNOWNに評価されるという点でFALSEと異なります。したがって、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

また、空の文字列が常にNULLと評価されると仮定して、PL/SQLを記述すべきではないことも学びました。

Oracle Databaseは現在、長さがゼロの文字値をNULLとして扱います。ただし、これは今後のリリースでは引き続き当てはまらない可能性があるため、空の文字列をNULLと同様に扱わないことをお薦めします。

47
Wolf

ロジックに他のブランチを追加して、コードのギャップを埋めて、何が起こるか見てみましょう。

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>

それで、2つの文字列は同じでも、同じでもありませんか?え?

これが原因です。 Oracleは空の文字列をNULLとして扱います。 NULLと別の文字列を比較しようとすると、結果はTRUEまたはFALSEではなく、NULLになります。これは、他の文字列もNULLであっても当てはまります。

11
APC

=ではなく、<>を使用して文字列を比較します。このコンテキストでは、=<>よりも合理的な方法で機能するようだということがわかりました。 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;
/
2
user272735

「これらの2つの変数の1つがnullの場合、これら2つの変数が同じ値を持たないことをどのように検出する必要がありますか?」というコアの質問を修正するには、nvl(my_column, 'some value that will never, ever, ever appear in the data and I can be absolutely sure of that')のアプローチが好きではありません。 tは、値が表示されないことを常に保証します(特にNUMBERの場合)。

私は次を使用しました:

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

免責事項:私はオラクルのウィザードではありません。自分でこのウィザードを思いついたので、他で見たことがないので、それが悪い考えであるという微妙な理由があるかもしれません。ただし、nullを他の何かと比較するとTRUEまたはFALSEではなく、NULLになるというAPCが言及したトラップを回避します。句(str1 is null)は常にTRUEまたはFALSEを返し、nullを返しません。

(PL/SQLは here と記載されているように、短絡評価を実行することに注意してください。)

1
Andrew Spencer

行を変更するだけstr1:= '';str1:= '';

0
MARIO GARCIA

このテキスト比較のためにストアド関数を作成しました:

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;
0
David Gausmann