在我的数据库中,我有几个表。一个是检查点表,该表记录了用户选择完成其项目之一的情况。该表包含一个自动创建的时间戳。每当用户完成项目时,都会在检查点表中添加新行(这样,我们还可以保留项目完成前的历史记录)。

我还有其他几个带有时间戳的表(或一些我也可以添加时间戳列的表),这些表在它们的表更改时会自动更新。

有没有一种简单的方法可以判断自该项目上一次完成以来是否有其他表更新了其数据?我不需要知道哪些表已更改数据,而只是知道有些表已更改数据。

例如,如果用户更改其表之一中的数据,我希望能够显示一条消息,指示其项目中包含未完成的数据。

我考虑过以下几种方法:

  • 检查每个表,以查看是否有任何时间戳记比检查点表中的最新时间戳记新。
  • 向主项目表中添加一个附加的timestamp列(我已经创建并更新了timestamp列)。其他大多数表都直接或间接链接到该主项目表。将触发器添加到其他所有表以在其数据更改时更新此时间戳。我还不确定如何为此正确设置适当的触发器。
  • 仅使用project_id和timestamp列创建一个新表。如选项2所示,向其他表添加触发器。

  • 随着添加新模块,我将向项目添加更多表,因此也需要易于扩展的内容。

    这些方法中的每一个似乎都涉及很多步骤。

    这些方法中的一种会比另一种更有效或可行吗?我还没有考虑其他方法吗?如果触发器是执行此操作的最佳方法,我将如何设置触发器?

    我的表的简化概述如下所示:
    main_project_table
        id
        user_id (FK to user_table)
        created_timestamp
        updated_timestamp
    
    checkpoint_group_table (users can choose which group to finalize their project too)
        id
        user_id (FK to user_table)
        group_name
    
    checkpoint_table (the table that records the finalized data and time of finalization)
        id
        checkpoint_group_id (FK to checkpoint_group_table)
        project_id (FK to main_project_table)
        project_finalized_timestamp
    
     parent_table (several of these)
         id
         project_id (FK to main_project_table)
    
     child_table (0 or more of these for each parent_table)
         id
         parent_id (FK to parent_table)
    

    最佳答案

    您实际上只有三种解决方案:中间件,触发器和常规日志文件。

    中间件解决方案:

    在每个相关表中添加一个timestamp字段,并将默认值设置为“CURRENT_TIMESTAMP”。每次更新时,这会将timestamp字段更新为当前时间。
    假设用户正在使用某些API,则可以编写一个JOIN查询,该查询将返回最新的时间戳。看起来像这样。

    SELECT
        CASE
            WHEN b.timestamp IS NOT NULL THEN 0
            WHEN c.timestamp IS NOT NULL THEN 0
            WHEN d.timestamp IS NOT NULL THEN 0
            WHEN e.timestamp IS NOT NULL THEN 0
            ELSE 1
         AS `test`
    FROM checkpoint_table a
    LEFT JOIN main_project_table b
        ON a.project_id = b.id
        AND b.timestamp > a.project_finalized_timestamp
    LEFT JOIN checkpoint_group_table c
        ON b.user_id = c.user_id
        AND c.timestamp > a.project_finalized_timestamp
    LEFT JOIN parent_table d
        ON b.id = d.project_id
        AND d.timestamp > a.project_finalized_timestamp
    LEFT JOIN child_table e ON d.id = e.parent_id
        ON b.id = d.project_id
        AND e.timestamp > a.project_finalized_timestamp
    

    现在,当请求路由到表时,您可以运行此查询,并且如果test == 0,则返回消息。
    <?php
          class middleware{
              public function getMessage(){
                  // run query
    
                  if($data[0]['test'] == 1){
                       return "project has unfinalized data";
                  }else{
                       return null;
                  }
              }
          }
    

    触发解决方案:
    CREATE TRIGGER checkpoint_group_table
    AFTER UPDATE on _table_
    FOR EACH ROW UPDATE _table_
    SET main_project_table.updated_timestamp = CURTIME()
    WHERE main_project_table.user_id=checkpoint_group_table.id
    

    这样做的好处是,它可能比中间件解决方案更优雅。缺点是触发器不是一目了然的,并且根据我的经验,当进程处于后台时,它们最终会被遗忘。从长远来看,您可能会留下这个积木难题,这会让您感到困难。

    常规日志文件解决方案:

    Mysql可以在服务器上记录每个查询。可以在这段时间内访问此日志文件,对其进行解析,并确定是否有任何表被更新。这样,您可以确定在项目完成后是否进行了任何更新。

    打开常规日志文件。
    SET GLOBAL general_log = 'ON';
    

    设置日志文件的路径。
    SET GLOBAL general_log_file = 'var/log/mysql/mysql_general.log'
    

    通过转到命令终端进行确认。
    mysql -se "SHOW VARIABLES" | grep  -e general_log
    

    您可能需要重置MySQL。
    sudo service MySQL restart
    

    您可以开始执行此脚本了吗?

    $v = shell_exec("sudo less /var/log/mysql/mysql_general.log");
    
    $lines = explode("\n",$v);
    
    $new = array();
    foreach($lines as $i => $line){
        if(substr($line,0,1) != " "){
            if(isset($l)){
                array_push($new,$l);
            }
            $l = $line;
        }else{
            $l.= preg_replace('/\s+/', ' ', $line);
        }
    }
    
    $lines = $new;
    
    $index = array();
    foreach($lines as $i => $line){
        $e = explode("\t",$line);
        $new = array();
        foreach($e as $key => $value){
            $new[$key] = trim($value);
        }
    
        $index[$i] = $new;
    
    }
    

    这将导致...
    array(3) {
      [0]=> string(27) "2017-10-01T08:17:04.659274Z"
      [1]=> string(8) "70 Query"
      [2]=> string(129) "UPDATE checkpoint_group_table SET group_name = 'Dev Group' Where id=6"
    

    }

    在这里,您可以使用一个名为PHP-SQL-Parser的库来解析查询。

    这种方法的优点是可以很好地扩展,因为您不必在数据库中添加任何列。缺点是这将涉及更多的代码,并且意味着更多的复杂性。如果没有为它编写单元测试,则可能无法真正实现此解决方案。

    10-04 11:27
    查看更多