diff --git a/functions-and-operators/bit-functions-and-operators.md b/functions-and-operators/bit-functions-and-operators.md index 3d8f1e8a1f60..c9e735995de0 100644 --- a/functions-and-operators/bit-functions-and-operators.md +++ b/functions-and-operators/bit-functions-and-operators.md @@ -11,13 +11,291 @@ TiDB 支持使用 MySQL 8.0 中提供的所有[位函数和操作符](https://de | 函数和操作符名 | 功能描述 | | -------------- | ------------------------------------- | -| [`BIT_COUNT()`](https://dev.mysql.com/doc/refman/8.0/en/bit-functions.html#function_bit-count) | 返回参数二进制表示中为 1 的个数 | -| [&](https://dev.mysql.com/doc/refman/8.0/en/bit-functions.html#operator_bitwise-and) | 位与 | -| [~](https://dev.mysql.com/doc/refman/8.0/en/bit-functions.html#operator_bitwise-invert) | 按位取反 | -| [\|](https://dev.mysql.com/doc/refman/8.0/en/bit-functions.html#operator_bitwise-or) | 位或 | -| [^](https://dev.mysql.com/doc/refman/8.0/en/bit-functions.html#operator_bitwise-xor) | 位亦或 | -| [<<](https://dev.mysql.com/doc/refman/8.0/en/bit-functions.html#operator_left-shift) | 左移 | -| [>>](https://dev.mysql.com/doc/refman/8.0/en/bit-functions.html#operator_right-shift) | 右移 | +| [`BIT_COUNT()`](#bit_count) | 返回参数二进制表示中为 1 的个数 | +| [`&`](#按位与) | 按位与 | +| [`~`](#按位取反) | 按位取反 | +| [`\|`](#按位或) | 按位或 | +| [`^`](#按位异或) | 按位异或 | +| [`<<`](#左移) | 左移 | +| [`>>`](#右移) | 右移 | + +## [`BIT_COUNT()`](https://dev.mysql.com/doc/refman/8.0/en/bit-functions.html#function_bit-count) + +`BIT_COUNT(expr)` 函数返回 `expr` 中为 1 的位数。 + +```sql +SELECT BIT_COUNT(b'00101001'); +``` + +``` ++------------------------+ +| BIT_COUNT(b'00101001') | ++------------------------+ +| 3 | ++------------------------+ +1 row in set (0.00 sec) +``` + +> **注意:** +> +> 当输入的 `expr` 参数是一个二进制数时,你需要在这个数之前显式地指定 `b`,比如 `b'00101001'`。否则,该函数会将其视为字符串处理而返回不同的结果。例如,`BIT_COUNT('00101001')` 会返回 `7`,因为它会将字符串 `'00101001'` 转换为十进制数 `101001`,并计算 `101001` 的二进制表示 `11000100001010001` 中 `1` 的位数。 + +下面的示例与前面的类似,但使用的参数是十六进制数而非二进制数。`CONV()` 函数用于将 `0x29` 从十六进制转换为二进制,可以看到 `0x29` 等价于二进制的 `00101001`。 + +```sql +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](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing) 表示法。在下面的示例中,子网掩码 `255.255.255.0` 被转换为其 CIDR 表示形式 `24`。 + +```sql +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) +``` + +## [`&`(按位与)](https://dev.mysql.com/doc/refman/8.0/en/bit-functions.html#operator_bitwise-and) + +`&` 操作符用于执行按位与 (bitwise AND) 操作。它会比较两个数中的对应位,如果两个对应位都是 1,则结果中的对应位为 1,否则为 0。 + +例如,对 `1010` 和 `1100` 进行按位与操作会返回 `1000`,因为在这两个数中只有最左边的第一位都是 1。 + +``` + 1010 +& 1100 + ---- + 1000 +``` + +在 SQL 中,可以这样使用 `&` 操作符: + +```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`。 + +```sql +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) +``` + +```sql +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) +``` + +## [`~`(按位取反)](https://dev.mysql.com/doc/refman/8.0/en/bit-functions.html#operator_bitwise-invert) + +`~` 操作符用于对给定的值进行按位取反(bitwise NOT)操作。它会对给定值中的每一位进行取反:0 的位变为 1,1 的位变为 0。 + +在进行取反操作之前,它会先将给定的值扩展到 64 位。 + +以二进制数 `1111000011110000` 为例。当扩展到 64 位并进行取反后,其结果如下: + +``` +Original (16 bits): 1111000011110000 +Expanded and inverted (64 bits): 1111111111111111111111111111111111111111111111110000111100001111 +``` + +在 SQL 中,可以这样使用 `~` 操作符: + +```sql +SELECT CONV(~ b'1111000011110000',10,2); ++------------------------------------------------------------------+ +| CONV(~ b'1111000011110000',10,2) | ++------------------------------------------------------------------+ +| 1111111111111111111111111111111111111111111111110000111100001111 | ++------------------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +如果需要对取反后的结果再次取反,可以再次应用 `~` 操作符。 + +```sql +SELECT CONV(~ b'1111111111111111111111111111111111111111111111110000111100001111',10,2); +``` + +``` ++----------------------------------------------------------------------------------+ +| CONV(~ b'1111111111111111111111111111111111111111111111110000111100001111',10,2) | ++----------------------------------------------------------------------------------+ +| 1111000011110000 | ++----------------------------------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +## [`|`(按位或)](https://dev.mysql.com/doc/refman/8.0/en/bit-functions.html#operator_bitwise-or) + +`|` 操作符用于执行按位或 (bitwise OR) 操作。它会比较两个数中的对应位,如果至少有一个对应位为 1,则结果中的对应位为 1。 + +例如,对 `1010` 和 `1100` 进行按位或操作会返回 `1110`,因为在这两个数的前三位中,至少有一个数的对应位为 1。 + +``` + 1010 +| 1100 + ---- + 1110 +``` + +在 SQL 中,可以这样使用 `|` 操作符: + +```sql +SELECT CONV(b'1010' | b'1100',10,2); +``` + +``` ++------------------------------+ +| CONV(b'1010' | b'1100',10,2) | ++------------------------------+ +| 1110 | ++------------------------------+ +1 row in set (0.00 sec) +``` + +## [`^`(按位异或)](https://dev.mysql.com/doc/refman/8.0/en/bit-functions.html#operator_bitwise-xor) + +`^` 操作符用于执行按位异或 (bitwise XOR) 操作。它会比较两个数中的对应位,如果对应位不同,则结果中的对应位为 1。 + +例如,对 `1010` 和 `1100` 进行按位异或操作会返回 `0110`,因为这两个数中的第二位和第三位都不同。 + +``` + 1010 +^ 1100 + ---- + 0110 +``` + +在 SQL 中,可以这样使用 `^` 操作符: + +```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`。 + +## [`<<`(左移)](https://dev.mysql.com/doc/refman/8.0/en/bit-functions.html#operator_left-shift) + +`<<` 操作符用于执行左移操作。它会将一个数中的所有位向左移动指定的位数,并用零填充右侧空出的位。 + +例如,下面的语句使用了 `1<>`(右移)](https://dev.mysql.com/doc/refman/8.0/en/bit-functions.html#operator_right-shift) + +`>>` 操作符用于执行右移操作。它会将数中的所有位向右移动指定的位数,并用零填充左侧空出的位。 + +例如,下面的语句使用了 `1024>>n` 将数字 `1024`(二进制为 `10000000000`)向右移动了 `n` 位: + +```sql +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](/tso.md) 时间戳中提取 UNIX 时间戳。 ## MySQL 兼容性 diff --git a/tso.md b/tso.md index 813cbd35d336..a36d0c77a673 100644 --- a/tso.md +++ b/tso.md @@ -78,7 +78,7 @@ FROM_UNIXTIME((@ts >> 18)/1000): 2023-08-27 20:33:41.6870 1 row in set (0.00 sec) ``` -`>> 18` 操作表示按位[右移](/functions-and-operators/bit-functions-and-operators.md) 18 位,用于提取物理时间戳。由于物理时间戳以毫秒为单位,与更常见的以秒为单位的 UNIX 时间戳格式不同,因此需要除以 1000 将其转换为与 [`FROM_UNIXTIME()`](/functions-and-operators/date-and-time-functions.md) 兼容的格式。这个转换过程与 `TIDB_PARSE_TSO()` 的功能一致。 +`>> 18` 操作表示按位[右移](/functions-and-operators/bit-functions-and-operators.md#右移) 18 位,用于提取物理时间戳。由于物理时间戳以毫秒为单位,与更常见的以秒为单位的 UNIX 时间戳格式不同,因此需要除以 1000 将其转换为与 [`FROM_UNIXTIME()`](/functions-and-operators/date-and-time-functions.md) 兼容的格式。这个转换过程与 `TIDB_PARSE_TSO()` 的功能一致。 你还可以将二进制中的逻辑时间戳 `000000000000000100`(即十进制中的 `4`)提取出来。