ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

MySQL查看有哪些表:命令、元数据与实战避坑

MySQL查看有哪些表:命令、元数据与实战避坑 最近被问到最多的基础问题就是“MySQL 查看有哪些表”。第一次听到这个问题我的第一反应也是这有什么难的SHOW TABLES;五个字母就完事了。但实际干过几年之后我才发现这个看似简单的操作背后其实藏着一整套门道。你是只想看当前库的表名还是想把视图、临时表一起揪出来是排查哪个表占空间还是写脚本批量处理一批表是命令行手敲还是通过程序代码去取需求不一样最优做法完全不一样。这篇文章就把“MySQL 查看有哪些表”这件事从头到尾捋一遍从最基础的命令讲到元数据查询、客户端工具、代码调用最后再聊几个我亲身踩过、也经常看别人踩的坑。不管是刚装完 MySQL 正在摸索的新手还是在生产环境里写脚本的运维和开发应该都能在里头找到用得上的东西。1. 先分清楚你要看的是库、表还是元数据1.1 没有选中数据库时SHOW TABLES 会直接报错很多新手第一次执行SHOW TABLES;的时候得到的结果不是表列表而是这么一行ERROR 1046 (3D000): No database selected原因很直白SHOW TABLES并不是在整台 MySQL 服务器上找表而是列出当前会话所选中数据库里的表。你要是不先用USE 库名;告诉 MySQL“我现在想看哪个库”它连目标都不知道自然就只能抛错。所以一个完整的、最朴素的查看流程是这样USE test_db; SHOW TABLES;如果你不想切换当前数据库也可以一步到位SHOW TABLES FROM test_db;这两种写法我平时都常用。USE之后查感觉更顺手命令短FROM适合那种不想改变当前上下文、偶尔嫖一眼别的库的情况。比如你在test_a库里操作突然想看看test_b库有哪些表直接SHOW TABLES FROM test_b;就不用USE test_b然后再切回来少一步也少一次误操作的可能。1.2 表和视图到底算不算“表”还有一个特别容易忽略的点SHOW TABLES默认列出来的不只是表还包括视图。如果你用SHOW TABLES;看到一行行名字然后想当然地把它们都当成普通表去处理后面很可能出事。举个例子你想对一个名字看着像表的对象执行DROP TABLE结果它其实是个视图MySQL 会直接报错ERROR 1051 (42S02): Unknown table表面上是“找不到表”实际上是你找错了对象类型。所以列出结果之后第一件事最好是分清楚哪些是BASE TABLE哪些是VIEW。做法很简单用SHOW FULL TABLESSHOW FULL TABLES;它会在结果里多一列Table_type能看到每个对象的类型。命令输出内容SHOW TABLES;只有一列名字表、视图混在一起SHOW FULL TABLES;加一列Table_type区分 BASE TABLE、VIEW从这里就引出了一个问题你查表的目的是什么如果只是“哦这个库看起来有十几张表”那怎么查都行。可如果你要把结果交给脚本做批量备份、批量删数据、给报表系统做元数据采集那就不能只看名字必须拿到完整、准确的结构信息。这就是下面要说的元数据视角。1.3 你需要的是“表名”还是“表信息”我打个比方SHOW TABLES相当于你在图书馆门口看索引屏它只告诉你“这个馆藏区域有哪些书”而INFORMATION_SCHEMA相当于图书馆的完整检索系统不光告诉你书名还告诉你作者、出版日期、页数、存放架位、借阅状态。日常随手看两眼用SHOW TABLES完全够了。但当你遇到下面这些场景就得换工具想知道每个表分别占用多少空间想知道哪些表是MyISAM、哪些是InnoDB想按名字模糊匹配并循环处理一批表想知道某个库到底有多少张表想知道哪些表是视图想在程序里拿到结构化的表清单。这些用SHOW TABLES都做不了或者说做起来很别扭。合适的选择是查询INFORMATION_SCHEMA.TABLES这个我会在第三部分展开。2. SHOW TABLES 的几个实用变体FULL、FROM、LIKE 和 WHERE2.1 最基础的 SHOW TABLES也有它的脾气SHOW TABLES;的默认输出只有一列列名是Tables_in_当前库名。比如你在shop库里执行列名就是Tables_in_shop。SHOW TABLES;------------------ | Tables_in_shop | ------------------ | orders | | products | | users | | v_user_summary | ------------------这里你能直接看到“当前库下面有哪些表”。但注意它不显示表的大小、引擎、行数、创建时间只给你一个名字。而且如果你当前库里的对象很多比如几百上千张表输出会很长翻起来很痛苦。我之前在维护一套老系统的时候单库表数超过两千张一屏SHOW TABLES的结果翻不到头。那时候最常用的反而是SHOW TABLES LIKE或者直接去查INFORMATION_SCHEMA。所以这里先提醒一下命令好用但要分场景用。2.2 FROM 和 LIKE跨库查看与模糊匹配SHOW TABLES FROM 库名我已经在前面提到了主要用来指定数据库。SHOW TABLES LIKE则是用来做简单模式匹配的。%表示匹配任意多个字符_表示匹配一个字符。比如我想看所有以tmp_开头的表SHOW TABLES LIKE tmp_%;想看名字里带log的表SHOW TABLES LIKE %log%;这两种写法在表特别多的库里面价值非常大。你不需要拉出全量列表再肉眼过滤直接让 MySQL 帮你筛。比较坑的地方在于LIKE的通配符和 SQL 标准一致但毕竟_也有特殊含义。你如果真想找名字里带下划线的表比如order_detail_2024这种直接写LIKE order_detail_2024是符合预期的因为普通字符串里的下划线会被当成通配符。但如果你要匹配一个本身就是下划线字符的名字就得用ESCAPE语法来转义。比如SHOW TABLES LIKE order\_detail%;加上反斜杠之后_就表示真实的下划线而不是任意字符。这个细节在表名含下划线的业务库里很容易踩尤其是当你用程序拼 SQL 时稍有疏忽就会把模式写错导致漏表。2.3 FULL 和 WHERE把类型和作用域塞进结果SHOW FULL TABLES前面已经说过它会额外显示Table_type。实际执行长这样SHOW FULL TABLES;------------------------------ | Tables_in_shop | Table_type | ------------------------------ | orders | BASE TABLE | | products | BASE TABLE | | v_user_summary | VIEW | ------------------------------这个命令最大的好处就是一眼分辨视图。我们做数据归档的时候最怕把视图当成表去处理SHOW FULL TABLES能直接规避这种低级错误。那WHERE条件怎么用SHOW TABLES其实支持在结尾加WHERE字段名就是输出列的名字也就是Tables_in_库名。例如SHOW TABLES FROM shop WHERE Tables_in_shop LIKE tmp_%;这样可以把LIKE和WHERE混着用。不过说实话一旦你开始用WHERE过滤我倒建议直接转战INFORMATION_SCHEMA因为那里的字段更丰富、语法更标准你不需要记住Tables_in_库名这种动态列名。还有一个经常被忽略的兄弟命令SHOW TABLE STATUS。SHOW TABLE STATUS FROM shop;它和SHOW TABLES不一样输出的不是一列名字而是类似SHOW CREATE TABLE那样的一大列属性包含Engine、Rows、Data_length、Create_time等。这个命令可以看作SHOW TABLES的“超级增强版”适合你在命令行里快速扫一眼表的大小和引擎又不想写复杂 SQL 的时候用。它的弊端也很明显一次查全库输出行数多、列也长肉眼扫起来会累而且它的Rows同样是估算值不能当精确行数用。3. INFORMATION_SCHEMA.TABLES被低估的元数据宝库3.1 一句 SQL胜过一百次 SHOW TABLESINFORMATION_SCHEMA是 MySQL 自带的一个“库中库”里面存放着所有数据库的元数据。其中最常用的一张表就是TABLES。它的每一行对应一个库中的表或视图字段非常多下面列几个最常用的字段名含义TABLE_SCHEMA数据库名TABLE_NAME表名TABLE_TYPEBASE TABLE或VIEWENGINE存储引擎比如 InnoDB、MyISAMTABLE_ROWS预估行数InnoDB 下不是精确值DATA_LENGTH数据占用字节数INDEX_LENGTH索引占用字节数DATA_FREE碎片可回收字节数CREATE_TIME创建时间UPDATE_TIME最近更新时间TABLE_COLLATION表的排序规则有了这张表你可以在命令行里直接写 SQL 来获取任何维度的表清单。比如查看shop库里的所有表SELECT TABLE_NAME, TABLE_TYPE, ENGINE FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA shop ORDER BY TABLE_NAME;这和SHOW TABLES的结果差不多但多出了引擎和类型列。如果你需要把这些信息带到别的系统里直接执行这条 SQL输出更容易解析。我的习惯是需要“给人看”的结果用SHOW命令需要“给程序用”的结果一律写INFORMATION_SCHEMA查询。3.2 几个特别能打的查询场景第一个场景统计每个库有多少张业务表排除视图。SELECT TABLE_SCHEMA, COUNT(*) AS table_count FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE BASE TABLE GROUP BY TABLE_SCHEMA ORDER BY table_count DESC;这条 SQL 在梳理整个实例的时候特别好用。我接手过一些“数据库黑洞”一查table_count直接上百上千你根本不知道哪个库才是核心库。用这条 SQL 扫一眼就知道表数据主要集中在哪里。第二个场景找出数据量特别大的“胖子表”。SELECT TABLE_SCHEMA, TABLE_NAME, ROUND(DATA_LENGTH / 1024 / 1024, 2) AS data_mb, ROUND(INDEX_LENGTH / 1024 / 1024, 2) AS index_mb FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA shop ORDER BY DATA_LENGTH DESC LIMIT 20;这条 SQL 帮我快速定位过好几个磁盘告警的根因。很多时候一看排名前几的data_mb立刻就能判断出哪张日志表该归档了。第三个场景把库里的视图单独捞出来。SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE VIEW ORDER BY TABLE_SCHEMA, TABLE_NAME;在做数据库迁移、重构的时候视图和表的处理顺序不一样通常先建表再建视图。如果迁移方案没分开后面会因为依赖关系报错。提前用这条 SQL 把视图清出来迁移步骤就清晰了。第四个场景查看哪些表用了非 InnoDB 引擎。SELECT TABLE_SCHEMA, TABLE_NAME, ENGINE FROM INFORMATION_SCHEMA.TABLES WHERE ENGINE InnoDB AND TABLE_TYPE BASE TABLE;在一些老项目里偶尔还能看到MyISAM表。如果你准备做在线 DDL、事务改造就得先把这些非事务表找出来评估风险。3.3 TABLE_ROWS 是预估值别拿它当精确行数这个坑我必须单独拿出来讲。INFORMATION_SCHEMA.TABLES里的TABLE_ROWS字段在 InnoDB 引擎下是一个统计估算值不是实时精确值。MySQL 在更新统计信息时会给它一个近似数误差可能很大尤其是大表。你可能会遇到这种情况用SHOW TABLE STATUS看某张表Rows显示 100 万觉得它也不大结果你写SELECT COUNT(*) FROM t一数实际是 800 万。这就是TABLE_ROWS估算导致的误判。所以做容量评估、分页总数统计、报表取数的时候不要依赖TABLE_ROWS。要拿精确行数老老实实执行COUNT(*)。但反过来如果你只是想快速比较一下哪几张表“看起来特别大”那TABLE_ROWS和DATA_LENGTH就足够了不用每个都跑一遍COUNT(*)把数据库压垮。总结一句话元数据表适合做粗筛和普查精细工作时还是要回到真实数据上去验证。4. 命令行、客户端和代码里的四种取表姿势4.1 命令行脚本批量导出表名很多人以为查看表只能在交互式终端里敲命令其实写脚本的时候更常用的是mysql -e这种非交互方式。比如你想把shop库里的所有表名导出成一个文本文件一条命令就能完成mysql -uroot -p -N -e SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMAshop AND TABLE_TYPEBASE TABLE tables.txt这里-N的作用是去掉输出里的列标题省的后面再处理表头。-e是直接在命令行执行 SQL不进入交互模式。得到的tables.txt每行一个表名可以直接喂给while read循环。接下来用 shell 循环做点批量操作就很顺了。比如逐个统计表行数while read t; do echo $t ; mysql -uroot -p -N -e SELECT COUNT(*) FROM shop.$t; done tables.txt这种脚本我在做数据核对、批量归档时经常用。注意拼接 SQL 的时候表名来自文件如果表名里带了特殊字符最好用反引号包一下。不过从根上建议建表就不要用怪名字省得后面所有脚本都难受。4.2 图形客户端里鼠标点几下就好如果你用的是 Navicat、DBeaver、MySQL Workbench 这类图形客户端查看有哪些表基本不需要动脑子。在 DBeaver 或 Navicat 里左侧导航栏展开对应数据库会自动列出表、视图、函数等分类。你可以直接搜索表名也可以右键某个表查看CREATE TABLE语句、表数据、索引信息。真正比命令行舒服的地方在于图形客户端可以“可视化”表之间的外键关系你双击一张表关联关系一目了然。不过图形客户端也有个局限字段太多、表太多的时候手动“点”不如 SQL 高效。比如你想查“所有超过 1GB 的表”鼠标点一百多张表显然不现实。所以我的建议是交互式探索用客户端自动化、批处理、监控脚本一律走命令行或程序代码。两者不冲突。4.3 Python 和 JDBC程序化获取表清单如果你要从程序里获取表清单有几种常见写法。以 Python 的pymysql为例import pymysql conn pymysql.connect( host127.0.0.1, userroot, passwordpassword, databaseshop ) cursor conn.cursor() cursor.execute(SHOW TABLES FROM shop) tables [row[0] for row in cursor.fetchall()] print(tables)直接把SHOW TABLES的结果取出来是最省事的办法。但你要是连表类型、引擎一起要就最好写INFORMATION_SCHEMA查询cursor.execute( SELECT TABLE_NAME, TABLE_TYPE, ENGINE FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA %s ORDER BY TABLE_NAME , (shop,)) for row in cursor.fetchall(): print(row)用参数绑定传库名避免字符串拼接可能带来的 SQL 注入问题。在程序里我几乎不直接拼字符串这是一个我很看重的习惯。Java JDBC 里也不复杂用DatabaseMetaData就能拿DatabaseMetaData metaData connection.getMetaData(); try (ResultSet rs metaData.getTables(shop, null, %, new String[]{TABLE})) { while (rs.next()) { String tableName rs.getString(TABLE_NAME); System.out.println(tableName); } }这里getTables的四个参数分别是catalog对应 MySQL 的库名、schemaPatternMySQL 传 null、tableNamePattern%表示全部、types传TABLE就只要基表传VIEW就只要视图。如果你只想查某一类表比如所有以log结尾的表可以把第三个参数改成%log。这类程序化获取表的用法在做数据质量平台、自动监控、自动化发布工具时需要经常用到。把表清单拿到手后面才能接上建表、备份、数据抽取这一整套流程。4.4 存储过程里动态取表名实现一批表统一处理如果你需要在存储过程里遍历一个库中的所有表不能像在应用代码里那么随意地动态造 SQL。这里有 MySQL 存储过程的一个标准写法用游标遍历INFORMATION_SCHEMA.TABLES查出来的表名再拼 SQL 执行。下面是一个简化示例把shop库里所有表名打印出来DELIMITER // CREATE PROCEDURE list_tables(IN dbname VARCHAR(64)) BEGIN DECLARE done INT DEFAULT 0; DECLARE tbl VARCHAR(64); DECLARE cur CURSOR FOR SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA dbname AND TABLE_TYPE BASE TABLE; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done 1; OPEN cur; read_loop: LOOP FETCH cur INTO tbl; IF done 1 THEN LEAVE read_loop; END IF; SELECT tbl; END LOOP; CLOSE cur; END// DELIMITER ;实际用到这种游标的地方通常是对一批表做归档、加字段、改字符集。要注意游标内部的动态 SQL 很考验细节表名要校验防止特殊字符注入执行前最好先查一次表是否存在处理过程中尽量避免长时间持有元数据锁。存储过程不是首选的批量操作方案但在某些无法改应用代码的历史系统里它又是唯一能落地的办法。5. 容易踩的坑大小写、权限、视图和临时表5.1 大小写敏感的表名让人很崩溃MySQL 表名是否区分大小写和lower_case_table_names这个参数有关不同平台默认值还不一样。Linux 下默认区分大小写Windows 下默认不区分macOS 介于两者之间。也就是说同一套 SQL在某些环境里能查到Users在另一些环境里就必须写成users。我在跨环境迁移的时候吃过这个亏。开发环境在 Mac 上表名是UserInfo测试环境是 Linux 服务器lower_case_table_names0结果赶上线的时候某条 SQL 写成了SELECT * FROM userinfo;直接报“表不存在”。排查了半天最后发现就是大小写没对上。更隐蔽的是SHOW TABLES LIKE的大小写匹配规则。它其实和字符串比较的排序规则有关。想要避免这种麻烦最省心的办法就是建表时统一用小写加下划线比如user_info、order_detail别搞UserInfo、ORDERDETAIL这种风格。命名风格统一很多隐患能从根上掐掉。5.2 权限不足导致“表少了”在排查“这个库到底有哪些表”时如果发现SHOW TABLES的结果比预期少了很多先别怀疑表被删了先检查当前账号的权限。MySQL 的意图很明确你只能看到你有权限访问的对象。如果一个账号只被授权了某几张表的SELECT权限那么SHOW TABLES甚至INFORMATION_SCHEMA.TABLES都只会返回它有权限的那部分表而不是这个库的完整列表。这会造成一种假象——明明库里有 50 张表你用自己的账号一查只有 5 张以为数据缺失了。遇到这种问题用管理员账号重新查一次就能确定到底是不是权限问题。如果确实需要这个账号看到所有表名就得给对应库授予SHOW VIEW、SELECT之类的必要权限。权限设计归设计但至少要让自己对“为什么看不到全表”心里有数。5.3 视图和临时表都不是常规“表”前面讲过视图会出现在SHOW TABLES的输出里很容易被当成普通表。再补一个容易坑人的点在 MySQL 里删除视图要用DROP VIEW不是DROP TABLE。如果你写脚本时用SHOW TABLES拿到一堆名字然后统一执行DROP TABLE遇到视图那一条就报错脚本直接中断。临时表则是另一个极端SHOW TABLES默认不会列出当前会话创建的临时表。也就是说你在同一个会话里执行CREATE TEMPORARY TABLE tmp_test (id INT); SHOW TABLES;结果里大概率看不到tmp_test。不要以为它没建成功可以用SHOW CREATE TABLE tmp_test;验证。这个坑在调试存储过程、写复杂报表的时候特别容易遇到查了半天看不到表急得不行。所以看到“表清单不全”时要立刻有条件反射是不是有视图是不是有临时表是不是有权限限制把这些可能性都排除一遍再怀疑真丢表。5.4 别去文件系统里数表有些老资料会建议你直接去 MySQL 数据目录下面数.frm文件或者表空间文件来确定有哪些表。这个思路在 MySQL 5.6、5.7 时代还有一定道理因为那时候表结构就在.frm文件里。但到了 MySQL 8.0数据字典已经挪到了内部存储表结构不再以独立.frm文件的形式裸露在数据目录中。你去数文件名数出来的东西既不完整也不准确还会被各种隐藏文件、临时文件干扰。更重要的是数据目录里的物理文件本来就不是给业务直接读的。你通过文件系统只能看到“物理文件层面”的表看不到逻辑意义上的视图、临时表也看不到哪些表属于哪个库的准确关联。真正权威的表清单永远是从INFORMATION_SCHEMA查出来的或者由SHOW TABLES提供的。记住这一点能省掉无数无谓的排查。文末我再补一句不管用哪条命令先想清楚你要的到底是名字、类型还是各项元数据。把需求拆明白了选命令就是顺手的事。
返回列表