在 MySql/C# 如何快速将 128 位整数转换为 varbinary(16)
In MySql/C# how to convert a 128 bit integer to a varbinary(16) in a fast manner
我远不是MySql专家,事实上我从未写过'a stored procedure',这可能正是我需要的。
我有什么:一个(最多)128 位整数 - 作为一个字符串(它实际上是一个 Ipv6 地址)。
我想要的:一个MySql varbinary(16)值。
我正在尝试转换一个 128 位整数,因此它可以作为 varbinary(16) 存储在 MySql 中。
据我所知,MySql 中唯一可以处理如此大整数的类型是 decimal
数据类型。
然而,似乎没有从 decimal
到十六进制字符串或 varbinary
的转换。我想使用 varbinary(16)
有两个原因:
- 它是最紧凑的。
- MySql 和 C# 都可以直接从这种格式创建 Ipv6 地址。而反之亦然。
我用'CAST'、'HEX'和ip转换函数尝试了几次(惨不忍睹)。他们似乎不适用于 Decimal
.
但是,我设法在 C# 中创建了这个查询,虽然有效,但速度很慢:
string value1 = "281470698520576"; // example1 (less than 128 bit - must be leftpadded with zeros).
string value2 = "42541870534966271977089220242718064640"; // example2 (128 bit)
StringBuilder sb = new StringBuilder("INSERT INTO ......... VALUES");
sb.Append("(").Append("UNHEX('").Append(BigInteger.Parse(value1).ToString("x32")).Append("'), ")
.Append("UNHEX('").Append(BigInteger.Parse(value2 ).ToString("x32")).Append("')");
我更喜欢 'do it in MySql',因为它通常更快,但我可以使用 C#/MySql 的任意组合 - 可能是一个存储过程。
在MySql有没有办法做到这一点?
MySQL 中的操作会很乏味。这是存储函数中可能包含的内容的概述。
将它们填入 DECIMAL(40,0)
列(假设足够大)。
做div和mod 2^16得到8块;将每个转换为十六进制 (CONV('12345', 10, 16)
).
然后执行以下 2 项操作之一:
方案A:
CONCAT_WS(':', ...)
组合棋子。这将是有效的 IPv6 语法,但不是最低限度的。 (无需担心缺少前导零。)
INET6_ATON('...')
将生成 0x...
INSERT INTO ... VALUES ( ..., 0xFDFE0000000000005A55CAFFFEFA9089 ,... )
将其填入 BINARY(16)
列。
B计划:
CONCAT
件。
确保每个块都有前导零:RIGHT(CONCAT('000', chunk), 4)
INSERT INTO ... VALUES ( ..., UNHEX('FDFE0000000000005A55CAFFFEFA9089') ,... )
将其填入 BINARY(16)
列。
在 MySql 我创建了一个 stored function
.
在这里 - 给未来的读者:
DELIMITER $$
CREATE DEFINER=`mydb`@`%` FUNCTION `Int128ToVarBinary`(`Int128` DECIMAL(60,20) UNSIGNED) RETURNS varbinary(16)
NO SQL
DETERMINISTIC
SQL SECURITY INVOKER
COMMENT 'Converts 128 bit (DECIMAL) Ipv6 address to VARBINARY(16)'
BEGIN
DECLARE TwoExp64 DECIMAL(20) UNSIGNED;
DECLARE HighPart BIGINT UNSIGNED;
DECLARE LowPart BIGINT UNSIGNED;
SET TwoExp64 = 18446744073709551616;
SET HighPart = Int128 DIV TwoExp64;
SET LowPart = Int128 MOD TwoExp64;
RETURN UNHEX(CONCAT(LPAD(HEX(HighPart), 16, '0'), LPAD(HEX(LowPart), 16, '0')));
END$$
DELIMITER ;
解释:
该函数采用一个整数(最多 128 位)作为其参数,此处包含 Ipv6 地址的十进制表示形式和 returns 一个 varbinary(16)
- 16 字节长的二进制字符串。
我首先声明 TwoExp64
为 2^64。然后我 DIV
和 MOD
Int128
用这个值得到 2 BIGINT
值所以我可以 HEX/UNHEX 他们得到二进制 string/byte 数组(左边填充到 16 的长度)。
我如何在 C# 中使用它构建查询:
StringBuilder sb = new StringBuilder("INSERT INTO ......... VALUES", 0x80000);
bool dataOk = false;
// and then in a loop coming from a csv file:
string value1 = "281470698520576"; // example1 (less than 128 bit - must be leftpadded with zeros).
string value2 = "42541870534966271977089220242718064640"; // example2 (128 bit)
sb.Append((dataOk) ? ", " : "").Append("(").Append("Int128ToVarBinary(").Append(value1).Append("), ").Append("Int128ToVarBinary(").Append(value2).Append("))"); dataOk = true;
与@Rick James 的回答不同的是:
参数类型一定要用`DECIMAL(60,20),否则除法不正确
我只对 decimal
类型进行了一次 DIV/MOD 操作。这给了我 2 BIGINT
个值。这样可以加快速度。
顺便说一句:在测试时,我犯了一个典型的错误:在我的开发计算机上进行测试,该计算机并不繁忙并且有足够的内核可以并行 运行。该测试表明使用 BigInteger
的 C# 版本是最快的。然而,当我最终在繁忙的生产服务器上进行测试时,上面显示的 MySql 存储函数是最快的。
所以,记得也要在生产服务器上进行速度测试:)
我远不是MySql专家,事实上我从未写过'a stored procedure',这可能正是我需要的。
我有什么:一个(最多)128 位整数 - 作为一个字符串(它实际上是一个 Ipv6 地址)。
我想要的:一个MySql varbinary(16)值。
我正在尝试转换一个 128 位整数,因此它可以作为 varbinary(16) 存储在 MySql 中。
据我所知,MySql 中唯一可以处理如此大整数的类型是 decimal
数据类型。
然而,似乎没有从 decimal
到十六进制字符串或 varbinary
的转换。我想使用 varbinary(16)
有两个原因:
- 它是最紧凑的。
- MySql 和 C# 都可以直接从这种格式创建 Ipv6 地址。而反之亦然。
我用'CAST'、'HEX'和ip转换函数尝试了几次(惨不忍睹)。他们似乎不适用于 Decimal
.
但是,我设法在 C# 中创建了这个查询,虽然有效,但速度很慢:
string value1 = "281470698520576"; // example1 (less than 128 bit - must be leftpadded with zeros).
string value2 = "42541870534966271977089220242718064640"; // example2 (128 bit)
StringBuilder sb = new StringBuilder("INSERT INTO ......... VALUES");
sb.Append("(").Append("UNHEX('").Append(BigInteger.Parse(value1).ToString("x32")).Append("'), ")
.Append("UNHEX('").Append(BigInteger.Parse(value2 ).ToString("x32")).Append("')");
我更喜欢 'do it in MySql',因为它通常更快,但我可以使用 C#/MySql 的任意组合 - 可能是一个存储过程。
在MySql有没有办法做到这一点?
MySQL 中的操作会很乏味。这是存储函数中可能包含的内容的概述。
将它们填入 DECIMAL(40,0)
列(假设足够大)。
做div和mod 2^16得到8块;将每个转换为十六进制 (CONV('12345', 10, 16)
).
然后执行以下 2 项操作之一:
方案A:
CONCAT_WS(':', ...)
组合棋子。这将是有效的 IPv6 语法,但不是最低限度的。 (无需担心缺少前导零。)
INET6_ATON('...')
将生成 0x...
INSERT INTO ... VALUES ( ..., 0xFDFE0000000000005A55CAFFFEFA9089 ,... )
将其填入 BINARY(16)
列。
B计划:
CONCAT
件。
确保每个块都有前导零:RIGHT(CONCAT('000', chunk), 4)
INSERT INTO ... VALUES ( ..., UNHEX('FDFE0000000000005A55CAFFFEFA9089') ,... )
将其填入 BINARY(16)
列。
在 MySql 我创建了一个 stored function
.
在这里 - 给未来的读者:
DELIMITER $$
CREATE DEFINER=`mydb`@`%` FUNCTION `Int128ToVarBinary`(`Int128` DECIMAL(60,20) UNSIGNED) RETURNS varbinary(16)
NO SQL
DETERMINISTIC
SQL SECURITY INVOKER
COMMENT 'Converts 128 bit (DECIMAL) Ipv6 address to VARBINARY(16)'
BEGIN
DECLARE TwoExp64 DECIMAL(20) UNSIGNED;
DECLARE HighPart BIGINT UNSIGNED;
DECLARE LowPart BIGINT UNSIGNED;
SET TwoExp64 = 18446744073709551616;
SET HighPart = Int128 DIV TwoExp64;
SET LowPart = Int128 MOD TwoExp64;
RETURN UNHEX(CONCAT(LPAD(HEX(HighPart), 16, '0'), LPAD(HEX(LowPart), 16, '0')));
END$$
DELIMITER ;
解释:
该函数采用一个整数(最多 128 位)作为其参数,此处包含 Ipv6 地址的十进制表示形式和 returns 一个 varbinary(16)
- 16 字节长的二进制字符串。
我首先声明 TwoExp64
为 2^64。然后我 DIV
和 MOD
Int128
用这个值得到 2 BIGINT
值所以我可以 HEX/UNHEX 他们得到二进制 string/byte 数组(左边填充到 16 的长度)。
我如何在 C# 中使用它构建查询:
StringBuilder sb = new StringBuilder("INSERT INTO ......... VALUES", 0x80000);
bool dataOk = false;
// and then in a loop coming from a csv file:
string value1 = "281470698520576"; // example1 (less than 128 bit - must be leftpadded with zeros).
string value2 = "42541870534966271977089220242718064640"; // example2 (128 bit)
sb.Append((dataOk) ? ", " : "").Append("(").Append("Int128ToVarBinary(").Append(value1).Append("), ").Append("Int128ToVarBinary(").Append(value2).Append("))"); dataOk = true;
与@Rick James 的回答不同的是:
参数类型一定要用`DECIMAL(60,20),否则除法不正确
我只对
decimal
类型进行了一次 DIV/MOD 操作。这给了我 2BIGINT
个值。这样可以加快速度。
顺便说一句:在测试时,我犯了一个典型的错误:在我的开发计算机上进行测试,该计算机并不繁忙并且有足够的内核可以并行 运行。该测试表明使用 BigInteger
的 C# 版本是最快的。然而,当我最终在繁忙的生产服务器上进行测试时,上面显示的 MySql 存储函数是最快的。
所以,记得也要在生产服务器上进行速度测试:)