PingKai Logo下载

位函数与运算符

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。

例如,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 地址是否属于同一网络非常有用。

在下面的两个示例中,IP 地址 192.168.1.1 和 192.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。

例如,1010 和 1100 进行按位或操作,结果为 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。

例如,1010 和 1100 进行按位异或操作,结果为 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。