最可靠方式是查information_schema.tables,需同时指定table_schema和table_name,注意大小写、权限及临时表不显示等问题。

直接查 information_schema.tables 最可靠
MySQL 没有内置的 IF EXISTS 语法用于判断表是否存在(不像 DROP TABLE IF EXISTS 那样可用),所以得手动查元数据。最稳妥的方式就是查 information_schema.tables,它在所有 MySQL 版本中都稳定存在,且权限要求低(只需对目标库有 SELECT 权限)。
注意:不能只查 table_name,必须同时指定 table_schema(即数据库名),否则跨库同名表会误判。
- 查询语句必须包含
WHERE table_schema = DATABASE()或显式写死库名,比如WHERE table_schema = 'mydb' - 表名区分大小写取决于系统变量
lower_case_table_names,建议统一用小写比对,或用LOWER()包裹字段 - 不要用
SHOW TABLES LIKE 'xxx'在存储过程中——它无法被直接赋值给变量,还得绕路用PREPARE,没必要
在存储过程中用 SELECT COUNT(*) INTO 判断
这是最常用、最易读的做法:查匹配行数,存进一个 INT 变量,再用 IF 分支处理。
DELIMITER $$
CREATE PROCEDURE check_table_exists()
BEGIN
DECLARE table_count INT DEFAULT 0;
SELECT COUNT(*) INTO table_count
FROM information_schema.tables
WHERE table_schema = DATABASE()
AND table_name = 'users';
IF table_count > 0 THEN
SELECT 'Table exists' AS msg;
ELSE
SELECT 'Table does not exist' AS msg;
END IF;
END$$
DELIMITER ;
-
INTO必须跟在SELECT后面,且查询结果只能有一行一列,否则报错Subquery returns more than 1 row - 如果表名是变量(比如传参进来),要用
CONCAT拼接或预处理语句,不能直接写table_name = my_table_var(因为这是字面量比对) - 别漏写
DELIMITER,否则分号会让 MySQL 提前结束过程定义
用 EXISTS 子查询更高效但写法稍绕
当只关心“是否存在”而不关心数量时,EXISTS 比 COUNT(*) 更快,尤其在大库中——它找到第一条就停,不用扫全表。
但它不能直接 INTO,得包装一层:
SELECT EXISTS(
SELECT 1 FROM information_schema.tables
WHERE table_schema = DATABASE()
AND table_name = 'orders'
) INTO table_exists;
-
EXISTS返回1或0,适合赋给TINYINT或BOOLEAN类型变量 - 里面子查询的
SELECT 1是惯用写法,不推荐写SELECT *,虽然也能跑通,但语义不清 - 这个写法在 MySQL 5.7+ 完全没问题,但某些旧版(如 5.5)对子查询嵌套层级敏感,若报错可退回用
COUNT(*)方案
常见坑:权限、字符集和临时表干扰
实际部署时,最容易栽在这三处,而不是逻辑本身。
- 用户账号没被授予对
information_schema的SELECT权限?会静默返回空结果(不是报错),导致永远走ELSE分支 - 数据库名或表名含下划线、短横线等特殊字符?确保它们在 SQL 字符串里是合法标识符,必要时用反引号包裹:
table_name = `my-table`(但注意:反引号不能放进字符串变量里) - 临时表(
CREATE TEMPORARY TABLE)不会出现在information_schema.tables中,查不到是正常的——这不是 bug,是设计如此
真正难搞的是跨 schema 场景:当过程在 db1 里执行,却要查 db2 下的表,DATABASE() 就不管用了,必须硬编码或传参指定 table_schema,而且得确认当前用户对该 schema 也有查询权限。











