位函数和操作符
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。
例如,对 1010
和 1100
进行按位与操作会返回 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.1
和 192.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。
例如,对 1010
和 1100
进行按位或操作会返回 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。
例如,对 1010
和 1100
进行按位异或操作会返回 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 不同。