使用BLOB作为数据类型,并编写一个函数,该函数将:
- 提取需要更新的字节
- 更改字节中的位
- 将更改后的字节插入到原始位置的BLOB中
以下是一种实现方法:
delimiter
create function set_bit(b blob, pos int, val int) returns blob reads sql data
comment 'changes the bit at position <pos> (0: right most bit) to <val> in the blob <b>'
begin
declare len int; -- byte length of the blob
declare byte_pos int; -- position of the affected byte (1: left most byte)
declare bit_pos int; -- position within the affected byte (0: right most bit)
declare byte_val int; -- value of the affected byte
set len = length(b);
set byte_pos = len - (pos div 8);
set bit_pos = pos mod 8;
set byte_val = ord(substring(b, byte_pos, 1)); -- read the byte
set byte_val = byte_val & (~(1 << bit_pos)); -- set the bit to 0
set byte_val = byte_val | (val << bit_pos); -- set the bit to <val>
return insert(b, byte_pos, 1, char(byte_val)); -- replace the byte and return
end
delimiter ;
一个简单的测试:
create table test(id int, b blob);
insert into test(id, b) select 1, 0x000000;
insert into test(id, b) select 2, 0xffffff;
我们有两个二进制掩码(每个3字节)- 一个全是零,一个全是一。在两个掩码中,我们将位于位置10(从右数第11位)的位设置为1,并将位于位置11(从右数第12位)的位设置为0。
update test set b = set_bit(b, 10, 1);
update test set b = set_bit(b, 11, 0);
select id, hex(b), to_base2(b) from test;
结果:
| id | hex(b) | to_base2(b) |
|
| 1 | 000400 | 00000000 00000100 00000000 |
| 2 | FFF7FF | 11111111 11110111 11111111 |
在DB Fiddle上查看
注意:to_base2()
是一个自定义函数,返回一个BLOB的比特表示字符串,仅用于展示目的。
这适用于MySQL 5.x和8.0。
可以在单个表达式中内联实现它(无需使用函数)-但那非常难以阅读:
update test t
cross join (select 10 as pos, 1 as val) i
set t.b = insert(
t.b,
length(t.b) - (i.pos div 8),
1,
char(ord(
substring(t.b, length(t.b) - (i.pos div 8), 1))
& ~(1 << (i.pos mod 8))
| (i.val << (i.pos mod 8)
))
);
在DB Fiddle上查看
在MySQL 8.0中,它变得更简单了,因为我们不需要提取字节,可以在blob上使用位运算。但是我们需要确保操作数的长度相同:
update test t
cross join (select 10 as pos, 1 as val) i
set t.b = t.b
& (~(concat(repeat(0x00,length(t.b)-1),char(1)) << i.pos))
| (concat(repeat(0x00,length(t.b)-1),char(i.val)) << i.pos)
在DB Fiddle上查看
另一种方法:
update test t
cross join (select 10 as pos, 1 as val) i
set t.b =
case when i.val = 1
then t.b | concat(repeat(0x00,length(t.b)-1),char(1)) << i.pos
else t.b & ~(concat(repeat(0x00,length(t.b)-1),char(1)) << i.pos)
end
在DB Fiddle上查看
varbinary
或blob
数据类型,因为没有公开的位图数据类型。 - Barmar