将 "summed up flags" 转换回原来的含义
Convert "summed up flags" back to original meaning
我必须使用一个包含列 change
的数据库,该列指示与之前的相应条目相比其他三个列的更改方式。更改类型可以是 new
、removed
或 changed
.
这些类型分配了以下编号:
column | a b c
----------+----------------------
new | 3 12 48
removed | 2 8 32
changed | 1 4 16
change
列的内容是所有应用更改类型的总和,即如果列 a
是 changed
和 b
removed
,change
列将是 1+8=9。 (总有变化,即可以有 1、2 或 3 个加数。)
我的问题:我想不出一个聪明的方法来将这个 "summed up flag" 转换回它原来的含义(部分问题是不知道 google 有什么用)。
我可以看出,如果 change
不均匀,那么 a
要么是 new
要么是 changed
;如果 change>=48
,c
是 new
加上可能的其他变化,否则 change>=32
=> c
是 removed
加上可能的其他变化等等在。我可能可以将它们组合成一个巨大的逻辑查询——但我确信必须有一个复杂的解决方案来做到这一点。
我使用 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 的记录,在另外三个列中更改为 a、b 和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)
我必须使用一个包含列 change
的数据库,该列指示与之前的相应条目相比其他三个列的更改方式。更改类型可以是 new
、removed
或 changed
.
这些类型分配了以下编号:
column | a b c
----------+----------------------
new | 3 12 48
removed | 2 8 32
changed | 1 4 16
change
列的内容是所有应用更改类型的总和,即如果列 a
是 changed
和 b
removed
,change
列将是 1+8=9。 (总有变化,即可以有 1、2 或 3 个加数。)
我的问题:我想不出一个聪明的方法来将这个 "summed up flag" 转换回它原来的含义(部分问题是不知道 google 有什么用)。
我可以看出,如果 change
不均匀,那么 a
要么是 new
要么是 changed
;如果 change>=48
,c
是 new
加上可能的其他变化,否则 change>=32
=> c
是 removed
加上可能的其他变化等等在。我可能可以将它们组合成一个巨大的逻辑查询——但我确信必须有一个复杂的解决方案来做到这一点。
我使用 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 的记录,在另外三个列中更改为 a、b 和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)