【问题标题】:Comparing row value with text fields to row of untyped string literals in CASE将行值与文本字段与 CASE 中的无类型字符串文字行进行比较
【发布时间】:2018-11-26 20:42:26
【问题描述】:

我在 stackoverflow 上找到了一种使用 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)
**/

【问题讨论】:

    标签: postgresql casting case


    【解决方案1】:

    这似乎是对您正在使用的“简单”或“切换”CASE的限制。
    CASE 的替代语法变体无需显式转换即可工作:

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

    只要您有一个案例要测试,这个变体甚至可以“最大限度地减少视觉混乱”。两个额外的字符,但更容易阅读(恕我直言)。不过,对于多种情况,“切换”变体似乎更可取。

    不同的行为显然来自“简单”CASE 中的不同工作流程。 The manual:

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

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

    但至少从 Postgres 8.4 开始,这种行为就一直是这样的(在 pg 11 中也是如此):

    db小提琴here

    到目前为止,在切换的CASE 中可能很少有人对未键入的行值有类似的想法。


    留下你的问题:

    1. 为什么 Postgres 可以在单个元素行中推断字符串文字的类型,但不能在具有 2 个元素的行中推断出字符串字面量的类型?

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

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

    有效地简化为:

    CASE  eventtype  WHEN  'Deposit'  THEN ...
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-08
      • 2013-07-28
      • 2015-08-03
      相关资源
      最近更新 更多