将 "summed up flags" 转换回原来的含义

Convert "summed up flags" back to original meaning

我必须使用一个包含列 change 的数据库,该列指示与之前的相应条目相比其他三个列的更改方式。更改类型可以是 newremovedchanged.

这些类型分配了以下编号:

column    |   a       b       c
----------+----------------------
new       |   3       12      48
removed   |   2       8       32
changed   |   1       4       16

change 列的内容是所有应用更改类型的总和,即如果列 achangedb removedchange 列将是 1+8=9。 (总有变化,即可以有 1、2 或 3 个加数。)

我的问题:我想不出一个聪明的方法来将这个 "summed up flag" 转换回它原来的含义(部分问题是不知道 google 有什么用)。

我可以看出,如果 change 不均匀,那么 a 要么是 new 要么是 changed;如果 change>=48cnew 加上可能的其他变化,否则 change>=32 => cremoved 加上可能的其他变化等等在。我可能可以将它们组合成一个巨大的逻辑查询——但我确信必须有一个复杂的解决方案来做到这一点。

我使用 PostgreSQL 以防万一。 table 有大约 5000 万行。

可以使用位运算。如果我理解正确的话:

select (case when col::bit(8) & B'00000011' then 'new'
             when col::bit(8) & B'00000001' then 'changed'
             when col::bit(8) & B'00000010' then 'removed'
        end) as a_status,
       (case when col::bit(8) & B'00001100' then 'new'
             when col::bit(8) & B'00000100' then 'changed'
             when col::bit(8) & B'00001000' then 'removed'
        end) as b_status,
       (case when col::bit(8) & B'00110000' then 'new'
             when col::bit(8) & B'00010000' then 'changed'
             when col::bit(8) & B'00100000' then 'removed'
        end) as c_status

这可以通过结合使用按位与运算符 (&) 和按位移位 (>>) 来完成。

以下查询 returns 所有 table 的记录,在另外三个列中更改为 ab c分别为:

select  *,
        case change & 3 
             when 1 then 'changed'
             when 2 then 'removed'
             when 3 then 'new'
        end as change_to_a,
        case (change >> 2) & 3 
             when 1 then 'changed'
             when 2 then 'removed'
             when 3 then 'new'
        end as change_to_b,
        case (change >> 4) & 3 
             when 1 then 'changed'
             when 2 then 'removed'
             when 3 then 'new'
        end as change_to_c
from    mytable;

这里是fiddle.

示例输出:

id  change  change_to_a change_to_b change_to_c
-----------------------------------------------
1     9       changed     removed     (null)
2    50       removed     (null)        new
3    83         new       (null)      changed
4    20        (null)     changed     changed
5    25       changed     removed     changed

这是另一种方法。这也是 returns 3 个额外的列,但是每种更改类型一个列,并且这些值是 'a'、'b'、'c'[=36 的串联=]:

select  *,
        concat(
          case when change        & 3 = 1 then 'a' end,
          case when (change >> 2) & 3 = 1 then 'b' end, 
          case when (change >> 4) & 3 = 1 then 'c' end) changed,
        concat(
          case when change        & 3 = 2 then 'a' end,
          case when (change >> 2) & 3 = 2 then 'b' end, 
          case when (change >> 4) & 3 = 2 then 'c' end) removed,
        concat(
          case when change        & 3 = 3 then 'a' end,
          case when (change >> 2) & 3 = 3 then 'b' end, 
          case when (change >> 4) & 3 = 3 then 'c' end) new
from    mytable;

这里是fiddle.

示例输出:

id  change  changed     removed    new
-----------------------------------------
1     9        a            b     (null)
2    50     (null)          a       c
3    83        c         (null)     a
4    20       bc         (null)   (null)
5    25       ac            b     (null)