17

単純な CASE 式と DECODE 関数は同等であり、それらによって返される結果は同一であるように思われます。彼らは?

ドキュメントには、単純な CASE 式について次のように記載されています。

単純な CASE 式は、selector_value が selector と一致する最初の結果を返します。残りの式は評価されません。セレクターに一致する selector_value がない場合、CASE 式は、存在する場合は else_result を返し、存在しない場合は NULL を返します。

これをDECODE 関数と比較すると、説明は同じように見えます。

DECODE は expr を各検索値と 1 つずつ比較します。expr が検索と等しい場合、Oracle Databaseは対応する結果を戻します。一致するものが見つからない場合、Oracle はデフォルトを返します。デフォルトが省略された場合、Oracle は null を返します。

検索されたCASE 式は単純に等価である可能性があるため、これも同じであると解釈できます。

これら 3 つのステートメントはすべて同じ結果 0 を返すようです。

select case 1 when 2 then null else 0 end as simple_case
     , case when 1 = 2 then null else 0 end as searched_case
     , decode(1, 2, null, 0) as decode
  from dual

単純な CASE 式と DECODE 関数 (特定の状況では、検索された CASE 式) は常に同じ結果を返しますか?

4

3 に答える 3

25

短い答え、いいえ。

少し長い答えはほぼです。

各ステートメントから得られる結果が同一であるように見えるだけです。DUMP関数を使用して返されたデータ型を評価すると、私の言いたいことがわかります。

SQL> select dump(case 1 when 2 then null else 0 end) as simple_case
  2       , dump(case when 1 = 2 then null else 0 end) as searched_case
  3       , dump(decode(1, 2, null, 0)) as decode
  4    from dual;

SIMPLE_CASE        SEARCHED_CASE      DECODE
------------------ ------------------ -----------------
Typ=2 Len=1: 128   Typ=2 Len=1: 128   Typ=1 Len=1: 48

SQL フィドル

DECODE のデータ型が 1 であるのに対し、2 つの CASE ステートメントは 2 のデータ型を「返す」ことがわかります。Oracle のData Type Summaryを使用すると、DECODE は VARCHAR2 (データ型 1) を返しますが、CASE ステートメントは「 " 数字 (データ型 2)。

名前が示すように、DECODE は関数であり、CASE は関数ではないため、これが発生すると思います。これは、内部で異なる方法で実装されていることを意味します。これを証明する実際の方法はありません。

これは実際には何の影響もないと考えるかもしれません。数値にする必要がある場合、Oracle は暗黙的な変換規則に従って暗黙的に文字を数値に変換します。これも正しくありません。データ型が同一でなければならないため、UNION では機能しません。オラクルは、物事を簡単にするために暗黙的な変換を行いません。第二に、暗黙的な変換についてOracleが言っていることは次のとおりです。

次の理由から、暗黙的または自動変換に依存するのではなく、明示的な変換を指定することをお薦めします:

  • 明示的なデータ型変換関数を使用すると、SQL ステートメントが理解しやすくなります。

  • 暗黙的なデータ型変換は、特に列値のデータ型が定数のデータ型に変換される場合、逆ではなく、パフォーマンスに悪影響を与える可能性があります。

  • 暗黙的な変換は、それが発生するコンテキストに依存し、すべての場合で同じように機能するとは限りません。たとえば、日時値から VARCHAR2 値への暗黙的な変換では、 NLS_DATE_FORMAT パラメータの値によっては予期しない年が返される場合があります。

  • 暗黙的な変換のアルゴリズムは、ソフトウェア リリース間および Oracle 製品間で変更される可能性があります。明示的な変換の動作はより予測可能です。

これはきれいなリストではありません。しかし、最後から 2 番目のポイントは、私をうまくデートに導きます。前のクエリを代わりに日付を使用するクエリに変換すると、次のようになります。

select case sysdate when trunc(sysdate) then null 
                    else sysdate 
       end as simple_case
     , case when sysdate = trunc(sysdate) then null 
            else sysdate 
       end as searched_case
     , decode(sysdate, trunc(sysdate), null, sysdate) as decode
  from dual;

もう一度、このクエリで DUMP を使用すると、CASE ステートメントはデータ型 12 の DATE を返します。DECODE はsysdateVARCHAR2 に変換されました。

SQL> select dump(case sysdate when trunc(sysdate) then null
  2                           else sysdate
  3              end) as simple_case
  4       , dump(case when sysdate = trunc(sysdate) then null
  5                   else sysdate
  6              end) as searched_case
  7       , dump(decode(sysdate, trunc(sysdate), null, sysdate)) as decode
  8    from dual;

SIMPLE_CASE          
---------------------------------- 
Typ=12 Len=7: 120,112,12,4,22,18,7 
SEARCHED_CASE
---------------------------------- 
Typ=12 Len=7: 120,112,12,4,22,18,7
DECODE
---------------------------------- 
Typ=1 Len=19: 50,48,49,50,45,49,50,45,48,52,32,50,49,58,49,55,58,48,54

SQL フィドル

セッションNLS_DATE_FORMATを使用して DATE が文字に変換されていることに注意してください(SQL Fiddle内) 。

暗黙的に VARCHAR2 に変換された日付があると、問題が発生する可能性があります。TO_CHARを使用して日付を文字に変換しようとしている場合、予期しない場所でクエリが中断されます。

SQL> select to_char( decode( sysdate
  2                         , trunc(sysdate), null
  3                         , sysdate )
  4                 , 'yyyy-mm-dd') as to_char
  5    from dual;
select to_char( decode( sysdate
                *
ERROR at line 1:
ORA-01722: invalid number

SQL フィドル

同様に、日付演算は機能しなくなりました。

SQL>
SQL>
SQL> select decode(sysdate, trunc(sysdate), null, sysdate) + 1 as decode
  2    from dual;
select decode(sysdate, trunc(sysdate), null, sysdate) + 1 as decode
       *
ERROR at line 1:
ORA-01722: invalid number

SQL フィドル

興味深いことに、DECODE は、可能な結果の 1 つが NULL の場合にのみ、式を VARCHAR2 に変換します。デフォルト値が NULL の場合、これは発生しません。例えば:

SQL> select decode(sysdate, sysdate, sysdate, null) as decode
  2    from dual;

DECODE
-------------------
2012-12-04 21:18:32

SQL> select dump(decode(sysdate, sysdate, sysdate, null)) as decode
  2    from dual;

DECODE
------------------------------------------    
Typ=13 Len=8: 220,7,12,4,21,18,32,0

SQL フィドル

DECODE は 13 のデータ型を返したことに注意してください。

つまり、可能であれば DECODE は避けてください。期待するデータ型が得られるとは限りません。トム・カイトを引用するには:

デコードはやや不明瞭です -- CASE は非常に明確です。デコードで実行しやすいことは CASE で実行しやすく、デコードで実行するのが困難またはほとんど不可能なことは CASE で実行しやすいです。ケース、論理的には、勝者です。


完全にするために、DECODE と CASE には 2 つの機能上の違いがあります。

  1. DECODE は PL/SQL 内では使用できません。
  2. CASE を使用して null を直接比較することはできません

    SQL> select case null when null then null else 1 end as case1
      2        , case when null is null then null else 1 end as case2
      3        , decode(null, null, null, 1) as decode
      4    from dual
      5         ;
    
         CASE1      CASE2 DECODE
    ---------- ---------- ------
             1
    

    SQL フィドル

于 2012-12-04T22:04:02.377 に答える
13

Ben は、DECODE と CASE の違いについて長い回答を書いています。彼は、DECODE と CASE が明らかに同じ値のセットに対して異なるデータ型を返す可能性があることを示していますが、これが起こる理由を適切に説明していません。

DECODE() は非常に規範的です。常に最初の結果パラメーターのデータ型です。Oracle は、他のすべての結果パラメータに暗黙的な変換を適用します。(たとえば)最初の結果パラメーターが数値で、デフォルト値が日付の場合、エラーがスローされます。

ORA-00932: inconsistent datatypes: expected NUMBER got DATE

これについては、ドキュメントで説明されています:詳しくはこちら をご覧ください

最初のシナリオでは、最初の結果パラメータは NULL であり、Oracle はこれを VARCHAR2 として扱うことを決定します。最初の結果パラメーターが数値でデフォルト値が null になるように変更すると、DECODE() ステートメントは NUMBER を返します。DUMP() は、これがそうであることを証明します。

一方、CASE はすべての戻り値が同じデータ型を持つことを主張し、そうでない場合はコンパイル エラーをスローします。暗黙的な変換は適用されません。これはドキュメントにも記載されています。ここでそれを読んでください

違いはこれに尽きます。次の DECODE ステートメントは実行されますが、CASE ステートメントは実行されません。

select decode(1, 1, 1, '1') from dual;

select case 1 when 1 then 1 else '1' end from dual;

必須の SQL フィドル。

于 2012-12-05T04:07:22.010 に答える
-3

遅すぎることはわかっていますが、誰かがそれを検索するとうまくいけば役立つので、ここに投稿します。私は同じためにMsSqlスクリプトを作成しました-

Declare @Var varchar(399)='DECODE(MyColumnName,''A'',''Auto'',''M'',''Manual'')'

Begin
Declare @Count int, @Counter int=1
Declare @TempTable table (ID int identity(1,1),Items varchar(500))
Declare @SqlText varchar(max)
Select @Var=Replace(Replace(@Var,'DECODE(',''),')','')

Insert Into @TempTable
Select * FROM [dbo].[Split] ( @Var ,',')
--Select * from @TempTable
Select @Count=Count(ID) from @TempTable

While(@Counter<=@Count)
Begin
    If(@Counter=1)
    Begin
    Select @SqlText='Case ' +Items from @TempTable Where ID=1
    End

    Else If(@Counter=@Count)
    Begin
    Select @SqlText+=' Then ' +Items +' End' from @TempTable Where ID=@Counter 
    End

    Else If(@Counter%2=0)
    Begin
    Select @SqlText +=' When ' +Items from @TempTable Where ID=@Counter
    End

    Else If(@Counter%2=1)
    Begin
    Select @SqlText +=' Then ' +Items from @TempTable Where ID=@Counter
    End

    Set @Counter+=1
End

Select @SqlText SqlServerCaseStatement
End

上記のスクリプトで Split 関数を使用しました。その関数が必要な場合は、Romil の回答 - How to split a comma-separated value to columns を参照できます。

于 2014-05-27T11:49:50.917 に答える