Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Functions: Add examples for bit functions and operators #18206

Merged
merged 7 commits into from
Jul 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
292 changes: 285 additions & 7 deletions functions-and-operators/bit-functions-and-operators.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,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<<n` 将二进制数 `1` 向左移动了 `n` 位:

```sql
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)
```

## [`>>`(右移)](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 兼容性

Expand Down
2 changes: 1 addition & 1 deletion tso.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`)提取出来。

Expand Down