繁体   English   中英

多案例声明无法在Postgres中正常工作

[英]Multiple case statement not working as expected in Postgres

这是查询:

SELECT DISTINCT 
         completed_phases,
         CAST(completed_phases::bit(8) AS VARCHAR),
         CASE WHEN STRPOS(CAST(completed_phases::bit(8) AS VARCHAR),'1')=1 THEN 'FT' ELSE '' END ||
         CASE WHEN STRPOS(CAST(completed_phases::bit(8) AS VARCHAR),'1')=2 THEN 'ED' ELSE '' END ||
         CASE WHEN STRPOS(CAST(completed_phases::bit(8) AS VARCHAR),'1')=3 THEN 'MC' ELSE '' END ||
         CASE WHEN STRPOS(CAST(completed_phases::bit(8) AS VARCHAR),'1')=4 THEN 'HC' ELSE '' END ||
         CASE WHEN STRPOS(CAST(completed_phases::bit(8) AS VARCHAR),'1')=5 THEN 'UV' ELSE '' END ||
         CASE WHEN STRPOS(CAST(completed_phases::bit(8) AS VARCHAR),'1')=6 THEN 'TT' ELSE '' END ||
         CASE WHEN STRPOS(CAST(completed_phases::bit(8) AS VARCHAR),'1')=7 THEN 'RX' ELSE '' END ||
         CASE WHEN STRPOS(CAST(completed_phases::bit(8) AS VARCHAR),'1')=8 THEN 'PI' ELSE '' END
FROM rx_sales_order

如果completed_phase为129 ,我最后一栏的输出应为FTPI。 但这仅显示FT。 即使所有情况都是不同的,似乎只有第一个case语句仍然有效。

STRPOS()将始终返回搜索到的字符串的第一个匹配项。 因此,对strpos()所有调用都将为输入值129返回1。

您可以改用substring()

CASE WHEN substring(CAST(completed_phases::bit(8) AS VARCHAR),1,1)='1' THEN 'FT' ELSE '' END ||
CASE WHEN substring(CAST(completed_phases::bit(8) AS VARCHAR),2,1)='1' THEN 'ED' ELSE '' END ||
CASE WHEN substring(CAST(completed_phases::bit(8) AS VARCHAR),3,1)='1' THEN 'MC' ELSE '' END ||
CASE WHEN substring(CAST(completed_phases::bit(8) AS VARCHAR),4,1)='1' THEN 'HC' ELSE '' END ||
CASE WHEN substring(CAST(completed_phases::bit(8) AS VARCHAR),5,1)='1' THEN 'UV' ELSE '' END ||
CASE WHEN substring(CAST(completed_phases::bit(8) AS VARCHAR),6,1)='1' THEN 'TT' ELSE '' END ||
CASE WHEN substring(CAST(completed_phases::bit(8) AS VARCHAR),7,1)='1' THEN 'RX' ELSE '' END ||
CASE WHEN substring(CAST(completed_phases::bit(8) AS VARCHAR),8,1)='1' THEN 'PI' ELSE '' END

另一个选择是使用get_bit()分别测试每个位:

case when get_bit(completed_phases::bit(8), 0) = 1 then 'FT' else '' END||
case when get_bit(completed_phases::bit(8), 1) = 1 then 'ED' else '' END||
case when get_bit(completed_phases::bit(8), 2) = 1 then 'MC' else '' END||
case when get_bit(completed_phases::bit(8), 3) = 1 then 'HC' else '' END||
case when get_bit(completed_phases::bit(8), 4) = 1 then 'UV' else '' END||
case when get_bit(completed_phases::bit(8), 5) = 1 then 'TT' else '' END||
case when get_bit(completed_phases::bit(8), 6) = 1 then 'RX' else '' END||
case when get_bit(completed_phases::bit(8), 7) = 1 then 'PI' else '' END

一种更灵活的方法是将这些位转换为行并将数组用作查找。 就像是:

with lookup (codes) as (
   values (array['FT','ED','MC','HC','UV','TT','RX','PI'])
)
SELECT  completed_phases, 
        completed_phases::bit(8),
        x.code
FROM rx_sales_order
  join lateral (
    select string_agg(codes[i],'') as code
    from lookup, unnest(string_to_array(completed_phases::bit(8)::text, null)) with ordinality as t(b,i) 
    where b = '1'
 ) as x on true

regexp_split_to_table(completed_phases::bit(8)::text, '') with ordinality as t(b,i)将返回以下值129:

b | i
--+--
1 | 1
0 | 2
0 | 3
0 | 4
0 | 5
0 | 6
0 | 7
1 | 8

code[i]使用索引查找匹配的代码,然后string_agg()将所有选定的代码再次放到一个字符串中。 where b = '1'的条件仅选择设置的位。

该解决方案将比硬编码的case表达式慢得多(因为它增加了行数,只是为了再次减少行数)-但它更加灵活并且易于维护。


如果您需要大量资源,最好的选择是将case表达式放入函数中并在查询中使用该函数。

create or replace function get_codes(p_phases integer)
  returns text
as 
$$
  select
    case when get_bit(p_phases::bit(8), 0) = 1 then 'FT' else '' END||
    case when get_bit(p_phases::bit(8), 1) = 1 then 'ED' else '' END||
    case when get_bit(p_phases::bit(8), 2) = 1 then 'MC' else '' END||
    case when get_bit(p_phases::bit(8), 3) = 1 then 'HC' else '' END||
    case when get_bit(p_phases::bit(8), 4) = 1 then 'UV' else '' END||
    case when get_bit(p_phases::bit(8), 5) = 1 then 'TT' else '' END||
    case when get_bit(p_phases::bit(8), 6) = 1 then 'RX' else '' END||
    case when get_bit(p_phases::bit(8), 7) = 1 then 'PI' else '' END
$$
language sql;

然后使用:

SELECT DISTINCT 
         completed_phases,
         get_codes(completed_phases) as codes
FROM rx_sales_order

正如a_horse_with_no_name答案中指出的那样strpos将返回搜索到的字符串的第一个匹配项。 无论如何,最好使用get_bit而不是强制转换为VARCHAR来检查是否设置了位。 另外,代替|| ,您可以使用concat ,它将处理null s作为空白字符串。 您的查询随后可以重写为:

SELECT DISTINCT 
     completed_phases,
     CAST(completed_phases::bit(8) AS VARCHAR),
     concat(CASE get_bit(completed_phases::bit(8), 0) WHEN 1 THEN 'FT' END,
            CASE get_bit(completed_phases::bit(8), 1) WHEN 1 THEN 'ED' END,
            CASE get_bit(completed_phases::bit(8), 2) WHEN 1 THEN 'MC' END,
            CASE get_bit(completed_phases::bit(8), 3) WHEN 1 THEN 'HC' END,
            CASE get_bit(completed_phases::bit(8), 4) WHEN 1 THEN 'UV' END,
            CASE get_bit(completed_phases::bit(8), 5) WHEN 1 THEN 'TT' END,
            CASE get_bit(completed_phases::bit(8), 6) WHEN 1 THEN 'RX' END,
            CASE get_bit(completed_phases::bit(8), 7) WHEN 1 THEN 'PI' END)
FROM rx_sales_order;

附带说明一下,如果您可以选择这样做,建议您更改数据库架构,以将阶段存储为单独的boolean列,而不要使用位图。 请参见数据库列中位标志的任何缺点? 很好地讨论了为什么。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM