扩展 MySQL 8.0  / 第 6 章 向 MySQL 添加函数  /  12.13 位函数和运算符

12.13 位函数和运算符

表 12.17 位函数和运算符

姓名 描述
& 按位与
>> 右移
<< 左移
^ 按位异或
BIT_COUNT() 返回设置的位数
| 按位或
~ 按位取反

位函数和运算符包括 BIT_COUNT(), BIT_AND(), BIT_OR(), BIT_XOR(), &, |, ^, ~, <<>>。(BIT_AND()BIT_OR()BIT_XOR()函数是第 12.20.1 节“聚合函数描述”中描述的聚合函数。)目前,位函数和运算符需要 BIGINT(64 位整数)参数和返回BIGINT值,因此它们的最大范围为 64 位. 其他类型的参数被转换为BIGINT并且可能会发生截断。

MySQL 8.0 的扩展改变了这种强制转换BIGINT行为:位函数和运算符允许二进制字符串类型参数(BINARYVARBINARYBLOB类型),使它们能够接受参数并产生大于 64 位的返回值。因此,MySQL 5.7 中二进制参数的位操作可能会在 MySQL 8.0 中产生不同的结果。为了提前通知这种潜在的行为变化,从 MySQL 5.7.11 开始,服务器会针对二进制参数在 MySQL 8.0 中未转换为整数的位操作生成警告。这些警告提供了重写受影响语句的机会。要以升级到 8.0 后不会更改的方式显式生成 MySQL 5.7 行为,请转换位操作二进制参数以将它们转换为整数。

需要注意的五种有问题的表达类型是:

nonliteral_binary { & | ^ } binary
binary  { & | ^ } nonliteral_binary
nonliteral_binary { << >> } anything
~ nonliteral_binary
AGGR_BIT_FUNC(nonliteral_binary)

这些表达式BIGINT在 MySQL 5.7 中返回,在 8.0 中返回二进制字符串。

符号说明:

  • { op1 op2 ... }:适用于给定表达式类型的运算符列表。

  • binary:任何类型的二进制字符串参数,包括十六进制文字、位文字或 NULL文字。

  • nonliteral_binary:一个参数,它是除十六进制文字、位文字或NULL文字之外的二进制字符串值。

  • AGGR_BIT_FUNC:采用位值参数的聚合函数: BIT_AND(), BIT_OR(), BIT_XOR()

服务器为语句中的每个有问题的表达式生成一个警告,而不是为处理的每一行生成一个警告。假设包含两个有问题的表达式的语句从表中选择三行。每个语句执行的警告数是两个,而不是六个。下面的例子说明了这一点。

mysql> CREATE TABLE t(vbin1 VARBINARY(32), vbin2 VARBINARY(32));
Query OK, 0 rows affected (0.03 sec)

mysql> INSERT INTO t VALUES (3,1), (3,2), (3,3);
Query OK, 3 rows affected (0.01 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> SELECT HEX(vbin1 & vbin2) AS op1,
    -> HEX(vbin1 | vbin2) AS op2
    -> FROM t;
+------+------+
| op1  | op2  |
+------+------+
| 1    | 3    |
| 2    | 3    |
| 3    | 3    |
+------+------+
3 rows in set, 2 warnings (0.00 sec)

mysql> SHOW WARNINGS\G
*************************** 1. row ***************************
  Level: Warning
   Code: 1287
Message: Bitwise operations on BINARY will change behavior in a future
         version, check the 'Bit functions' section in the manual.
*************************** 2. row ***************************
  Level: Warning
   Code: 1287
Message: Bitwise operations on BINARY will change behavior in a future
         version, check the 'Bit functions' section in the manual.
2 rows in set (0.00 sec)

为避免受影响的语句在升级到 MySQL 8.0 后产生不同的结果,请重写它以使其不生成位操作警告。为此,请将至少一个二进制参数强制转换为BIGINTwith CAST(... AS UNSIGNED)。这使得 MySQL 5.7 隐式二进制到整数转换显式:

mysql> SELECT HEX(CAST(vbin1 AS UNSIGNED) & CAST(vbin2 AS UNSIGNED)) AS op1,
    -> HEX(CAST(vbin1 AS UNSIGNED) | CAST(vbin2 AS UNSIGNED)) AS op2
    -> FROM t;
+------+------+
| op1  | op2  |
+------+------+
| 1    | 3    |
| 2    | 3    |
| 3    | 3    |
+------+------+
3 rows in set (0.01 sec)

mysql> SHOW WARNINGS\G
Empty set (0.00 sec)

如图所示重写语句后,MySQL 8.0 尊重将二进制参数视为整数的意图,并产生与 5.7 中相同的结果。此外,将语句从 MySQL 5.7 复制到 8.0 不会在不同的服务器上产生不同的结果。

无法重写的受影响语句在升级和复制方面会遇到以下潜在问题:

  • 升级到 MySQL 8.0 后,该语句可能会返回不同的结果。

  • 对于基于语句和混合格式的二进制日志记录,从旧版本复制到 MySQL 8.0 可能会失败。这也适用于在 8.0 服务器上重放旧的二进制日志(例如,使用mysqlbinlog)。为避免这种情况,请切换到旧源服务器上的基于行的二进制日志记录。

以下列表描述了可用的位函数和运算符:

  • |

    按位或。

    结果是一个无符号的 64 位整数。

    mysql> SELECT 29 | 15;
            -> 31
  • &

    按位与。

    结果是一个无符号的 64 位整数。

    mysql> SELECT 29 & 15;
            -> 13
  • ^

    按位异或。

    结果是一个无符号的 64 位整数。

    mysql> SELECT 1 ^ 1;
            -> 0
    mysql> SELECT 1 ^ 0;
            -> 1
    mysql> SELECT 11 ^ 3;
            -> 8
  • <<

    将 longlong ( BIGINT) 数字向左移动。

    结果是一个无符号的 64 位整数。该值被截断为 64 位。特别是,如果移位计数大于或等于无符号 64 位数字的宽度,则结果为零。

    mysql> SELECT 1 << 2;
            -> 4
  • >>

    将 longlong ( BIGINT) 数字右移。

    结果是一个无符号的 64 位整数。该值被截断为 64 位。特别是,如果移位计数大于或等于无符号 64 位数字的宽度,则结果为零。

    mysql> SELECT 4 >> 2;
            -> 1
  • ~

    反转所有位。

    结果是一个无符号的 64 位整数。

    mysql> SELECT 5 & ~1;
            -> 4
  • BIT_COUNT(N)

    返回在参数中设置 N为无符号 64 位整数的位数,或者 NULL如果参数是 NULL.

    mysql> SELECT BIT_COUNT(29), BIT_COUNT(b'101010');
            -> 4, 3