它没有转换回 IPv4 人类可读格式,而是转换为 IPv6,因为 INET6_NTOA 的参数(二进制值)是 16 个字节。
该函数将其视为 IPv6 地址的表示,而不是 IPv4 地址,它只有四个字节。
我认为问题可以追溯到问题中的第一行 SQL,即转换为 BINARY(16)。它返回 16 个字节的 固定长度。从为 IPv4 地址返回的四个字节开始,然后在右侧用零填充,最长为 16 个字节。
如果我们移除强制转换为固定长度,并允许INET6_ATON 函数的结果只有四个字节会发生什么?
当存储在数据库中的值只有四个字节时会发生什么?
如果我们更正stats 表的内容,将那个 16 字节的二进制值(IPv6 地址的表示)更改为 IPv4 地址的 4 字节的二进制表示
UPDATE `stats`
SET ip_binary = INET6_ATON('66.249.64.90')
WHERE ip_binary = CAST(INET6_ATON('66.249.64.90') AS BINARY(16))
--或者--
UPDATE `stats`
SET ip_binary = X'42f9405a' + 0
WHERE ip_binary = X'42f9405a000000000000000000000000' + 0
跟进
问题说...“使用 [表达式] 在数据库 [列] 中存储 IP 地址,如下所示:
cast(INET6_ATON(trim(:ipbinary)) as binary(16)))
我们不需要使用CAST。而且我们不需要使用CONVERT、HEX/UNHEX 或SUBSTR。用相同的表达式转换 IPv4 和 IPv6 地址:
INSERT ... ip_binary ... VALUES ( ... , INET6_ATON( :ip_string ) , ...
并将它们转换回这样的字符串:
SELECT ... , INET6_NTOA( ip_binary ) AS ip_string , ...
CAST、CONVERT、SUBSTR、HEX/UNHEX 的繁琐令人困惑,并导致无法正常工作。
为了更正已经存储在数据库中的值,我们需要一种方法来区分哪些 16 字节二进制表示实际上是 IPv4 地址,应该存储为 4 字节。
如果ip_delete包含字符串表示,我们可以重新转换为二进制表示。
UPDATE `stats`
SET ip_binary = INET6_ATON( ip_delete )
演示
CREATE TABLE `addr` (ip_string VARCHAR(45), ip_binary VARBINARY(16)) ;
INSERT INTO `addr` VALUES ( '66.249.64.90' , INET6_ATON( '66.249.64.90' ));
INSERT INTO `addr` VALUES ( '127.0.0.1' , INET6_ATON( '127.0.0.1' ));
INSERT INTO `addr` VALUES ( '192.168.1.1' , INET6_ATON( '192.168.0.1' ));
INSERT INTO `addr` VALUES ( '2001:4860:4860::8888' , INET6_ATON( '2001:4860:4860::8888' ));
SELECT ip_string, HEX(ip_binary), INET6_NTOA(ip_binary) FROM `addr` ;
ip_string HEX(ip_binary) INET6_NTOA(ip_binary)
-------------------- -------------------------------- -----------------------
66.249.64.90 42F9405A 66.249.64.90
127.0.0.1 7F000001 127.0.0.1
192.168.1.1 C0A80001 192.168.0.1
2001:4860:4860::8888 20014860486000000000000000008888 2001:4860:4860::8888