14.12 位函数和运算符
以下列表描述可用的位函数和运算符:
-
位 wise OR。
结果类型取决于是否将参数评估为二进制字符串或数字:
-
二进制字符串评估发生在参数有二进制字符串类型,且至少一个参数不是十六进制字面量、位字面量或
NULL
字面量。否则,进行必要的无符号 64 位整数转换。 -
二进制字符串评估产生与参数相同长度的二进制字符串。如果参数长度不等,出现
ER_INVALID_BITWISE_OPERANDS_SIZE
错误。数字评估产生无符号64位整数。
更多信息,请参见本节的介绍讨论。
Press CTRL+C to copymysql> SELECT 29 | 15; -> 31 mysql> SELECT _binary X'40404040' | X'01020304'; -> 'ABCD'
在mysql客户端中,二进制字符串结果使用十六进制表示,取决于
--binary-as-hex
的值。关于该选项的更多信息,请参见第6.5.1节,“mysql — The MySQL Command-Line Client”. -
-
位与。
结果类型取决于参数是否被评估为二进制字符串或数字:
-
二进制字符串评估发生在参数有二进制字符串类型,且至少一个不是十六进制字面量、位字面量或
NULL
字面量时。否则,进行数字评估,并将参数转换为无符号64位整数以满足要求。 -
二进制字符串评估产生与参数相同长度的二进制字符串。如果参数长度不等,出现
ER_INVALID_BITWISE_OPERANDS_SIZE
错误。数字评估产生无符号64位整数。
更多信息,请参阅本节的介绍讨论。
Press CTRL+C to copymysql> SELECT 29 & 15; -> 13 mysql> SELECT HEX(_binary X'FF' & b'11110000'); -> 'F0'
在mysql客户端中,二进制字符串结果使用十六进制表示,取决于
--binary-as-hex
的值。关于该选项的更多信息,请参阅第6.5.1节,“mysql — The MySQL Command-Line Client”. -
-
异或。
结果类型取决于参数是否被评估为二进制字符串或数字:
-
二进制字符串评估发生在参数有二进制字符串类型,且至少一个不是十六进制字面量、位字面量或
NULL
字面量时。否则,进行数字评估,并将参数转换为无符号64位整数以必要。 -
二进制字符串评估产生与参数相同长度的二进制字符串。如果参数长度不等,出现
ER_INVALID_BITWISE_OPERANDS_SIZE
错误。数字评估产生无符号64位整数。
更多信息,请参阅本节的介绍讨论。
Press CTRL+C to copymysql> SELECT 1 ^ 1; -> 0 mysql> SELECT 1 ^ 0; -> 1 mysql> SELECT 11 ^ 3; -> 8 mysql> SELECT HEX(_binary X'FEDC' ^ X'1111'); -> 'EFCD'
在mysql客户端中,二进制字符串结果使用十六进制表示,取决于
--binary-as-hex
的值。关于该选项的更多信息,请参阅第6.5.1节,“mysql — The MySQL Command-Line Client”. -
-
将长整数(
BIGINT
)或二进制字符串左移。结果类型取决于位操作数是否被评估为二进制字符串或数字:
-
二进制字符串评估发生在位操作数具有二进制字符串类型,且不是十六进制字面量、位字面量或
NULL
字面量时。否则,进行数字评估,并将参数转换为无符号64位整数,如有必要。 -
二进制字符串评估产生与位参数相同长度的二进制字符串。数字评估产生无符号64位整数。
无论参数类型,右移操作将超出末尾的位丢失,而不发出警告。如果右移计数大于或等于位参数中的位数,则结果中的所有位都是0。
更多信息,请参阅本节的介绍讨论。
Press CTRL+C to copymysql> SELECT 1 << 2; -> 4 mysql> SELECT HEX(_binary X'00FF00FF00FF' << 8); -> 'FF00FF00FF00'
如果在mysql客户端中调用位移操作,二进制字符串结果将使用十六进制表示,取决于
--binary-as-hex
的值。关于该选项的更多信息,请参阅第6.5.1节,“mysql — The MySQL Command-Line Client”. -
-
>>
将长整数(
BIGINT
)数字或二进制字符串右移。结果类型取决于位参数是否被评估为二进制字符串或数字:
-
二进制字符串评估发生在位参数具有二进制字符串类型且不是十六进制字面量、位字面量或
NULL
字面量时。否则,进行必要的无符号64位整数转换以执行数字评估。 -
二进制字符串评估产生与位参数相同长度的二进制字符串。数字评估产生无符号64位整数。
无论参数类型,右移操作将超出末尾的位丢失,而不发出警告。如果移位计数大于或等于位参数中的位数,则结果中的所有位都是0。
更多信息,请参见本节的介绍讨论。
Press CTRL+C to copymysql> SELECT 4 >> 2; -> 1 mysql> SELECT HEX(_binary X'00FF00FF00FF' >> 8); -> '0000FF00FF00'
如果在mysql客户端中调用位移操作,二进制字符串结果将使用十六进制表示,取决于
--binary-as-hex
的值。关于该选项的更多信息,请参见第6.5.1节,“mysql — The MySQL Command-Line Client”. -
-
~
反转所有位。
结果类型取决于是否将位参数评估为二进制字符串或数字:
-
二进制字符串评估发生在位参数具有二进制字符串类型且不是十六进制字面量、位字面量或
NULL
字面量时。否则,进行必要的无符号64位整数转换。 -
二进制字符串评估产生与位参数相同长度的二进制字符串。数字评估产生无符号64位整数。
更多信息,请参见本节的介绍讨论。
Press CTRL+C to copymysql> SELECT 5 & ~1; -> 4 mysql> SELECT HEX(~X'0000FFFF1111EEEE'); -> 'FFFF0000EEEE1111'
如果从mysql 客户端中调用按位反转,二进制字符串结果将以十六进制表示,取决于
--binary-as-hex
选项的值。关于该选项的更多信息,请参见第6.5.1节,“mysql — MySQL 命令行客户端”。 -
-
BIT_COUNT(
N
)返回一个无符号64位整数,表示参数
N
中的置位个数,如果参数为NULL
则返回NULL
。Press CTRL+C to copymysql> SELECT BIT_COUNT(64), BIT_COUNT(BINARY 64); -> 1, 7 mysql> SELECT BIT_COUNT('64'), BIT_COUNT(_binary '64'); -> 1, 7 mysql> SELECT BIT_COUNT(X'40'), BIT_COUNT(_binary X'40'); -> 1, 1
位函数和操作符包括BIT_COUNT()
、BIT_AND()
、BIT_OR()
、BIT_XOR()
、&
、|
、^
、~
、<<
和>>
。(BIT_AND()
、BIT_OR()
和BIT_XOR()
聚合函数在第14.19.1节,“聚合函数描述”中进行了描述。)
位函数和操作符允许二进制字符串类型的参数(BINARY
、VARBINARY
和BLOB
类型),并返回值的类型。非二进制字符串参数将被转换为BIGINT
。
MySQL 8.4 直接处理二进制字符串参数(不进行转换),并生成二进制字符串结果。不是整数或二进制字符串的参数将被转换为整数。
可以认为是二进制字符串的参数包括列值、存储程序参数、局部变量和用户定义变量,类型为 BINARY
、VARBINARY
或 BLOB
类型。
可以使用十六进制字面量或位字面量来指定位操作的参数,MySQL 在所有位参数都是十六进制或位字面量时对位操作进行数值上下文的评估。要将位操作评估为二进制字符串,请至少在一个字面值中使用 _binary
引用符。
-
这类位操作将十六进制字面量和位字面量评估为整数:
Press CTRL+C to copymysql> SELECT X'40' | X'01', b'11110001' & b'01001111'; +---------------+---------------------------+ | X'40' | X'01' | b'11110001' & b'01001111' | +---------------+---------------------------+ | 65 | 65 | +---------------+---------------------------+
-
这类位操作将十六进制字面量和位字面量评估为二进制字符串,归因于
_binary
引用符:Press CTRL+C to copymysql> SELECT _binary X'40' | X'01', b'11110001' & _binary b'01001111'; +-----------------------+-----------------------------------+ | _binary X'40' | X'01' | b'11110001' & _binary b'01001111' | +-----------------------+-----------------------------------+ | A | A | +-----------------------+-----------------------------------+
虽然两个语句都生成结果的数值为 65,但是第二个语句在二进制字符串上下文中,65 是 ASCII A
。
在数值评估上下文中,十六进制字面量和位字面量参数的允许值最多为 64 位,而在二进制字符串评估上下文中,允许的参数(结果)可以超过 64 位:
Press CTRL+C to copymysql> SELECT _binary X'4040404040404040' | X'0102030405060708'; +---------------------------------------------------+ | _binary X'4040404040404040' | X'0102030405060708' | +---------------------------------------------------+ | ABCDEFGH | +---------------------------------------------------+
可以通过以下几种方式在位操作中引用十六进制字面量或位字面量来导致二进制字符串评估:
Press CTRL+C to copy_binary literal BINARY literal CAST(literal AS BINARY)
另一种生产十六进制字面量或位字面量的二进制字符串评估方法是将其赋值给用户定义变量,这样结果变量就具有二进制字符串类型:
Press CTRL+C to copymysql> SET @v1 = X'40', @v2 = X'01', @v3 = b'11110001', @v4 = b'01001111'; mysql> SELECT @v1 | @v2, @v3 & @v4; +-----------+-----------+ | @v1 | @v2 | @v3 & @v4 | +-----------+-----------+ | A | A | +-----------+-----------+
在二进制字符串上下文中,位操作参数必须相同长度否则出现ER_INVALID_BITWISE_OPERANDS_SIZE
错误:
Press CTRL+C to copymysql> SELECT _binary X'40' | X'0001'; ERROR 3513 (HY000): Binary operands of bitwise operators must be of equal length
满足相同长度要求,可以将较短值前面填充零或如果较长值以零开头且较短结果值可接受,去除它们:
Press CTRL+C to copymysql> SELECT _binary X'0040' | X'0001'; +---------------------------+ | _binary X'0040' | X'0001' | +---------------------------+ | A | +---------------------------+ mysql> SELECT _binary X'40' | X'01'; +-----------------------+ | _binary X'40' | X'01' | +-----------------------+ | A | +-----------------------+
填充或去除也可以使用函数如LPAD()
、RPAD()
、SUBSTR()
或CAST()
。在这种情况下,表达式参数不再都是字面量<_binary>变得多余。示例:
Press CTRL+C to copymysql> SELECT LPAD(X'40', 2, X'00') | X'0001'; +---------------------------------+ | LPAD(X'40', 2, X'00') | X'0001' | +---------------------------------+ | A | +---------------------------------+ mysql> SELECT X'40' | SUBSTR(X'0001', 2, 1); +-------------------------------+ | X'40' | SUBSTR(X'0001', 2, 1) | +-------------------------------+ | A | +-------------------------------+
下面的示例展示了使用位操作来提取 UUID 值的部分,例如时间戳和 IEEE 802 节点号。这项技术需要为每个提取部分创建位掩码。
将文本 UUID 转换为对应的 16 字节二进制值,以便在二进制字符串上下使用位操作:
Press CTRL+C to copymysql> SET @uuid = UUID_TO_BIN('6ccd780c-baba-1026-9564-5b8c656024db'); mysql> SELECT HEX(@uuid); +----------------------------------+ | HEX(@uuid) | +----------------------------------+ | 6CCD780CBABA102695645B8C656024DB | +----------------------------------+
构建时间戳和节点号部分的位掩码。时间戳由前三个部分组成(64 位,0 到 63),节点号是最后一个部分(48 位,80 到 127):
Press CTRL+C to copymysql> SET @ts_mask = CAST(X'FFFFFFFFFFFFFFFF' AS BINARY(16)); mysql> SET @node_mask = CAST(X'FFFFFFFFFFFF' AS BINARY(16)) >> 80; mysql> SELECT HEX(@ts_mask); +----------------------------------+ | HEX(@ts_mask) | +----------------------------------+ | FFFFFFFFFFFFFFFF0000000000000000 | +----------------------------------+ mysql> SELECT HEX(@node_mask); +----------------------------------+ | HEX(@node_mask) | +----------------------------------+ | 00000000000000000000FFFFFFFFFFFF | +----------------------------------+
在这里使用了 CAST(... AS BINARY(16))
函数,因为掩码必须与 UUID 值相同长度。同样结果可以使用其他函数将掩码填充到所需长度:
Press CTRL+C to copySET @ts_mask= RPAD(X'FFFFFFFFFFFFFFFF' , 16, X'00'); SET @node_mask = LPAD(X'FFFFFFFFFFFF', 16, X'00') ;
使用掩码来提取时间戳和节点号部分:
Press CTRL+C to copymysql> SELECT HEX(@uuid & @ts_mask) AS 'timestamp part'; +----------------------------------+ | timestamp part | +----------------------------------+ | 6CCD780CBABA10260000000000000000 | +----------------------------------+ mysql> SELECT HEX(@uuid & @node_mask) AS 'node part'; +----------------------------------+ | node part | +----------------------------------+ | 000000000000000000005B8C656024DB | +----------------------------------+
前面的示例使用了这些位操作:右移 (>>
) 和按位与 (&
).
UUID_TO_BIN()
函数带有一个标志,导致结果二进制 UUID 值的位重新排列。如果使用该标志,修改提取掩码相应地。
下面的示例使用位操作来提取 IPv6 地址的网络和主机部分。假设网络部分长度为 80 位,那么主机部分长度为 128 - 80 = 48 位。要提取地址的网络和主机部分,先将其转换为二进制字符串,然后在二进制字符串上下使用位操作:
将文本 IPv6 地址转换为对应的二进制字符串:
Press CTRL+C to copymysql> SET @ip = INET6_ATON('fe80::219:d1ff:fe91:1a72');
定义网络长度(以位为单位):
Press CTRL+C to copymysql> SET @net_len = 80;
通过左或右移位操作构建网络和主机掩码。要做到这点,首先从地址::
开始,这是所有零的简写形式,如下所示:
Press CTRL+C to copymysql> SELECT HEX(INET6_ATON('::')) AS 'all zeros'; +----------------------------------+ | all zeros | +----------------------------------+ | 00000000000000000000000000000000 | +----------------------------------+
生成补码(所有.ones),使用~
操作符反转位:
Press CTRL+C to copymysql> SELECT HEX(~INET6_ATON('::')) AS 'all ones'; +----------------------------------+ | all ones | +----------------------------------+ | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF | +----------------------------------+
将所有.ones值左或右移位来生成网络和主机掩码:
Press CTRL+C to copymysql> SET @net_mask = ~INET6_ATON('::') << (128 - @net_len); mysql> SET @host_mask = ~INET6_ATON('::') >> @net_len;
显示掩码以验证它们覆盖了正确的地址部分:
Press CTRL+C to copymysql> SELECT INET6_NTOA(@net_mask) AS 'network mask'; +----------------------------+ | network mask | +----------------------------+ | ffff:ffff:ffff:ffff:ffff:: | +----------------------------+ mysql> SELECT INET6_NTOA(@host_mask) AS 'host mask'; +------------------------+ | host mask | +------------------------+ | ::ffff:255.255.255.255 | +------------------------+
提取并显示地址的网络和主机部分:
Press CTRL+C to copymysql> SET @net_part = @ip & @net_mask; mysql> SET @host_part = @ip & @host_mask; mysql> SELECT INET6_NTOA(@net_part) AS 'network part'; +-----------------+ | network part | +-----------------+ | fe80::219:0:0:0 | +-----------------+ mysql> SELECT INET6_NTOA(@host_part) AS 'host part'; +------------------+ | host part | +------------------+ | ::d1ff:fe91:1a72 | +------------------+
前面的示例使用这些位操作:补码(~
)、左移位(<<
)和位与(&
)。
以下讨论每个位操作的参数处理,关于位操作中的字面值处理提供了更多信息。
对于&
、|
和^
位操作,结果类型取决于是否将参数评估为二进制字符串或数字:
-
二进制字符串评估发生在参数类型为二进制字符串且至少有一个不是十六进制字面量、位字面量或
NULL
字面量时,否则进行数值评估,必要时将参数转换为无符号64位整数。 -
二进制字符串评估生成与参数相同长度的二进制字符串。如果参数长度不等,出现
ER_INVALID_BITWISE_OPERANDS_SIZE
错误。数值评估生成无符号64位整数。
数值评估示例:
Press CTRL+C to copymysql> SELECT 64 | 1, X'40' | X'01'; +--------+---------------+ | 64 | 1 | X'40' | X'01' | +--------+---------------+ | 65 | 65 | +--------+---------------+
二进制字符串评估示例:
Press CTRL+C to copymysql> SELECT _binary X'40' | X'01'; +-----------------------+ | _binary X'40' | X'01' | +-----------------------+ | A | +-----------------------+ mysql> SET @var1 = X'40', @var2 = X'01'; mysql> SELECT @var1 | @var2; +---------------+ | @var1 | @var2 | +---------------+ | A | +---------------+
对于~
、<<
和>>
位操作,结果类型取决于位参数是否被评估为二进制字符串或数值:
-
二进制字符串评估发生在位参数类型为二进制字符串且不是十六进制字面量、位字面量或
NULL
字面量时,否则进行数值评估,必要时将参数转换为无符号64位整数。 -
二进制字符串评估生成与位参数相同长度的二进制字符串。数值评估生成无符号64位整数。
对于位移操作,位移后的值将被丢弃,而不管参数类型。特别是,如果位移计数大于或等于位参数的位数,则结果中的所有位都是0。
数字评估示例:
Press CTRL+C to copymysql> SELECT ~0, 64 << 2, X'40' << 2; +----------------------+---------+------------+ | ~0 | 64 << 2 | X'40' << 2 | +----------------------+---------+------------+ | 18446744073709551615 | 256 | 256 | +----------------------+---------+------------+
二进制字符串评估示例:
Press CTRL+C to copymysql> SELECT HEX(_binary X'1111000022220000' >> 16); +----------------------------------------+ | HEX(_binary X'1111000022220000' >> 16) | +----------------------------------------+ | 0000111100002222 | +----------------------------------------+ mysql> SELECT HEX(_binary X'1111000022220000' << 16); +----------------------------------------+ | HEX(_binary X'1111000022220000' << 16) | +----------------------------------------+ | 0000222200000000 | +----------------------------------------+ mysql> SET @var1 = X'F0F0F0F0'; mysql> SELECT HEX(~@var1); +-------------+ | HEX(~@var1) | +-------------+ | 0F0F0F0F | +-------------+
BIT_COUNT()
函数总是返回无符号64位整数,或者如果参数为 NULL
则返回 NULL
。
Press CTRL+C to copymysql> SELECT BIT_COUNT(127); +----------------+ | BIT_COUNT(127) | +----------------+ | 7 | +----------------+ mysql> SELECT BIT_COUNT(b'010101'), BIT_COUNT(_binary b'010101'); +----------------------+------------------------------+ | BIT_COUNT(b'010101') | BIT_COUNT(_binary b'010101') | +----------------------+------------------------------+ | 3 | 3 | +----------------------+------------------------------+
对于BIT_AND()
、BIT_OR()
和 BIT_XOR()
位函数,结果类型取决于函数参数值是否被评估为二进制字符串或数字:
-
二进制字符串评估发生在参数值具有二进制字符串类型且不是十六进制字面量、位字面量或
NULL
字面量时。否则,进行必要的无符号64位整数转换以进行数字评估。 -
二进制字符串评估产生与参数值相同的长度的二进制字符串。如果参数值不等长,出现
ER_INVALID_BITWISE_OPERANDS_SIZE
错误。如果参数值超过511字节,出现ER_INVALID_BITWISE_AGGREGATE_OPERANDS_SIZE
错误。数值评估产生无符号64位整数。
NULL
值除非所有值都是NULL
,否则不影响结果。在这种情况下,结果是一个与参数值相同长度的中性值(对于BIT_AND()
,所有位为1;对于BIT_OR()
,所有位为0;对于BIT_XOR()
,结果是中性值)。
示例:
Press CTRL+C to copymysql> CREATE TABLE t (group_id INT, a VARBINARY(6)); mysql> INSERT INTO t VALUES (1, NULL); mysql> INSERT INTO t VALUES (1, NULL); mysql> INSERT INTO t VALUES (2, NULL); mysql> INSERT INTO t VALUES (2, X'1234'); mysql> INSERT INTO t VALUES (2, X'FF34'); mysql> SELECT HEX(BIT_AND(a)), HEX(BIT_OR(a)), HEX(BIT_XOR(a)) FROM t GROUP BY group_id; +-----------------+----------------+-----------------+ | HEX(BIT_AND(a)) | HEX(BIT_OR(a)) | HEX(BIT_XOR(a)) | +-----------------+----------------+-----------------+ | FFFFFFFFFFFF | 000000000000 | 000000000000 | | 1234 | FF34 | ED00 | +-----------------+----------------+-----------------+
十六进制字面量、位字面量和NULL字面量的特殊处理
MySQL 8.4 在所有位操作数都是十六进制字面量、位字面量或NULL
字面量时,评估位操作在数字上下文中。这意味着,如果所有位操作数是未标记的十六进制字面量、位字面量或NULL
字面量,不会使用二进制字符串评估(除非它们被明确指定为二进制字符串,例如使用 BINARY
运算符或其他方式)。
示例:
-
这些位操作在数字上下文中评估字面量,产生一个
BIGINT
结果:Press CTRL+C to copyb'0001' | b'0010' X'0008' << 8
-
这些位操作在数字上下文中评估
NULL
,产生一个BIGINT
结果,该结果是一个NULL
值:Press CTRL+C to copyNULL & NULL NULL >> 4
可以通过明确指示至少有一个参数是二进制字符串来使那些操作在二进制字符串上下文中评估:
Press CTRL+C to copy_binary b'0001' | b'0010' _binary X'0008' << 8 BINARY NULL & NULL BINARY NULL >> 4
最后两个表达式的结果仍然是NULL
,只是结果的数据类型是一个二进制字符串类型,而不是整数类型。