似乎简单的CASE表达式和DECODE函数是等价的,并且它们返回的结果应该是相同的。是吗?
文档中有关于simple CASE expression:
的以下内容简单的CASE表达式返回第一个结果 selector_value匹配选择器。剩下的表达式不是 评估。如果没有selector_value与selector匹配,则表示CASE表达式 如果存在则返回else_result,否则返回NULL。
将此与DECODE function相比较,描述似乎相同。
DECODE逐个将expr与每个搜索值进行比较。如果expr相等 搜索,然后Oracle数据库返回相应的结果。如果 找不到匹配项,然后Oracle返回默认值。如果省略default, 然后Oracle返回null。
由于searched CASE expression可以等同于简单,因此可以理解为相同。
这三个陈述似乎都返回相同的结果,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表达式)是否总是返回相同的结果?
答案 0 :(得分:24)
简短回答,不。
接近稍长的答案。
只有出现从每个语句获得的结果是相同的。如果我们使用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
您可以看到DECODE的数据类型是1,而两个CASE语句“返回”数据类型2.使用Oracle的Data Type Summary,DECODE返回VARCHAR2(数据类型1),而CASE语句是“返回”数字(数据类型2)。
我认为这是因为,正如名称所示,DECODE是函数而CASE不是,这意味着它们在内部以不同的方式实现。没有真正的方法来证明这一点。
你可能认为这并没有真正影响任何事情。如果你需要它是一个数字,Oracle将隐式地将字符转换为implicit conversion rules下的数字,对吧?这也不是真的,它是won't work in a UNION,因为数据类型具有相同; Oracle不会进行任何隐式转换,以便您轻松完成任务。其次,这是Oracle关于隐式转换的说法:
Oracle建议您指定显式转换,而不是依赖隐式或自动转换,原因如下:
使用显式数据类型转换函数时,SQL语句更容易理解。
隐式数据类型转换可能会对性能产生负面影响,尤其是当列值的数据类型转换为常量的数据类型而不是相反时。
隐式转换取决于它发生的上下文,并且在每种情况下可能无法以相同的方式工作。例如,从datetime值到VARCHAR2值的隐式转换可能会返回意外的年份,具体取决于NLS_DATE_FORMAT的值 参数。
隐式转换的算法可能会在软件版本和Oracle产品之间发生变化。显式转换的行为更具可预测性。
这不是一个漂亮的清单;但倒数第二点让我很好地约会。如果我们采用上一个查询并将其转换为使用日期的查询:
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已将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 Fiddle中)使用会话NLS_DATE_FORMAT将DATE转换为字符。
将日期隐式转换为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> 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
有趣的是,如果其中一个可能的结果为NULL,则DECODE仅将表达式转换为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
请注意,DECODE返回的数据类型为13.这没有记录,但我认为是日期算术等的日期类型。
简而言之,如果可能,请避免使用DECODE;您可能不一定得到您期望的数据类型。致quote Tom Kyte:
解码有点模糊 - CASE非常清楚。事情是 在CASE中很容易做到解码很容易做的事情 在CASE中很容易做到与解码几乎无关。 CASE,逻辑 明智的,赢得胜利。
为了完成,DECODE和CASE之间存在两个功能差异。
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
答案 1 :(得分:13)
Ben对DECODE和CASE之间的差异写了一个冗长的答案。他演示了DECODE和CASE可能会为显然相同的值集返回不同的数据类型,而没有正确解释为什么会发生这种情况。
DECODE()非常规范:它始终是第一个结果参数的数据类型。 Oracle将隐式转换应用于所有其他结果参数。如果(例如)第一个结果参数是数字且默认值是日期,则会抛出错误。
ORA-00932: inconsistent datatypes: expected NUMBER got DATE
文档中描述了这一点:find out more。
在第一个场景中,第一个结果参数为NULL,Oracle决定将其视为VARCHAR2。如果我们更改它以使第一个结果参数为数字且默认值为null,则DECODE()语句将返回NUMBER; DUMP()证明了这一点。
而CASE坚持所有返回的值都具有相同的数据类型,如果不是这样,则会抛出编译错误。它不会应用隐式转换。这也包含在文档中。 Read it here
差异归结为此。将运行以下DECODE语句,CASE语句将不会:
select decode(1, 1, 1, '1') from dual;
select case 1 when 1 then 1 else '1' end from dual;
答案 2 :(得分:-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