是否可以自動預測DELETE CASCADE後面的操作?在我的軟件中,我想向用戶發出警告,提供有關將要刪除的數據的詳細信息。在MySQL中模擬DELETE CASCADE?
回答
可以使數據庫的副本,並把觸發器在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重新創建複製數據庫中級聯中已刪除的行。
我認爲您可以將Johan的觸發解決方案與您回滾的交易結合使用。這樣可以避免需要第二個數據庫以及手動還原已刪除的條目。
- 添加觸發器和日誌表
- 每個試圖刪除啓動事務,並刪除條目
- 呈現來自日誌的信息,您的用戶批准
- 如果用戶同意提交交易,否則回滾
唯一的問題是,如果你記錄的引擎進入也支持事務處理,日誌記錄也會被回滾,所以你需要使用非事務引擎進行日誌記錄,比如MyISAM。 – Johan 2011-09-26 17:38:43
交易想法很好。事實上,我不會使用觸發器,也不會使用日誌。我只是模擬刪除和事後回滾。 – user694971 2011-09-26 18:18:19
@ user694971:我認爲如果你想向用戶顯示刪除的條目,你需要日誌。沒有它,你只能顯示剩餘的條目,除非你的應用程序邏輯可以解決這個問題。 – Stefan 2011-09-28 15:25:50
我寫了一個非常快速的黑客,完全符合你在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)));
和它打印一個包含所有行和所有將被刪除的值的完整表。
但正如我所說,這是一個非常,非常快速和骯髒的黑客攻擊。我會很高興爲任何錯誤報告(並且肯定有很多它們!)。
- 1. DELETE CASCADE中的MySQL
- 2. ON DELETE CASCADE在MySQL
- 3. MySQL ON DELETE CASCADE
- 4. MySQL的ON DELETE CASCADE不行
- 5. ON DELETE CASCADE SQLite中
- 6. 在SQLITE ANDROID DELETE CASCADE
- 7. ON DELETE CASCADE
- 8. cascade =「all-delete-orphan」
- 9. SQL「DELETE CASCADE」
- 10. mysql PDO ON DELETE CASCADE - 不工作
- 11. MySQL的語法錯誤#1064 ON DELETE CASCADE
- 12. MySQL ON UPDATE/ON DELETE CASCADE NOT CASCADEING
- 13. MySQL FOREIGN KEY錯誤,ON DELETE CASCADE
- 14. Django沒有設置MySQL ON DELETE = CASCADE
- 15. MySQL外部約束ON DELETE CASCADE
- 16. 刪除ON DELETE CASCADE
- 17. 逆轉的DELETE CASCADE
- 18. ON DELETE CASCADE支持?
- 19. ON DELETE CASCADE在c中不工作#
- 20. 在Oracle XE 10g中MERGE和ON DELETE CASCADE
- 21. 在pgAdmin中定義ON DELETE CASCADE 3
- 22. 如何在NHibernate中實現cascade ='delete'?
- 23. Hibernate映射:delete-orphan cascade
- 24. 實體框架ON DELETE CASCADE
- 25. SQL ALTER TABLE ON DELETE CASCADE
- 26. MongoDB的DBREF ON DELETE CASCADE
- 27. PostgreSQL的:FOREIGN KEY/ON DELETE CASCADE
- 28. 臨時啓用DELETE CASCADE
- 29. ON DELETE CASCADE不起作用
- 30. SQLite:ON DELETE CASCADE性能不佳
好問題,我也對答案感興趣。 – Alp 2011-05-17 10:12:35
我現在能想到的唯一合理的方法是'SHOW FULL COLUMNS FROM'然後遍歷相應的表...(硬編碼fkey_names => table_names ...) –
user694971
2011-05-20 14:53:53