【问题标题】:Checking if a user changed any of their data in multiple tables检查用户是否更改了多个表中的任何数据
【发布时间】:2017-09-28 21:57:14
【问题描述】:

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

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

是否有一种简单的方法可以判断自项目上次完成后是否有任何其他表更新了它们的数据?我不需要知道哪些表改变了数据,只是有表改变了数据。

例如,如果用户更改了其中一个表中的数据,我希望能够显示一条消息,表明他们的项目有未最终确定的数据。

我考虑过几种方法:

  1. 检查每个表以查看是否有任何时间戳比检查点表中的最新时间戳更新。
  2. 向主项目表添加一个额外的时间戳列(我已经有一个创建和更新的时间戳列)。大多数其他表直接或间接链接到此主项目表。将触发器添加到每个其他表以在其数据更改时更新此时间戳。我还不太确定如何为此正确设置适当的触发器。
  3. 创建仅包含 project_id 和时间戳列的新表。向其他表添加触发器,如选项 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)

【问题讨论】:

  • 您可以在采取任何其他操作之前添加一个检查以查看项目是否已完成,如果是,只需将一列设置为“未完成的数据”或其他内容,这样您就不会必须检查更多表,并且您只需在更新其他表之一中的数据时调用 1 个函数
  • 这个^。只需在项目表中添加一个 is_finalized TinyInt 列。项目完成后,将其更新为 1。当有任何更改时,将其更新为 0。
  • 为什么是 tinyInt?只需将其设为布尔值即可。
  • @TonyChiboucas MySQL 中没有真正的布尔数据类型。
  • @TonyChiboucas 是的,但是 bit(1) 与 MySQL 中的布尔值不同。关键字 boolean 实际上是 tinyint(1) 的别名。在旧版本的 MySQL 中,bit 也是 tinyint 的别名。

标签: php mysql database


【解决方案1】:

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

中间件解决方案:

为每个相关表添加一个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 的库来解析查询。

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

【讨论】:

    【解决方案2】:

    如果我遇到你的情况,我会制作一个表,其中包含字段项目 ID (FK) 和 is_finalized 的布尔值。所以每次完成一个项目,我都会在里面添加一个条目。

    +-----------------+--------------+
    | project_id      | is_finalized |
    |-----------------|--------------|
    | 12              | 1            |
    +-----------------+--------------+
    

    在任何更新/插入之前,只需检查我的项目是否存在此密钥。如果存在,将其更改为 0,在加载文件时,只需检查值是否为 0。如果为 0,则显示消息:project has unfinalized data.

    仅当键存在且值为 0 时才应显示消息。如果项目未完成。该表将没有值,因此没有消息。

    非常简单,处理速度更快(而不是检查每个时间戳)和可扩展的方法,因为它仅依赖于更新或插入查询,您可以在将来的模块中使用它们。

    【讨论】:

      【解决方案3】:

      进行多重检查时,时间戳比较可能会很麻烦。

      ...我不需要知道哪些表更改了数据,只是有表更改了数据...

      加入查询以生成 (1) 数据集,JSON/SERIALIZE 它,然后 MD5,将这个匆忙的字符串保存到数据库中。下次比较一下,如果有任何不同,数据集已经改变。这是大数据/文件比较/代码仓库的一般思路。

      但鉴于...

      ..项目的更多表..

      然后只需对表中的每个数据行使用 MD5。更改后的哈希字符串将有所不同。

      【讨论】:

        【解决方案4】:

        A 计划:现成的解决方案:

        1. 设置主从。从站将包含数据的“旧”副本。
        2. 建立“延迟”复制。假设是 1 小时。
        3. 获取pt-table-checksum;每小时运行两次。

        这将在一小时内发现变化。 (如果数据量很大或很小,时间可能需要调整。)

        B计划

        拒绝真实人类的所有直接访问。相反,构建一个通过一些 API 处理所有正常访问的应用程序。然后我会检测 API 以收集我选择的任何内容。

        即席查询(没有 API):

        • 也许不允许他们
        • 也许有一个审查委员会(我)承认他们的竞选。
        • 也许有一个 API 可以运行查询,但会立即记录/电子邮件/铃声/其他任何内容。

        【讨论】:

          【解决方案5】:

          真的不知道为什么这些答案暗示依赖 ID 或复杂的数据记录,这是一个相当常见的问题,有一些非常简单的解决方案。

          使用那些父/子关系

          Note: when documenting a schema, it is important to note more than just FK relationships, but also the type of replationship (one-to-one, many-to-one, one-to-many, many-to-many).

          您已经有了相当明确的父/子关系,我假设是:

          ma​​in_project one&lt;--many parent one&lt;--many child

          使用它们的两种方式之一:

          1. 更新parent 和main_project 的日期,该日期存储任何child 的最新修改日期。
          2. 在使用main_project、parent 和child 的查询中使用join/max/modified 的组合。

          child_updated 日期

          main_project.child_updated
          parent.child_updated
          

          每当更新任何 child 时,还要更新main_project 和parent 的child_modified 日期。与parent 类似,更新main_project。这可以通过触发器、php 或对main_project 对象 的连接或视图的巧妙使用来完成。我强烈建议坚持使用这些表的 PHP 模型来执行此操作。

          加入/最大/修改

          只需构建一个查询来获取四个值,然后检查它们:

          • checkpoint_table.main_project_finalized
          • main_project.modified
          • MAX(parent.modified)
          • MAX(child.modified)

          这些连接可能会有些棘手,因此您必须尝试一下。

          SELECT m.modified as modified, MAX(c.project_finalized_timestamp) as finalized, MAX(p.modified) AS parent_modified, MAX(c.modified) as child_modified
          FROM main_project_table m 
          LEFT JOIN checkpoint_table c
              ON m.id = c.project_id
          LEFT JOIN parent_table p 
              ON m.id = p.project_id
          LEFT JOIN child_table c
              ON p.id = c.parent_id
          GROUP BY m.id
          

          这将为您提供您关心的所有日期的一行,允许您在 PHP 中为其创建一些简单的逻辑。

          $result = // retrieve joined data as above
          if ($result['finalized'] < max($result['modified'], $result['parent_modified'], $result['child_modified']) {
              // changed
          }
          

          【讨论】:

            【解决方案6】:

            到目前为止,已经提到了一些很好的解决方案。另一种是利用 MySQL 的信息模式。这样做,例如,您可以选择所有具有您知道的名称的时间戳字段的表,并检查它们的修改时间。这可能是最动态和无缝的方法,但并不是最好的方法。如果我在遗留代码或第三方代码之上构建接口并且无法控制应用程序的那部分,我通常只会做这样的事情。

            在架构上,我认为最好的方法是让您的应用程序了解相关的表/字段并对其进行审核。我假设数据与所讨论的对象相关,因此尽管它们是外部表,但仍然可以轻松检查它们是否有修改。

            另一个好主意是将版本控制添加到所有有问题的表中,以便在您的应用程序的此步骤中显示更改的内容。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2016-07-25
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-10-14
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多