【问题标题】:mySQL: get hash value for each row?mySQL:获取每一行的哈希值?
【发布时间】:2012-01-09 23:24:43
【问题描述】:

目前我正在手动创建一个字符串,在其中连接表中每一行中的所有值。我正在对每一行的这个字符串进行哈希处理,以获得该行当前值(/状态)的哈希值,稍后我将使用它来确定该行是否已更改。

mySQL 中是否有一种内置方法可以为每一行获取唯一的哈希值,而不是手动执行此操作?

【问题讨论】:

    标签: mysql hash


    【解决方案1】:

    你可以做类似的事情

    SELECT MD5(concat(field1, field2, field3, ...)) AS rowhash
    

    但你不能不列出你想要的字段,因为concat(*) 不是一个选项(语法错误)。

    【讨论】:

    • 如果其中一个字段为 NULL,这将不起作用。您应该将 COALESCE(fieldX, 'some_NULL_placeholder') 用于 NULLable 字段。
    • @Mrskman 该文档说它确实跳过了分隔符参数之后的任何 NULL 值(至少从 MySQL 8.0 开始就是这样)。 (dev.mysql.com/doc/refman/8.0/en/…)
    • @aderchox CONCAT_WS() 与 CONCAT() 不同;-) 如果任何参数为 NULL,则 CONCAT() 返回 NULL。 dev.mysql.com/doc/refman/8.0/en/…
    【解决方案2】:

    最好使用 concat_ws()。例如两个相邻的列:12,3 => 1,23。

    抱歉,这仍然存在一些问题。想想空值,空字符串,字符串可以包含','等...

    需要一个程序来生成哈希语句,它应该将 null 替换为特定值(对于可以为 null 的列),并且还使用很少使用的 char/byte 作为分隔符。

    【讨论】:

      【解决方案3】:

      好吧,我制作了一个小脚本,它可以完全按照您的要求执行,也可以按照其他人的要求执行...所以就这样...对于 PHP... 首先,您必须列出表的列,然后根据每列的类型为每列创建一个“case when”语句,并将其放入 concat_ws 语句中,然后使用 sha1 对其进行散列...我用过这种方法适用于非常大的表(600000+ 条记录),并且在选择所有记录时速度非常好。我还认为在 concat_ws 中连接所需数据并在 php 或您正在使用的任何东西中分解它会更快,但这只是一种预感......

      <?
      $query= mysql_query("SHOW COLUMNS FROM $table", $linklive);
              while ($col = mysql_fetch_assoc($query)) {
                  $columns[] = mysql_real_escape_string($col['Field']);
                  if ($col['Key'] == 'PRI') {
                      $key = mysql_real_escape_string($col['Field']);
                  }
                  $columnsinfo[$col['Field']] = $col;
              }
              $dates = array("date","datetime","time");
                          $int = array("int","decimal");
                          $implcols = array();
                          foreach($columns as $col){
                              if(in_array($columnsinfo[$col]['Type'], $dates)){
                                  $implcols[] = "(CASE WHEN (UNIX_TIMESTAMP(`$col`)=0 || `$col` IS NULL) THEN '[$col EMPTY]' ELSE `$col` END)";
                              }else{
                                  list($type, $rest) = explode("(",$columnsinfo[$col]['Type']);
                                  if(in_array($columnsinfo[$col]['Type'], $dates)){
                                      $implcols[] = "(CASE WHEN ( `$col`=0 || `$col` IS NULL ) THEN '[$col EMPTY]' ELSE `$col` END)";
                                  }else{
                                      $implcols[] = "(CASE WHEN ( `$col`='' || `$col` IS NULL ) THEN '[$col EMPTY]' ELSE `$col` END)";
                                  }
                              }
                          }
                          $keyslive = array();
                          //echo "SELECT $key SHA1(CONCAT_WS('',".implode(",", $columns).")) as compare FROM $table"; exit;
                          $q = "SELECT $key as `key`, SHA1(CONCAT_WS('',".implode(", ",$implcols).")) as compare FROM $table";
          ?>
      

      【讨论】:

        【解决方案4】:

        CONCAT 存在问题,例如CONCAT('ab', 'c')CONCAT('a', 'bc')。两个不同的行,但结果是一样的。您可以使用CONCAT_WS(';', 'ab', 'c') 来获得ab;c,但在CONCAT_WS(';', ';', '')CONCAT_WS(';', '', ';') 的情况下,您仍然会得到相同的结果。

        同样CONCAT(NULL, 'c') 返回NULL

        我认为最好的方法是使用QUOTE

        SELECT MD5(CONCAT(QUOTE(c1), QUOTE(c2), QUOTE(c3))) AS row_hash FROM t1;

        结果:select (concat(quote('a'), quote('bc'), quote('NULL'), quote(NULL), quote('\''), quote('')));

        是:'a''bc''NULL'NULL'\''''

        另外,不要使用 GROUP_CONCAT() 来获取表的哈希,它有限制:https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_group_concat_max_len

        相反,CHECKSUM TABLE 可能会更好,但您不能跳过带有 CHECKSUM TABLE https://dev.mysql.com/doc/refman/5.7/en/checksum-table.html 的列

        【讨论】:

          【解决方案5】:

          聚会迟到了,但是...

          您可以添加一个 updated 字段,该字段会在行更改时更新。
          然后你只需要跟踪时间戳,像这样显示

          updated: 2017-12-30 17:51:19 
          

          这样,您不仅可以知道如果行发生了更改,还可以知道何时是最后一次更新。
          MySQL 命令:

          ALTER TABLE mytable ADD `updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
          ON UPDATE CURRENT_TIMESTAMP;
          

          默认精度是一秒(这应该足够了,如果您只是不时检查一行是否发生了变化)。但如有必要,您可以设置几分之一秒的精度,例如微秒

          ALTER TABLE mytable ADD `updated` timestamp(6) NOT NULL
          DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6);
          

          时间戳会显示如下

          updated: 2017-12-30 17:51:19.123456 
          

          Link to the documentation,这是 8.0 的,但我已经在 5.7 上成功使用了。

          【讨论】:

            猜你喜欢
            • 2017-02-16
            • 2015-05-26
            • 1970-01-01
            • 1970-01-01
            • 2015-10-12
            • 2016-07-22
            • 2016-06-04
            • 2018-02-04
            • 2012-10-31
            相关资源
            最近更新 更多