📣
TiDB Cloud Essential 开放公测中。此页面由 AI 自动翻译,英文原文请见此处。

位函数与运算符

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 从十六进制(基数 16)转换为二进制(基数 2),显示其二进制等价为 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)

&(按位与)

& 运算符执行按位与操作。它比较两个数字对应位:如果对应位都为 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 地址是否属于同一网络非常有用。

在下面的两个示例中,IP 地址 192.168.1.1192.168.1.2 在掩码 255.255.255.0 下都属于同一网络 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)

~(按位取反)

~ 运算符对给定值执行按位取反(或按位非)操作。它会将给定值的每一位取反:0 变为 1,1 变为 0。

在操作前,值会被扩展为 64 位。

以二进制数 1111000011110000 为例,扩展为 64 位并取反后如下所示:

原始(16 位): 1111000011110000 扩展并取反(64 位): 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)

|(按位或)

| 运算符执行按位或操作。它比较两个数字对应位:只要对应位中至少有一个为 1,结果的该位就为 1。

例如,10101100 进行按位或操作,结果为 1110,因为在前 3 位中,两个数字至少有一个对应位为 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)

^(按位异或)

^ 运算符执行按位异或(异或或“互斥或”)操作。它比较两个数字对应位:如果对应位不同,结果的该位为 1。

例如,10101100 进行按位异或操作,结果为 0110,因为这两个数字的第 2 位和第 3 位不同。

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

文档内容是否有帮助?