位函数和操作符

TiDB 支持使用 MySQL 8.0 中提供的所有位函数和操作符

位函数和操作符表

函数和操作符名功能描述
BIT_COUNT()返回参数二进制表示中为 1 的个数
&按位与
~按位取反
|按位或
^按位异或
<<左移
>>右移

BIT_COUNT()

BIT_COUNT(expr) 函数返回 expr 中为 1 的位数。

SELECT BIT_COUNT(b'00101001');
+------------------------+ | BIT_COUNT(b'00101001') | +------------------------+ | 3 | +------------------------+ 1 row in set (0.00 sec)

下面的示例与前面的类似,但使用的参数是十六进制数而非二进制数。CONV() 函数用于将 0x29 从十六进制转换为二进制,可以看到 0x29 等价于二进制的 00101001

SELECT BIT_COUNT(0x29), CONV(0x29,16,2);
+-----------------+-----------------+ | BIT_COUNT(0x29) | CONV(0x29,16,2) | +-----------------+-----------------+ | 3 | 101001 | +-----------------+-----------------+ 1 row in set (0.01 sec)

BIT_COUNT(expr) 函数的一个常见用法是将子网掩码转换为 CIDR 表示法。在下面的示例中,子网掩码 255.255.255.0 被转换为其 CIDR 表示形式 24

SELECT BIT_COUNT(INET_ATON('255.255.255.0'));
+---------------------------------------+ | BIT_COUNT(INET_ATON('255.255.255.0')) | +---------------------------------------+ | 24 | +---------------------------------------+ 1 row in set (0.00 sec)

&(按位与)

& 操作符用于执行按位与 (bitwise AND) 操作。它会比较两个数中的对应位,如果两个对应位都是 1,则结果中的对应位为 1,否则为 0。

例如,对 10101100 进行按位与操作会返回 1000,因为在这两个数中只有最左边的第一位都是 1。

1010 & 1100 ---- 1000

在 SQL 中,可以这样使用 & 操作符:

SELECT CONV(b'1010' & b'1000',10,2);
+------------------------------+ | CONV(b'1010' & b'1000',10,2) | +------------------------------+ | 1000 | +------------------------------+ 1 row in set (0.00 sec)

你可以将 & 操作符与 INET_NTOA()INET_ATON() 函数结合在一起使用,对 IP 地址和网络掩码进行按位与操作,以获取网络地址。这对于判断多个 IP 地址是否属于同一网络非常有用。

在以下示例中,使用掩码 255.255.255.0 时,IP 地址 192.168.1.1192.168.1.2 都属于同一网络 192.168.1.0/24

SELECT INET_NTOA(INET_ATON('192.168.1.1') & INET_ATON('255.255.255.0'));
+------------------------------------------------------------------+ | INET_NTOA(INET_ATON('192.168.1.1') & INET_ATON('255.255.255.0')) | +------------------------------------------------------------------+ | 192.168.1.0 | +------------------------------------------------------------------+ 1 row in set (0.00 sec)
SELECT INET_NTOA(INET_ATON('192.168.1.2') & INET_ATON('255.255.255.0'));
+------------------------------------------------------------------+ | INET_NTOA(INET_ATON('192.168.1.2') & INET_ATON('255.255.255.0')) | +------------------------------------------------------------------+ | 192.168.1.0 | +------------------------------------------------------------------+ 1 row in set (0.00 sec)

~(按位取反)

~ 操作符用于对给定的值进行按位取反(bitwise NOT)操作。它会对给定值中的每一位进行取反:0 的位变为 1,1 的位变为 0。

在进行取反操作之前,它会先将给定的值扩展到 64 位。

以二进制数 1111000011110000 为例。当扩展到 64 位并进行取反后,其结果如下:

Original (16 bits): 1111000011110000 Expanded and inverted (64 bits): 1111111111111111111111111111111111111111111111110000111100001111

在 SQL 中,可以这样使用 ~ 操作符:

SELECT CONV(~ b'1111000011110000',10,2); +------------------------------------------------------------------+ | CONV(~ b'1111000011110000',10,2) | +------------------------------------------------------------------+ | 1111111111111111111111111111111111111111111111110000111100001111 | +------------------------------------------------------------------+ 1 row in set (0.00 sec)

如果需要对取反后的结果再次取反,可以再次应用 ~ 操作符。

SELECT CONV(~ b'1111111111111111111111111111111111111111111111110000111100001111',10,2);
+----------------------------------------------------------------------------------+ | CONV(~ b'1111111111111111111111111111111111111111111111110000111100001111',10,2) | +----------------------------------------------------------------------------------+ | 1111000011110000 | +----------------------------------------------------------------------------------+ 1 row in set (0.00 sec)

|(按位或)

| 操作符用于执行按位或 (bitwise OR) 操作。它会比较两个数中的对应位,如果至少有一个对应位为 1,则结果中的对应位为 1。

例如,对 10101100 进行按位或操作会返回 1110,因为在这两个数的前三位中,至少有一个数的对应位为 1。

1010 | 1100 ---- 1110

在 SQL 中,可以这样使用 | 操作符:

SELECT CONV(b'1010' | b'1100',10,2);
+------------------------------+ | CONV(b'1010' | b'1100',10,2) | +------------------------------+ | 1110 | +------------------------------+ 1 row in set (0.00 sec)

^(按位异或)

^ 操作符用于执行按位异或 (bitwise XOR) 操作。它会比较两个数中的对应位,如果对应位不同,则结果中的对应位为 1。

例如,对 10101100 进行按位异或操作会返回 0110,因为这两个数中的第二位和第三位都不同。

1010 ^ 1100 ---- 0110

在 SQL 中,可以这样使用 ^ 操作符:

SELECT CONV(b'1010' ^ b'1100',10,2);
+------------------------------+ | CONV(b'1010' ^ b'1100',10,2) | +------------------------------+ | 110 | +------------------------------+ 1 row in set (0.00 sec)

需要注意的是,由于省略了前导零,结果会显示为 110 而不是 0110

<<(左移)

<< 操作符用于执行左移操作。它会将一个数中的所有位向左移动指定的位数,并用零填充右侧空出的位。

例如,下面的语句使用了 1<<n 将二进制数 1 向左移动了 n 位:

WITH RECURSIVE cte(n) AS ( SELECT 0 AS n UNION ALL SELECT 1+n FROM cte WHERE n<10 ) SELECT n,1<<n,LPAD(CONV(1<<n,10,2),11,0) FROM cte;
+------+------+----------------------------+ | n | 1<<n | LPAD(CONV(1<<n,10,2),11,0) | +------+------+----------------------------+ | 0 | 1 | 00000000001 | | 1 | 2 | 00000000010 | | 2 | 4 | 00000000100 | | 3 | 8 | 00000001000 | | 4 | 16 | 00000010000 | | 5 | 32 | 00000100000 | | 6 | 64 | 00001000000 | | 7 | 128 | 00010000000 | | 8 | 256 | 00100000000 | | 9 | 512 | 01000000000 | | 10 | 1024 | 10000000000 | +------+------+----------------------------+ 11 rows in set (0.00 sec)

>>(右移)

>> 操作符用于执行右移操作。它会将数中的所有位向右移动指定的位数,并用零填充左侧空出的位。

例如,下面的语句使用了 1024>>n 将数字 1024(二进制为 10000000000)向右移动了 n 位:

WITH RECURSIVE cte(n) AS ( SELECT 0 AS n UNION ALL SELECT n+1 FROM cte WHERE n<11 ) SELECT n,1024>>n,LPAD(CONV(1024>>n,10,2),11,0) FROM cte;
+------+---------+-------------------------------+ | n | 1024>>n | LPAD(CONV(1024>>n,10,2),11,0) | +------+---------+-------------------------------+ | 0 | 1024 | 10000000000 | | 1 | 512 | 01000000000 | | 2 | 256 | 00100000000 | | 3 | 128 | 00010000000 | | 4 | 64 | 00001000000 | | 5 | 32 | 00000100000 | | 6 | 16 | 00000010000 | | 7 | 8 | 00000001000 | | 8 | 4 | 00000000100 | | 9 | 2 | 00000000010 | | 10 | 1 | 00000000001 | | 11 | 0 | 00000000000 | +------+---------+-------------------------------+ 12 rows in set (0.00 sec)

>> 操作符还可以用于提取一个大数中的特定部分,例如从 TiDB TSO 时间戳中提取 UNIX 时间戳。

MySQL 兼容性

在处理位函数和操作符时,MySQL 8.0 与之前版本的 MySQL 之间存在一些差异。TiDB 旨在遵循 MySQL 8.0 的行为。

已知问题

在以下情况中,TiDB 中的查询结果与 MySQL 5.7 相同,但与 MySQL 8.0 不同。

  • 二进制参数的位操作。更多信息,请参考 #30637
  • BIT_COUNT() 函数的结果。更多信息,请参考 #44621

文档内容是否有帮助?