位函数和操作符
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 不同。