简体   繁体   English

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

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

This is the query: 这是查询:

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

If completed_phase is 129 , my output for final column should be FTPI. 如果completed_phase为129 ,我最后一栏的输出应为FTPI。 But it is only showing FT. 但这仅显示FT。 Only the first case statement seems to work, even if all of them are distinct. 即使所有情况都是不同的,似乎只有第一个case语句仍然有效。

STRPOS() will always return the first occurance of the searched string. STRPOS()将始终返回搜索到的字符串的第一个匹配项。 So all calls to strpos() will return 1 for the input value 129. 因此,对strpos()所有调用都将为输入值129返回1。

You can use substring() instead: 您可以改用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

Another option would be to use get_bit() to test each bit individually: 另一个选择是使用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

A more flexible way of doing that is to turn the bits into rows and use an array as a lookup. 一种更灵活的方法是将这些位转换为行并将数组用作查找。 Something like: 就像是:

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

The part regexp_split_to_table(completed_phases::bit(8)::text, '') with ordinality as t(b,i) will return the following for the value 129: 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] the uses the index to lookup the matching code and string_agg() then puts all selected codes together again into a single string. code[i]使用索引查找匹配的代码,然后string_agg()将所有选定的代码再次放到一个字符串中。 The condition where b = '1' only selects the bits that are set. where b = '1'的条件仅选择设置的位。

That solution will be substantially slower than the hardcoded case expression (because it increases the number of rows, just to reduce them again) - but it is more flexible and easier to maintain. 该解决方案将比硬编码的case表达式慢得多(因为它增加了行数,只是为了再次减少行数)-但它更加灵活并且易于维护。


If you need that a lot, the best option would be to put the case expression into a function and use the function in your queries. 如果您需要大量资源,最好的选择是将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;

Then use: 然后使用:

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

As pointed out in the answer by a_horse_with_no_name , strpos will return the first occurrence of the searched string. 正如a_horse_with_no_name答案中指出的那样strpos将返回搜索到的字符串的第一个匹配项。 At any rate, it's better to use get_bit instead of casting to VARCHAR to check if a bit is set. 无论如何,最好使用get_bit而不是强制转换为VARCHAR来检查是否设置了位。 Also, instead of || 另外,代替|| , you can use concat , which will handle null s as blank strings. ,您可以使用concat ,它将处理null s作为空白字符串。 Your query could then be rewritten to: 您的查询随后可以重写为:

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;

On a side note, if you have the option to do so, I would recommend changing your database schema to store the phases as individual boolean columns instead of using a bit map. 附带说明一下,如果您可以选择这样做,建议您更改数据库架构,以将阶段存储为单独的boolean列,而不要使用位图。 See Any disadvantages to bit flags in database columns? 请参见数据库列中位标志的任何缺点? for a good discussion of why. 很好地讨论了为什么。

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

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