HOWTO · MySQL

如何删除 MySQL 中的所有表

安全删除 MySQL 架构中的所有基表,处理外键并验证结果。

本页内容

若要删除所有表但保留 MySQL 数据库本身,请为该架构中的 BASE TABLE 对象生成一条经过检查的 DROP TABLE 语句,在同一会话中暂时禁用 FOREIGN_KEY_CHECKS 后执行,再验证结果。表定义和数据会被永久删除,因此执行前请备份,并确认所选架构可以安全丢弃。

为此任务选择 DROP TABLE

DROP TABLE 会删除表;它不同于只删除行而保留表定义的 DELETE,也不同于清空表的 TRUNCATEDROP DATABASE 是另一项范围更广的操作,只有同时打算删除数据库容器及架构级对象时才应使用。删除表还会删除触发器并导致隐式提交,所以不能在事务中试验后期待回滚恢复。

你需要拥有待删除表的 DROP 权限。请在明确指定的架构中操作,使用备份或已知可丢弃的测试数据库,并让准备、删除和恢复语句始终使用同一个 MySQL 连接。

生成带引号的基本表列表

INFORMATION_SCHEMA.TABLES 可以区分 BASE TABLE 与视图。使用 table_type = 'BASE TABLE' 过滤,因此不会把应保留的视图当作表删除。查询还会用反引号包围标识符,并在构造语句前将表名中的反引号加倍。

根据架构中名称的数量和长度,将 group_concat_max_len 设得足够大。对于空架构,IF 表达式会返回无害消息,而不是尝试执行没有名称的 DROP TABLE。在接近生产环境执行前,请检查 @drop_sql 中生成的值。

在一个会话中删除由外键关联的基本表

外键顺序可能使单独删除失败。保存当前会话值,只在这个受控会话中关闭检查,执行生成的语句后恢复保存的值。下面是本文使用的精确 MySQL 8.4.11 fixture:它创建两个由外键关联的表和一个视图,只删除表,并输出前后的计数。

#!/bin/sh
# Verify that a generated DROP TABLE statement removes FK-linked base tables.
# A view is retained deliberately so the fixture also proves the table/view boundary.
set -eu

fixture_tmp=$(mktemp -d)
trap 'kill "$server_pid" 2>/dev/null || true; rm -rf "$fixture_tmp"' EXIT INT TERM
mysqld --no-defaults --initialize-insecure --datadir="$fixture_tmp/data" --user=root >/dev/null 2>&1
mysqld --no-defaults --datadir="$fixture_tmp/data" --socket="$fixture_tmp/mysql.sock" \
  --pid-file="$fixture_tmp/mysql.pid" --skip-networking --user=root >/dev/null 2>&1 &
server_pid=$!
for _ in $(seq 1 30); do
  mysqladmin --no-defaults --socket="$fixture_tmp/mysql.sock" ping >/dev/null 2>&1 && break
  sleep 1
done
mysqladmin --no-defaults --socket="$fixture_tmp/mysql.sock" ping >/dev/null 2>&1

mysql --no-defaults --batch --raw --skip-column-names --socket="$fixture_tmp/mysql.sock" <<'SQL'
CREATE DATABASE drop_all_demo;
USE drop_all_demo;
CREATE TABLE parent (id INT PRIMARY KEY);
CREATE TABLE child (
  id INT PRIMARY KEY,
  parent_id INT,
  CONSTRAINT child_parent_fk FOREIGN KEY (parent_id) REFERENCES parent(id)
);
CREATE VIEW parent_ids AS SELECT id FROM parent;

SELECT COUNT(*)
FROM information_schema.tables
WHERE table_schema = DATABASE() AND table_type = 'BASE TABLE';

SET SESSION group_concat_max_len = 1048576;
SELECT GROUP_CONCAT(
         CONCAT('`', REPLACE(table_name, '`', '``'), '`')
         ORDER BY table_name SEPARATOR ', '
       )
INTO @table_list
FROM information_schema.tables
WHERE table_schema = DATABASE() AND table_type = 'BASE TABLE';
SET @drop_sql = IF(
  @table_list IS NULL,
  'SELECT ''No base tables found''',
  CONCAT('DROP TABLE IF EXISTS ', @table_list)
);

SET @saved_fk_checks = @@SESSION.foreign_key_checks;
SET SESSION foreign_key_checks = 0;
PREPARE drop_statement FROM @drop_sql;
EXECUTE drop_statement;
DEALLOCATE PREPARE drop_statement;
SET SESSION foreign_key_checks = @saved_fk_checks;

SELECT COUNT(*)
FROM information_schema.tables
WHERE table_schema = DATABASE() AND table_type = 'BASE TABLE';
SELECT COUNT(*)
FROM information_schema.tables
WHERE table_schema = DATABASE() AND table_type = 'VIEW';
SELECT @@SESSION.foreign_key_checks;
SQL
2
0
1
1

输出表示起初有两个基本表,删除后为零,仍保留一个视图,并且 FOREIGN_KEY_CHECKS 已恢复到原先启用的值。该策略不会删除视图、存储过程、事件、用户或数据库本身。

删除后验证架构

执行 fixture 使用的同一 INFORMATION_SCHEMA.TABLES 计数,并预期得到零行 BASE TABLE。当需要确认剩余对象是视图而非表时,SHOW FULL TABLES FROM your_database 也很有用。如果脚本修改了 FOREIGN_KEY_CHECKS,请在关闭连接前检查其会话值;不要假设其他会话继承了该改动。

了解外键失败的边界

若不临时禁用检查,MySQL 会拒绝删除被引用的父表。此边界 fixture 会以错误 3730 退出,说明无序的 DROP TABLE 命令列表为何可能失败。

#!/bin/sh
# Demonstrate the foreign-key failure when a referenced table is dropped normally.
set -eu

fixture_tmp=$(mktemp -d)
trap 'kill "$server_pid" 2>/dev/null || true; rm -rf "$fixture_tmp"' EXIT INT TERM
mysqld --no-defaults --initialize-insecure --datadir="$fixture_tmp/data" --user=root >/dev/null 2>&1
mysqld --no-defaults --datadir="$fixture_tmp/data" --socket="$fixture_tmp/mysql.sock" \
  --pid-file="$fixture_tmp/mysql.pid" --skip-networking --user=root >/dev/null 2>&1 &
server_pid=$!
for _ in $(seq 1 30); do
  mysqladmin --no-defaults --socket="$fixture_tmp/mysql.sock" ping >/dev/null 2>&1 && break
  sleep 1
done
mysqladmin --no-defaults --socket="$fixture_tmp/mysql.sock" ping >/dev/null 2>&1

mysql --no-defaults --batch --raw --skip-column-names --socket="$fixture_tmp/mysql.sock" <<'SQL'
CREATE DATABASE fk_boundary_demo;
USE fk_boundary_demo;
CREATE TABLE parent (id INT PRIMARY KEY);
CREATE TABLE child (id INT PRIMARY KEY, parent_id INT, CONSTRAINT child_parent_fk FOREIGN KEY (parent_id) REFERENCES parent(id));
DROP TABLE parent;
SQL

捕获到的诊断是 ERROR 3730 (HY000):MySQL 报告 parentchild 中的 child_parent_fk 引用。不要借助禁用检查来绕过未知依赖;仅在确认删除所有基本表是有意操作后才使用此方法。

可丢弃数据库和命令行自动化的替代方案

对于完全可丢弃的架构,删除并重新创建数据库可能更简单,但它比删除表的范围更广。它需要相应的数据库级权限,并会删除基本表方法刻意保留的对象。不要把它描述为保留视图、例程或事件的方法。

mysqldump --add-drop-table --no-data 可以生成面向 shell 的删除脚本,在审查后适合自动化。不要直接在命令行中写入密码,检查生成的语句,并将凭据和可移植性问题与推荐的 SQL 方法分开处理。

限制与故障排除

如果没有表被删除,先确认选定的架构和 BASE TABLE 过滤器。非常大的架构可能超过 GROUP_CONCAT 限制,应有意识地提高限制或分批生成并检查语句。权限错误表示账户没有 DROP 访问权限。视图会按设计保留;只有在获批清理的一部分时才单独删除它们。最后,由于 DROP TABLE 会隐式提交,请从备份恢复,而不要期望回滚找回已删除的数据。