在CASE

时间:2018-11-26 20:42:27

标签: postgresql casting case

我找到了一种使用ROW值来处理多变量case语句的方法。看起来很干净很不错...

但是,直接将由text类型的2个表列组成的行值与由字符串文字组成的行进行比较时,会出现错误。

我在功能spTup('Deposit', '' )中使用了一种解决方法,该方法可以正常工作,但速度可能较慢。另一种可行的方法是将字符串文字显式转换为text,但这会造成很多视觉混乱。

问题:

  1. 为什么Postgres不能推断出字符串文字应该被对待 是text类型?
  2. 为什么Postgres可以推断出单个元素行中的字符串文字类型,而不是具有2个元素的行中的字符串文本类型?
  3. 我以为我可以使用Postgres类型处理,但是我不太了解这种情况,有人可以解释吗?
  4. 还有其他方法可以最大程度地减少视觉混乱吗?

我在本地主机上使用Postgres 10.1,在测试和生产服务器中使用9.6.6。

测试设置:

create table if not exists tblTest ( SeqID serial, EventType text, EventResult text, Amt decimal );
truncate table tblTest;
insert into tblTest( EventType, EventResult, Amt )
values ( 'Withdrawal', '', 1.11 ), ('Deposit', '', 2.22 ), ('Deposit', 'succeeded', 3.33 ), ('Deposit', 'failed', 4.44 );

create or replace function spTup( p_1 text, p_2 text ) 
returns record as $func$
    select ( p_1, p_2 );
$func$ LANGUAGE sql IMMUTABLE; 


-- Runs without error (using single element tuple)
select SeqID, EventType, case ( EventType ) when ( 'Deposit' ) then Amt else 9.999 end
from tblTest;

-- ERROR: cannot compare dissimilar column types text and unknown at record column 1
select SeqID, EventType, EventResult, case ( EventType, EventResult ) 
when ( 'Deposit', '' ) then Amt else 9.999 end
from tblTest;

-- Runs without error -- visually the cleanest apart from using spTup function
select SeqID, EventType, EventResult, case ( EventType, EventResult )::text 
when ( 'Deposit', '' )::text then Amt else 9.999 end
from tblTest;

-- Runs without error
select SeqID, EventType, EventResult, case ( EventType, EventResult ) 
when ( 'Deposit'::text, ''::text ) then Amt else 9.999 end
from tblTest;

select SeqID, EventType, EventResult, case ( EventType, EventResult ) 
when spTup( 'Deposit', '' ) then Amt else 9.999 end
from tblTest;

-- ERROR: input of anonymous composite types is not implemented
select SeqID, EventType, EventResult, case ( EventType, EventResult ) 
when '( "Deposit", "" )' then Amt else 9.999 end
from tblTest;

-- Just out of interest
select ( 'Deposit', '' ), ( 'Deposit'::text, ''::text );
/**
    row             row
    (Deposit,"")    (Deposit,"")
**/

select SeqID, EventType, EventResult, ( EventType, EventResult )
from tblTest;
/** 
    seqid   eventtype   eventresult     row
    1       Withdrawal                  (Withdrawal,"")
    2       Deposit                     (Deposit,"")
    3       Deposit     succeeded       (Deposit,succeeded)
    4       Deposit     failed          (Deposit,failed)
**/

1 个答案:

答案 0 :(得分:1)

这似乎是对您使用的“简单”或“切换” CASE 的限制。
CASE的另一种语法变体无需显式强制转换即可使用:

select SeqID, EventType, EventResult
     , CASE WHEN (EventType, EventResult) = ('Deposit', '') THEN amt ELSE 9.999 END
from tblTest;

只要您要测试一个案例,该变体甚至会“最小化视觉混乱” 。另外两个字符,但更易于阅读(IMHO)。不过,对于多种情况,“切换”变体似乎更可取。

不同的行为显然是由“简单” CASE中不同的工作流导致的。 The manual:

  

计算第一个 expression ,然后与每个 value 进行比较强>   WHEN子句中的表达式,直到找到一个相等的表达式为止。

简单的表达式-值比较的代码路径尝试减少解析数据类型的难度-对于匿名row values失败。感觉像实施中的缺点。人们可能会期望两种变体具有相同的行为-并提交错误报告。

但是至少从Postgres 8.4起,行为就一直是这样(与第11页相同):

db <>提琴here

到目前为止,几乎没有多少人在开关CASE中具有未键入行值的类似想法。


留下您的问题:

  
      
  1. 为什么Postgres可以推断出单个元素行中的字符串文字类型,而不是具有2个元素的行中的字符串文本类型?
  2.   

答案:因为在Postgres中几乎所有地方都对表达式进行求值时,具有单个元素((foo))的行值被简化为其单个元素(foo)。所以这个:

CASE (eventtype) WHEN ('Deposit') THEN ...

有效地简化为:

CASE  eventtype  WHEN  'Deposit'  THEN ...