代码之家  ›  专栏  ›  技术社区  ›  Poul Bak

在MySql/C中#如何快速地将128位整数转换为varbinary(16)

  •  0
  • Poul Bak  · 技术社区  · 5 年前

    我远非MySql专家,事实上我从未编写过“存储过程”,这可能是我在这里需要的。

    我所拥有的 :一个(最多)128位整数-作为字符串(实际上是一个Ipv6地址)。

    我想要什么 :一个二进制(16)值。

    我正在尝试转换一个128位整数,这样它就可以作为varbinary(16)存储在MySql中。

    据我所知,MySql中唯一能处理如此大的整数的类型是 decimal 数据类型。 然而,似乎没有从中转换 十进制的 到十六进制字符串或 varbinary .我想用 varbinary(16) 原因有二:

    1. 这是最紧凑的。
    2. 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("')");
    

    我更喜欢“在MySql中实现”,因为这通常更快,但我可以使用C#/MySql的任何组合——可能是一个存储过程。

    有没有办法在MySql中实现这一点?

    0 回复  |  直到 5 年前
        1
  •  1
  •   Rick James diyism    5 年前

    在MySQL中进行操作将是乏味的。下面是存储函数中可能包含的内容的概要。

    把它们塞进盒子里 DECIMAL(40,0) 列(假设该列足够大)。

    执行div和mod 2^16获得8个块;将每个转换为十六进制( CONV('12345', 10, 16) ).

    然后做以下两件事之一:

    计划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') ,... ) 把它塞进 二进制(16)

        2
  •  0
  •   Poul Bak    5 年前

    在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地址的十进制表示形式,并返回 varbinary(16) -16字节长的二进制字符串。

    我先声明 TwoExp64 作为2^64。然后我 DIV MOD Int128 用这个值得到2 BIGINT 值,这样我就可以对它们进行十六进制/反求,以获得二进制字符串/字节数组(左填充为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的答案不同之处在于:

    1. 参数类型必须使用`DECIMAL(60,20),否则分割将不正确。

    2. 我只对 decimal 类型这给了我两个 比金 价值观这加快了速度。

    顺便说一句:在测试时,我犯了一个典型的错误:在我的开发计算机上测试,它不忙,有很多内核可以并行运行。该测试表明,C#版本使用 BigInteger 是最快的。然而,当我最终在繁忙的生产服务器上测试时,上面显示的MySql存储函数是最快的。

    所以,记住也要在生产服务器上加速测试:)