Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/70.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在MySQL中模拟删除级联?_Mysql_Cascading Deletes - Fatal编程技术网

在MySQL中模拟删除级联?

在MySQL中模拟删除级联?,mysql,cascading-deletes,Mysql,Cascading Deletes,是否可以自动预测删除级联之后的操作?在我的软件中,我想给用户一个警告,其中包含将被删除的数据的详细信息。您可以复制数据库,并在删除后在上放置触发器 DELIMITER $$ CREATE TRIGGER ad_table1_each AFTER DELETE ON table1 FOR EACH ROW BEGIN INSERT INTO log VALUES (null /*autoinc id*/ , 'table1'

是否可以自动预测删除级联之后的操作?在我的软件中,我想给用户一个警告,其中包含将被删除的数据的详细信息。

您可以复制数据库,并在删除后在
上放置触发器

DELIMITER $$

CREATE TRIGGER ad_table1_each AFTER DELETE ON table1 FOR EACH ROW
BEGIN
  INSERT INTO log VALUES (null                 /*autoinc id*/
        , 'table1'                             /*tablename*/
        , old.id                               /*tableid*/
        , concat_ws(',',old.field1,old.field2  /*CSV's of fields*/
        , NOW()                                /*timestamp*/
        , 'delete');                           /*what action*/


  REPLACE INTO restore_table1 VALUES (old.id,
        , old.field1
        , old.field2
        , ... );

END $$

DELIMITER ;
日志表只是包含以下字段的表:

id            integer autoincrement primary key
tablename     varchar(45)
table_id      integer
fields        varchar(6000)
delete_time   timestamp
action        enum('insert','update','delete')
如果在副本的删除级联之前执行
操作,请从日志中选择@last_id:=max(id)。
然后您可以从日志中选择*,其中id>@last\u id

并获取将在级联中删除的所有行


之后,您可以使用restore_table1在复制数据库中重新创建级联中删除的行。

您可以复制数据库,并在删除后在
上放置触发器

DELIMITER $$

CREATE TRIGGER ad_table1_each AFTER DELETE ON table1 FOR EACH ROW
BEGIN
  INSERT INTO log VALUES (null                 /*autoinc id*/
        , 'table1'                             /*tablename*/
        , old.id                               /*tableid*/
        , concat_ws(',',old.field1,old.field2  /*CSV's of fields*/
        , NOW()                                /*timestamp*/
        , 'delete');                           /*what action*/


  REPLACE INTO restore_table1 VALUES (old.id,
        , old.field1
        , old.field2
        , ... );

END $$

DELIMITER ;
日志表只是包含以下字段的表:

id            integer autoincrement primary key
tablename     varchar(45)
table_id      integer
fields        varchar(6000)
delete_time   timestamp
action        enum('insert','update','delete')
如果在副本的删除级联之前执行
操作,请从日志中选择@last_id:=max(id)。
然后您可以从日志中选择*,其中id>@last\u id

并获取将在级联中删除的所有行


之后,您可以使用restore_table1在复制数据库中重新创建级联中删除的行。

我认为您可以将Johan的触发器解决方案与回滚的事务结合使用。这避免了需要第二个数据库和手动恢复已删除的条目

  • 添加触发器和日志表
  • 对于每次尝试的删除,启动一个事务并删除条目
  • 将日志中的信息提交给用户以供批准
  • 如果用户同意提交事务,否则回滚

    • 我认为您可以将Johan的触发器解决方案与回滚的事务结合使用。这避免了需要第二个数据库和手动恢复已删除的条目

      • 添加触发器和日志表
      • 对于每次尝试的删除,启动一个事务并删除条目
      • 将日志中的信息提交给用户以供批准
      • 如果用户同意提交事务,否则回滚

      我写了一个非常快速的hack,它完全满足您在PHP中的需要,因为我想做完全相同的事情,但还没有在网上找到任何相关资源

      对你来说可能太晚了,但它可能会帮助别人

      function get_referencing_foreign_keys ($database, $table) {
          $query = 'SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, REFERENCED_COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE REFERENCED_TABLE_SCHEMA = "'.$database.'" AND REFERENCED_TABLE_NAME = '.esc($table);
          $result = rquery($query);
          $foreign_keys = array();
          while ($row = mysql_fetch_row($result)) {
              $foreign_keys[] = array('database' => $row[0], 'table' => $row[1], 'column' => $row[2], 'reference_column' => $row[3]);
          }
      
          return $foreign_keys;
      }
      
      function get_foreign_key_deleted_data_html ($database, $table, $where) {
          $data = get_foreign_key_deleted_data ($database, $table, $where);
      
          $html = '';
          foreach ($data as $key => $this_data) {
              $html .= "<h2>$key</h2>\n";
      
              $html .= "<table>\n";
              $i = 0;
              foreach ($this_data as $value) {
                  if($i == 0) {
                      $html .= "\t<tr>\n";
                      foreach ($value as $column => $column_value) {
                          $html .= "\t\t<th>".htmlentities($column)."</th>\n";
                      }
                      $html .= "\t</tr>\n";
                  }
                  $html .= "\t<tr>\n";
                  foreach ($value as $column => $column_value) {
                      $html .= "\t\t<td>".htmlentities($column_value)."</td>\n";
                  }
                  $html .= "\t</tr>\n";
                  $i++;
              }
              $html .= "</table>\n";
          }
      
          return $html;
      }
      
      function get_foreign_key_deleted_data ($database, $table, $where) {
          $GLOBALS['get_data_that_would_be_deleted'] = array();
          $data = get_data_that_would_be_deleted($database, $table, $where);
          $GLOBALS['get_data_that_would_be_deleted'] = array();
          return $data;
      }
      
      function get_data_that_would_be_deleted ($database, $table, $where, $recursion = 100) {
          if($recursion <= 0) {
              die("Deep recursion!");
          }
      
          if($recursion == 100) {
              $GLOBALS['get_data_that_would_be_deleted'] = array();
          }
      
          if($table) {
              if(is_array($where)) {
                  $foreign_keys = get_referencing_foreign_keys($database, $table);
                  $data = array();
      
                  $query = 'SELECT * FROM `'.$table.'`';
                  if(count($where)) {
                      $query .= ' WHERE 1';
                      foreach ($where as $name => $value) {
                          $query .= " AND `$name` = ".esc($value);
                      }
                  }
                  $result = rquery($query);
      
                  $to_check = array();
      
                  while ($row = mysql_fetch_row($result)) {
                      $new_row = array();
                      $i = 0;
                      foreach ($row as $this_row) {
                          $field_info = mysql_fetch_field($result, $i);
                          $new_row[$field_info->name] = $this_row;
                          foreach ($foreign_keys as $this_foreign_key) {
                              if($this_foreign_key['reference_column'] == $field_info->name) {
                                  $to_check[] = array('value' => $this_row, 'foreign_key' => array('table' => $this_foreign_key['table'], 'column' => $this_foreign_key['column'], 'database' => $this_foreign_key['database']));
                              }
                          }
                          $i++;
                      }
                      $GLOBALS['get_data_that_would_be_deleted'][$table][] = $new_row;
                  }
                  foreach ($to_check as $this_to_check) {
                      if(isset($this_to_check['value']) && !is_null($this_to_check['value'])) {
                          get_data_that_would_be_deleted($database, $this_to_check['foreign_key']['table'], array($this_to_check['foreign_key']['column'] => $this_to_check['value']), $recursion - 1);;
                      }
                  }
      
                  $data = $GLOBALS['get_data_that_would_be_deleted'];
      
                  return $data;
              } else {
                  die("\$where needs to be an array with column_name => value pairs");
              }
          } else {
              die("\$table was not defined!");
          }
      }
      
      它将打印一个完整的表,其中包含所有要删除的行和值


      但正如我所说,这是一个非常、非常快速和肮脏的黑客行为。我很高兴有任何bug报告(当然有很多!)。

      我写了一个非常快速的hack,它完全符合PHP的需要,因为我想做完全相同的事情,但还没有在网上找到任何相关资源

      对你来说可能太晚了,但它可能会帮助别人

      function get_referencing_foreign_keys ($database, $table) {
          $query = 'SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, REFERENCED_COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE REFERENCED_TABLE_SCHEMA = "'.$database.'" AND REFERENCED_TABLE_NAME = '.esc($table);
          $result = rquery($query);
          $foreign_keys = array();
          while ($row = mysql_fetch_row($result)) {
              $foreign_keys[] = array('database' => $row[0], 'table' => $row[1], 'column' => $row[2], 'reference_column' => $row[3]);
          }
      
          return $foreign_keys;
      }
      
      function get_foreign_key_deleted_data_html ($database, $table, $where) {
          $data = get_foreign_key_deleted_data ($database, $table, $where);
      
          $html = '';
          foreach ($data as $key => $this_data) {
              $html .= "<h2>$key</h2>\n";
      
              $html .= "<table>\n";
              $i = 0;
              foreach ($this_data as $value) {
                  if($i == 0) {
                      $html .= "\t<tr>\n";
                      foreach ($value as $column => $column_value) {
                          $html .= "\t\t<th>".htmlentities($column)."</th>\n";
                      }
                      $html .= "\t</tr>\n";
                  }
                  $html .= "\t<tr>\n";
                  foreach ($value as $column => $column_value) {
                      $html .= "\t\t<td>".htmlentities($column_value)."</td>\n";
                  }
                  $html .= "\t</tr>\n";
                  $i++;
              }
              $html .= "</table>\n";
          }
      
          return $html;
      }
      
      function get_foreign_key_deleted_data ($database, $table, $where) {
          $GLOBALS['get_data_that_would_be_deleted'] = array();
          $data = get_data_that_would_be_deleted($database, $table, $where);
          $GLOBALS['get_data_that_would_be_deleted'] = array();
          return $data;
      }
      
      function get_data_that_would_be_deleted ($database, $table, $where, $recursion = 100) {
          if($recursion <= 0) {
              die("Deep recursion!");
          }
      
          if($recursion == 100) {
              $GLOBALS['get_data_that_would_be_deleted'] = array();
          }
      
          if($table) {
              if(is_array($where)) {
                  $foreign_keys = get_referencing_foreign_keys($database, $table);
                  $data = array();
      
                  $query = 'SELECT * FROM `'.$table.'`';
                  if(count($where)) {
                      $query .= ' WHERE 1';
                      foreach ($where as $name => $value) {
                          $query .= " AND `$name` = ".esc($value);
                      }
                  }
                  $result = rquery($query);
      
                  $to_check = array();
      
                  while ($row = mysql_fetch_row($result)) {
                      $new_row = array();
                      $i = 0;
                      foreach ($row as $this_row) {
                          $field_info = mysql_fetch_field($result, $i);
                          $new_row[$field_info->name] = $this_row;
                          foreach ($foreign_keys as $this_foreign_key) {
                              if($this_foreign_key['reference_column'] == $field_info->name) {
                                  $to_check[] = array('value' => $this_row, 'foreign_key' => array('table' => $this_foreign_key['table'], 'column' => $this_foreign_key['column'], 'database' => $this_foreign_key['database']));
                              }
                          }
                          $i++;
                      }
                      $GLOBALS['get_data_that_would_be_deleted'][$table][] = $new_row;
                  }
                  foreach ($to_check as $this_to_check) {
                      if(isset($this_to_check['value']) && !is_null($this_to_check['value'])) {
                          get_data_that_would_be_deleted($database, $this_to_check['foreign_key']['table'], array($this_to_check['foreign_key']['column'] => $this_to_check['value']), $recursion - 1);;
                      }
                  }
      
                  $data = $GLOBALS['get_data_that_would_be_deleted'];
      
                  return $data;
              } else {
                  die("\$where needs to be an array with column_name => value pairs");
              }
          } else {
              die("\$table was not defined!");
          }
      }
      
      它将打印一个完整的表,其中包含所有要删除的行和值


      但正如我所说,这是一个非常、非常快速和肮脏的黑客行为。我很高兴看到任何错误报告(当然有很多!)。

      好问题,我也对答案感兴趣。目前我能想到的唯一合理方法是
      显示
      中的完整列,然后遍历到相应的表中。。。(硬代码fkey_名称=>表名称…)好问题,我也对答案感兴趣。目前我能想到的唯一合理方法是
      显示
      中的完整列,然后遍历到相应的表中。。。(硬代码fkey_names=>table_names…)唯一的问题是,如果您登录的引擎也支持事务,那么日志记录也将回滚,因此您需要使用非事务性引擎进行日志记录,如MyISAM。事务想法非常好。事实上,我既不使用触发器也不使用日志。我只是模拟删除,然后回滚。@user694971:如果您想向用户显示删除的条目,我想您需要日志。如果没有它,您只能显示剩余的条目,除非您的应用程序逻辑能够解决这个问题。唯一的问题是,如果您登录的引擎也支持事务,那么日志记录也将回滚,因此您需要使用非事务性引擎来记录,如MyISAM。事务想法非常好。事实上,我既不使用触发器也不使用日志。我只是模拟删除,然后回滚。@user694971:如果您想向用户显示删除的条目,我想您需要日志。如果没有它,您只能显示其余的条目,除非您的应用程序逻辑能够解决这个问题。