CASE语句和DECODE是否相同?

看起来像简单的CASEexpression式和DECODE函数是等价的,它们返回的结果应该是相同的。 他们?

该文档有以下关于简单的CASEexpression式的说法:

简单的CASEexpression式返回selector_value匹配select器的第一个结果。 剩余的expression式不被评估。 如果没有selector_value匹配select器,则CASEexpression式返回else_result(如果存在),否则返回NULL。

将其与DECODE函数进行比较,说明似乎是相同的。

DECODE将expr与每个search值逐一进行比较。 如果expr等于search,则Oracle数据库返回相应的结果。 如果找不到匹配,则Oracle返回默认值。 如果省略默认值,那么Oracle返回null。

由于search到的CASEexpression式可以等价于简单的,所以这可以被解释为相同。

这三个陈述似乎都返回相同的结果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 

做简单的CASEexpression式和DECODE函数(在特定情况下search的CASEexpression式)总是返回相同的结果吗?

Ben对DECODE和CASE之间的区别写了一个冗长的回答。 他演示了DECODE和CASE可能会返回不同的数据types,显然是相同的一组值,没有正确解释为什么会发生这种情况。

DECODE()是相当规范的:它总是第一个结果参数的数据types。 Oracle将隐式转换应用于所有其他结果参数。 如果(比如说)第一个结果参数是数字,并且默认值是一个date,它会抛出一个错误。

 ORA-00932: inconsistent datatypes: expected NUMBER got DATE 

这是在文档中描述: 了解更多 。

在第一种情况下,第一个结果参数是NULL,Oracle决定将其视为VARCHAR2。 如果我们改变它,以便第一个结果参数是数字,并且默认值为空,则DECODE()语句将返回一个NUMBER; DUMP()certificate了这一点。

鉴于CASE坚持所有返回的值都具有相同的数据types,如果不是这种情况,将会抛出一个编译错误。 它不会应用隐式转换。 这也在文档中介绍。 在这里阅读 。

差异归结为此。 下面的DECODE语句将运行,CASE语句不会:

 select decode(1, 1, 1, '1') from dual; select case 1 when 1 then 1 else '1' end from dual; 

必须的SQL小提琴。

简短的回答,没有。

稍微长一点的答案差不多。

只是从每个陈述得出的结果看起来是一样的。 如果我们使用DUMP函数来评估返回的数据types,您将会看到我的意思:

 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的数据types为1,而两个CASE语句“返回”数据types2.使用Oracle的数据types摘要 ,DECODE返回一个VARCHAR2(数据types1),而CASE语句是“返回“数字(数据types2)。

我认为这是因为,正如名称所示,DECODE是一个函数 ,CASE不是,这意味着它们在内部已经被实现了。 没有办法certificate这一点。

你可能会认为这并不影响任何事情。 如果你需要它是一个数字,Oracle会隐式地把这个字符转换成隐式转换规则下的一个数字,对吗? 这也不是真的,因为数据types必须相同,所以它不能在UNION中工作 。 Oracle不会做任何隐式转换,以使您的工作变得简单。 其次,这里是甲骨文关于隐式转换的说法:

Oraclebuild议您指定显式转换,而不是依靠隐式转换或自动转换,原因如下:

  • 当使用显式数据types转换函数时,SQL语句更容易理解。

  • 隐式数据types转换可能会对性能产生负面影响,特别是如果将列值的数据types转换为常量而不是其他方式。

  • 隐式转换取决于它发生的上下文,并且在每种情况下可能不会以相同的方式工作。 例如,从date时间值到VARCHAR2值的隐式转换可能会返回意外的一年,具体取决于NLS_DATE_FORMAT参数的值。

  • 隐式转换的algorithm可能会在软件版本和Oracle产品之间发生变化。 显式转换的行为更可预测。

这不是一个漂亮的名单, 但是倒数第二点让我很好地看到date。 如果我们把前面的查询转换成一个使用date的方法:

 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语句返回数据types12,一个DATE。 DECODE已经将sysdate转换为VARCHAR2。

 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小提琴

请注意(在SQL小提琴中)DATE已经使用会话NLS_DATE_FORMAT转换为字符。

将date隐式转换为VARCHAR2可能会导致问题。 如果您打算使用TO_CHAR ,要将date转换为字符,您的查询将打破您不期待的地方。

 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小提琴

同样,date算术不再起作用:

 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小提琴

有趣的是,如果其中一个可能的结果是NULL,DECODE只会将expression式转换为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的数据types。这是没有logging,但是,我认为,作为date算术等工作的datetypes。

总之,如果可能的话可以避免DECODE; 你可能不一定会得到你所期望的数据types。 引用Tom Kyte的话 :

解码有点难懂 – CASE非常清晰。 在CASE中易于执行的事情在CASE中很容易实现,在CASE中很难做到与解码很难或几乎不可能做的事情。 CASE,逻辑明智,赢得双手。


要完成,DECODE和CASE之间有两个function差异。

  1. DECODE不能在PL / SQL中使用。
  2. CASE不能用来直接比较空值

     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小提琴

我知道我为时已晚,但在这里张贴,因为如果有人search,希望它可以帮助。 我创build了一个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 

我在上面的脚本中使用了拆分function,如果您需要该function,您可以参考罗米尔的答案 – 如何将逗号分隔的值拆分为列