STRPOS()
will always return the first occurance of the searched string. So all calls to strpos()
will return 1 for the input value 129.
You can use substring()
instead:
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:
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:
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. The condition where b = '1'
only selects the bits that are set.
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.
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.
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