跨库查询必须显式写库名,USE在存储过程中无效;动态库名需用CONCAT+PREPARE拼接,?仅支持值不支持标识符;权限校验以调用者为准,非定义者;跨库JOIN性能差、锁难控,应避免。

跨库查询必须显式写库名,不能靠 USE 切换
MySQL 存储过程中 USE 语句无效,执行后不会改变后续 SQL 的默认数据库上下文。所有表引用都得带库前缀,否则会报 Table 'xxx' doesn't exist——哪怕当前连接用户对目标库有权限,也不行。
常见错误是写成:SELECT * FROM users,以为在存储过程里先 USE other_db 就能生效;实际它只影响该语句本身(且在存储过程里基本没用),后续查询仍按调用时的默认库解析。
- 正确写法永远是:
SELECT * FROM other_db.users - 如果库名或表名动态,得用
CONCAT+PREPARE拼接 SQL,不能直接变量插值 - 注意:库名、表名在
PREPARE中属于标识符,不能用参数占位符?替代,否则语法报错
调用者权限决定能否查到跨库数据,不是定义者权限
MySQL 存储过程默认以 DEFINER 权限执行,但跨库查询时,**实际生效的是调用者(INVOKER)对目标库的权限**。这是最容易踩坑的地方:你用高权限账号创建了过程,但普通应用账号调用时仍会报 Access denied for user ... to database 'other_db'。
- 确认调用账号对目标库有
SELECT权限,例如:GRANT SELECT ON other_db.* TO 'app_user'@'%' - 不要依赖
SQL SECURITY DEFINER绕过权限检查——它只管过程内部其他操作(如写日志表),不覆盖跨库 SELECT 的权限校验 - 若必须用 DEFINER 权限查跨库,只能把目标库的权限也授予 DEFINER 用户,并确保该用户是可信的 DBA 账号
动态库名需用 PREPARE + EXECUTE,不能直接拼字符串
当库名来自参数或变量(比如传入 db_name VARCHAR(64)),不能写 SELECT * FROM db_name.table_name——MySQL 会把它当字面量库名,而非变量值。
必须走预编译流程,否则语法错误或查错库:
SET @sql = CONCAT('SELECT * FROM ', db_name, '.users WHERE id = ?');
PREPARE stmt FROM @sql;
EXECUTE stmt USING in_id;
DEALLOCATE PREPARE stmt;-
@sql是用户变量,必须用SET赋值,不能用DECLARE声明的局部变量直接拼接 -
?占位符只支持值,不支持库名/表名等标识符;所以库名必须拼进字符串,值才用USING - 每次
PREPARE都要DEALLOCATE,否则可能触发MySQL Error 1470: Prepared statement not deallocated
跨库 JOIN 性能差、锁范围难控,别轻易上
在存储过程中写 SELECT a.id, b.name FROM db1.t1 a JOIN db2.t2 b ON a.ref = b.id 看似方便,但底层会跨引擎拉取数据,无法利用目标库的索引优化,执行计划也常失真。
- JOIN 跨库时,MySQL 默认用 Block Nested-Loop,内存消耗大,慢查询概率陡增
- 锁行为不可控:即使
db2.t2只读,也可能因 JOIN 触发db2上的元数据锁(MDL),阻塞其他 DDL - 更稳的做法是分两步:先查
db1.t1得到 ID 列表,再用IN查db2.t2,或让应用层聚合
真正需要跨库关联的场景极少,多数时候是设计阶段没理清边界。一旦发现存储过程里频繁跨库 JOIN,该重新评估分库逻辑了。


















