2011-05-17 75 views
13

是否可以自动预测DELETE CASCADE后面的操作?在我的软件中,我想向用户发出警告,提供有关将要删除的数据的详细信息。在MySQL中模拟DELETE CASCADE?

+2

好问题,我也对答案感兴趣。 – Alp 2011-05-17 10:12:35

+0

我现在能想到的唯一合理的方法是'SHOW FULL COLUMNS FROM '然后遍历相应的表...(硬编码fkey_names => table_names ...) – user694971 2011-05-20 14:53:53

回答

3

可以使数据库的副本,并把触发器在after delete

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') 

如果你之前做了SELECT @last_id:= max(id) FROM log副本上的删除级联。
然后,您可以执行SELECT * FROM log WHERE id > @last_id
并获取将在级联中删除的所有行。

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

1

我认为您可以将Johan的触发解决方案与您回滚的交易结合使用。这样可以避免需要第二个数据库以及手动还原已删除的条目。

  • 添加触发器和日志表
  • 每个试图删除启动事务,并删除条目
  • 呈现来自日志的信息,您的用户批准
  • 如果用户同意提交交易,否则回滚
+0

唯一的问题是,如果你记录的引擎进入也支持事务处理,日志记录也会被回滚,所以你需要使用非事务引擎进行日志记录,比如MyISAM。 – Johan 2011-09-26 17:38:43

+0

交易想法很好。事实上,我不会使用触发器,也不会使用日志。我只是模拟删除和事后回滚。 – user694971 2011-09-26 18:18:19

+1

@ user694971:我认为如果你想向用户显示删除的条目,你需要日志。没有它,你只能显示剩余的条目,除非你的应用程序逻辑可以解决这个问题。 – Stefan 2011-09-28 15:25:50

0

我写了一个非常快速的黑客,完全符合你在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!"); 
    } 
} 

想象我有一个数据库中的“DB”被称为“表”的表,我想删除一个id为180的话,我会打电话:

print(get_foreign_key_deleted_data_html('db', 'table', array('id' => 180))); 

和它打印一个包含所有行和所有将被删除的值的完整表。

但正如我所说,这是一个非常,非常快速和肮脏的黑客攻击。我会很高兴为任何错误报告(并且肯定有很多它们!)。