1. 为什么跨库巡检总在“连一个库改一次连接串”上翻车
跨库巡检这件事,听起来只是“把同一段 SQL 在多个库上跑一遍”,真做起来却很容易变成体力活。你手上有三套实例、每套实例下面十几个业务库,想统计每个库的表数量、最大表行数、最近一次备份时间、有没有异常增长的日志表。最直觉的做法是写一个脚本,把连接串里的数据库名当变量,循环替换、循环执行。问题是:连接串一换,连接池要重建;库一多,脚本跑到一半断了,你根本不知道断在哪个库;某个库权限不足直接抛异常,整个循环就停了,前面的结果也没落盘。
我试过用 Python 拼连接串的方式做这件事,二十多个库跑下来,最耗时的不是查询本身,而是反复建连和异常处理。后来换成“在一个能访问实例元数据的库里,用游标把库名逐个取出来,再动态执行巡检 SQL”,整个流程才稳定下来。这就是 SQL 游标遍历所有数据库的思路:游标负责“列出有哪些库”,循环负责“逐个进去干活”,异常处理负责“某个库挂了不影响其他库”。
这里要先说清楚一个前提:游标遍历所有数据库,通常是在同一个实例内遍历sys.databases或information_schema.schemata这类系统视图。如果你要跨多个实例,那得先有一个“实例清单”,再对每个实例分别跑一遍游标脚本。本文的场景是:多实例连接 + 单实例内游标逐库 + 异常跳过 + 结果汇总,最后把巡检结果统一输出。
适合谁看?适合做 DBA、数据平台、后端运维的同学,尤其是手上管着多套数据库、又不想为每个库单独写脚本的人。核心检索词就是 SQL 游标、跨库巡检、数据库遍历、元数据采集。下面我会先讲清楚 TaoToken 统一 Key 怎么接入,再给可复制的游标脚本模板,最后逐库执行验证,把全库清单和状态输出一次跑通。
需要提醒的是,游标遍历所有数据库时,sys.databases里会包含master、tempdb、model、msdb这些系统库。如果你只想巡检业务库,记得用WHERE name NOT IN (...)过滤掉,或者用命名前缀过滤,比如WHERE name LIKE 'WHQJ%'。这一步不做,后面统计出来的表数量会被系统库污染。
2. TaoToken 统一 Key 前置:把多实例连接收敛成一套配置
跨库巡检的第一个痛点是连接管理。多套实例、多个库,如果每个库都配一套账号密码,脚本里就会散落一堆凭据,改一次密码要改十几个地方。TaoToken 在这里的作用是把模型调用和数据库巡检脚本的接入方式统一起来:你拿一个 Key,配一个 Base URL,就能在脚本里用同一套配置去调用模型能力,比如让模型帮你生成巡检 SQL、解释异常结果、汇总巡检报告。
先拿 Key。打开官网 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,注册后在控制台创建 API Key。控制台地址是 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API Key 管理页在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。Key 拿到后不要写死在脚本里,放到环境变量里,比如TAOTOKEN_API_KEY。
Base URL 用 https://taotoken.net/api ,注意这个地址不加 UTM 参数,直接写就行。模型 ID 按你实际要用的填,比如做代码生成和 SQL 解释,选一个你账号下可用的模型即可。这里要强调三件套:Base URL、API Key、Model ID,缺一不可。很多接入失败不是 Key 错了,而是 Base URL 多写了斜杠或者少了/api。
如果你用的是 Claude Code 这类编码工具,接入配置可以写成环境变量或者配置文件。下面给一个通用的环境变量写法,Linux/macOS 下直接 export,Windows 下用 set 或者写进系统环境变量:
export TAOTOKEN_BASE_URL="https://taotoken.net/api" export TAOTOKEN_API_KEY="sk-你的Key" export TAOTOKEN_MODEL="你的模型ID"如果你用的是 Codex 的auth.json,配置结构大致是这样,注意路径和字段名要和你的工具版本一致:
{ "base_url": "https://taotoken.net/api", "api_key": "sk-你的Key", "model": "你的模型ID" }如果你用的是 Cline 或者带 MCP 的编辑器插件,配置里同样要写全三件套。MCP 的配置文件通常是 JSON,字段名可能是baseUrl、apiKey、model,具体看你用的插件版本。这里不展开每个工具的细节,核心就一句话:Base URL 指向 https://taotoken.net/api ,Key 用你创建的那把,Model ID 填你账号下可用的。
为什么要先做这一步?因为跨库巡检脚本里,你可能会让模型帮你做两件事:一是根据库名和表结构生成巡检 SQL,二是把巡检结果汇总成可读报告。如果连接配置散落在脚本各处,后面排障会很痛苦。统一 Key 之后,脚本里只读环境变量,换环境只改环境变量,不动代码。
还有一个实际好处:TaoToken 的模型对话入口 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 可以用来验证 Key 是否可用。你先在对话页发一条消息,确认能正常返回,再去跑脚本。这样能把“Key 问题”和“脚本问题”分开,排障效率高很多。
3. 可复制配置:游标脚本模板与统一 Key 配置片段
这一节给可直接复制的配置和脚本。先给统一 Key 的配置片段,再给 SQL 游标遍历所有数据库的模板。脚本以 SQL Server 的 T-SQL 为例,因为sys.databases+ 游标的组合最典型,其他数据库的游标语法类似,改一下系统视图和循环语法即可。
先看统一 Key 的配置片段。如果你用 Python 脚本调模型,可以这样读环境变量:
import os BASE_URL = os.environ.get("TAOTOKEN_BASE_URL", "https://taotoken.net/api") API_KEY = os.environ.get("TAOTOKEN_API_KEY", "") MODEL_ID = os.environ.get("TAOTOKEN_MODEL", "") assert API_KEY, "请先设置 TAOTOKEN_API_KEY" assert MODEL_ID, "请先设置 TAOTOKEN_MODEL"如果你用 TOML 配置文件,比如某些 CLI 工具支持config.toml,写法如下:
[provider] base_url = "https://taotoken.net/api" api_key = "sk-你的Key" model = "你的模型ID"注意,配置文件里的 Key 不要提交到 Git。生产环境用环境变量注入,本地调试可以用.env文件,但.env要加进.gitignore。
接下来是核心:SQL 游标遍历所有数据库的模板。这个模板做三件事:声明游标取库名、循环逐个库执行巡检 SQL、把结果插入汇总表。先建汇总表:
IF OBJECT_ID('dbo.DBInspectResult', 'U') IS NULL BEGIN CREATE TABLE dbo.DBInspectResult ( id INT IDENTITY(1,1) PRIMARY KEY, db_name NVARCHAR(128), table_count INT, total_rows BIGINT, inspect_time DATETIME DEFAULT GETDATE(), status NVARCHAR(50), err_msg NVARCHAR(4000) ); END然后是游标脚本主体。这里用动态 SQL 拼接,因为要跨库查询,必须用EXEC或者sp_executesql:
SET NOCOUNT ON; DECLARE @db_name NVARCHAR(128); DECLARE @sql NVARCHAR(MAX); DECLARE @table_count INT; DECLARE @total_rows BIGINT; DECLARE db_cursor CURSOR LOCAL FAST_FORWARD FOR SELECT name FROM sys.databases WHERE name NOT IN ('master', 'tempdb', 'model', 'msdb') AND state_desc = 'ONLINE' AND name LIKE 'WHQJ%' ORDER BY name; OPEN db_cursor; FETCH NEXT FROM db_cursor INTO @db_name; WHILE @@FETCH_STATUS = 0 BEGIN BEGIN TRY SET @sql = N' SELECT @tc = COUNT(*), @tr = ISNULL(SUM(p.rows), 0) FROM [' + @db_name + N'].sys.tables t JOIN [' + @db_name + N'].sys.partitions p ON t.object_id = p.object_id AND p.index_id IN (0,1) WHERE t.is_ms_shipped = 0;'; EXEC sp_executesql @sql, N'@tc INT OUTPUT, @tr BIGINT OUTPUT', @tc = @table_count OUTPUT, @tr = @total_rows OUTPUT; INSERT INTO dbo.DBInspectResult (db_name, table_count, total_rows, status) VALUES (@db_name, @table_count, @total_rows, 'OK'); END TRY BEGIN CATCH INSERT INTO dbo.DBInspectResult (db_name, status, err_msg) VALUES (@db_name, 'ERROR', ERROR_MESSAGE()); END CATCH; FETCH NEXT FROM db_cursor INTO @db_name; END CLOSE db_cursor; DEALLOCATE db_cursor;这段脚本的关键点:CURSOR LOCAL FAST_FORWARD表示只读、只进、本地游标,性能比默认游标好;TRY...CATCH保证某个库权限不足或离线时,循环不会中断,错误信息落到err_msg;sys.partitions里index_id IN (0,1)对应堆表和聚集索引,SUM(p.rows)才是比较准的行数估算。
如果你要跨多个实例,就把这段脚本包一层“实例循环”。实例清单可以放在一张配置表里,比如dbo.InstanceList,字段是instance_name、conn_str。然后用sqlcmd或者 Python 的pyodbc对每个实例执行上面的游标脚本。这里不展开跨实例的完整代码,核心是:实例循环在外层,库游标在内层,两层都做异常捕获。
配置片段和脚本模板都给全了。下一步是逐库执行验证,看结果表里是不是每个库都有一行,状态是不是 OK。
4. 逐库执行验证:从全库清单到状态输出一次跑通
脚本写好了,别急着一次性跑全量。先做小范围验证,确认游标能正确取到库名、动态 SQL 能执行、结果能落表。验证分三步:先看游标取到的库清单,再单库试跑,最后全量跑并检查结果。
第一步,只看游标取到的库名,不执行巡检 SQL。把游标里的SELECT name单独拿出来跑:
SELECT name, state_desc, recovery_model_desc FROM sys.databases WHERE name NOT IN ('master', 'tempdb', 'model', 'msdb') AND state_desc = 'ONLINE' AND name LIKE 'WHQJ%' ORDER BY name;这一步确认三件事:库名列表是不是你预期的、有没有库处于OFFLINE或RESTORING状态、命名前缀过滤对不对。如果这里多出或少掉库,后面巡检结果一定不对。我踩过的坑是:有个库处于RECOVERY_PENDING,游标取到了但动态 SQL 执行时报错,后来在WHERE里加了state_desc = 'ONLINE'才稳定。
第二步,单库试跑。把游标脚本里的@db_name手动赋值为一个具体库,比如WHQJ_Game01,然后执行动态 SQL 部分,看@table_count和@total_rows有没有值:
DECLARE @db_name NVARCHAR(128) = N'WHQJ_Game01'; DECLARE @sql NVARCHAR(MAX); DECLARE @table_count INT; DECLARE @total_rows BIGINT; SET @sql = N' SELECT @tc = COUNT(*), @tr = ISNULL(SUM(p.rows), 0) FROM [' + @db_name + N'].sys.tables t JOIN [' + @db_name + N'].sys.partitions p ON t.object_id = p.object_id AND p.index_id IN (0,1) WHERE t.is_ms_shipped = 0;'; EXEC sp_executesql @sql, N'@tc INT OUTPUT, @tr BIGINT OUTPUT', @tc = @table_count OUTPUT, @tr = @total_rows OUTPUT; SELECT @db_name AS db_name, @table_count AS table_count, @total_rows AS total_rows;如果这一步报“对象名无效”或者“权限不足”,说明当前登录账号对目标库没有VIEW DEFINITION或SELECT权限。解决办法是给巡检账号授予目标库的db_datareader角色,或者单独授SELECT ON SCHEMA::sys。注意,不要用 sa 跑巡检脚本,权限太大,风险高。
第三步,全量跑游标脚本,然后查结果表:
SELECT db_name, table_count, total_rows, status, err_msg, inspect_time FROM dbo.DBInspectResult ORDER BY inspect_time DESC, db_name;预期结果是:每个符合条件的库都有一行,status大部分是OK,个别权限不足的库是ERROR且err_msg里有具体原因。如果结果表里只有一行,说明游标没循环起来,检查FETCH NEXT是不是漏了,或者@@FETCH_STATUS判断写反了。如果结果表里库名重复,说明游标没DEALLOCATE,重复执行时旧游标还在。
验证通过后,你可以把巡检结果导出成 CSV,或者让模型帮你汇总。比如把结果表的前 50 行贴到模型对话页 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,让它按“表数量异常”“行数增长异常”“错误库”分类。这一步不是必须的,但能省掉手工看结果的时间。
如果你要长期跑这个巡检,建议把脚本做成定时任务,比如 SQL Server Agent 的 Job,每天凌晨跑一次,结果表保留最近 30 天。长期编码和 Agent 场景可以看 Coding Plan https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,把巡检脚本和模型汇总串成自动化流程。
5. 常见错排查:401、local proxy failed、reading choices、OAuth 对照
跨库巡检脚本本身和模型接入是两条线,但排障时经常混在一起。这一节把常见报错按“脚本侧”和“接入侧”分开,对照真实报错给排查路径。
先看接入侧。第一个高频报错是401 Unauthorized。原因通常是 Key 没设置、Key 写错、或者环境变量没生效。排查顺序:先确认TAOTOKEN_API_KEY在当前 shell 里能echo出来;再确认 Base URL 是 https://taotoken.net/api ,没有多余斜杠;最后去 API Keys 页面 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 确认 Key 没过期、没被删。注意,Key 只在创建时显示一次,如果忘了就重新建一把。
第二个报错是local proxy failed。这个通常出现在你本地配了代理,但代理没启动或者端口不对。排查:检查环境变量HTTP_PROXY、HTTPS_PROXY是不是指向了一个不存在的端口;如果不需要代理,直接unset掉。注意,这里说的是本地网络配置问题,不是让你去用什么特殊网络工具,企业内网环境按公司规范配置即可。
第三个报错是reading choices相关,比如error reading choices: unexpected end of JSON input。这通常是响应体不是预期 JSON,可能是 Base URL 指错了,或者模型 ID 不存在。排查:先用 curl 直接打一次接口,看返回体长什么样:
curl -s -X POST "https://taotoken.net/api/v1/chat/completions" \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model":"你的模型ID","messages":[{"role":"user","content":"ping"}]}'如果返回的是 HTML 而不是 JSON,说明 URL 路径不对。如果返回model not found,说明 Model ID 填错了。这一步能把“网络问题”和“参数问题”分开。
第四个是OAuth相关报错。如果你用的工具走 OAuth 流程,报错可能是OAuth token expired或invalid_grant。排查:重新走一遍授权流程,确认回调地址和工具里配的一致。如果你用的是 API Key 模式,就不会有 OAuth 问题,所以能不用 OAuth 就不用,Key 模式更直接。
再看脚本侧。第一个常见错是游标未关闭,表现为重复执行脚本时报“游标已存在”。解决办法:在脚本开头加IF CURSOR_STATUS('global','db_cursor') >= -1 DEALLOCATE db_cursor;,或者每次执行前手动DEALLOCATE。第二个错是动态 SQL 拼接注入风险,虽然库名来自sys.databases,相对可信,但仍建议用QUOTENAME(@db_name)包一层:
SET @sql = N'SELECT @tc = COUNT(*) FROM ' + QUOTENAME(@db_name) + N'.sys.tables;';第三个错是权限不足导致整个循环中断。如果你忘了写TRY...CATCH,一个库报错,后面所有库都不跑了。加上TRY...CATCH后,错误落到err_msg,循环继续。第四个错是结果表字段长度不够,err_msg用NVARCHAR(4000),如果错误信息超长会被截断,排查时看不到完整原因。可以改成NVARCHAR(MAX)。
还有一个容易忽略的点:sys.partitions的rows是估算值,不是精确值。如果你要精确行数,得对每个表COUNT(*),但那会非常慢。巡检场景用估算值就够了,别为了精确把脚本跑成几小时。
排障时记住一个原则:先确认接入侧三件套(Base URL、Key、Model ID)没问题,再查脚本侧游标和动态 SQL。接入侧问题去 API Keys 和接入文档 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 对照;脚本侧问题看错误信息和结果表里的err_msg。
6. 把巡检脚本接进日常:从手动跑到定时汇总
脚本跑通一次不难,难的是让它稳定地跑下去。这一节说几个实际经验,帮你把跨库巡检从“手动执行”变成“日常可依赖”。
第一,把游标脚本存成存储过程,比如dbo.usp_InspectAllDatabases,参数是库名前缀和是否包含系统库。这样调用方只需要EXEC dbo.usp_InspectAllDatabases @prefix = 'WHQJ%';,不用每次贴一大段脚本。存储过程里记得加SET NOCOUNT ON,避免多余的结果集干扰调用方。
第二,结果表加分区或者按天清理。巡检结果每天一行每库,一个月就是几百行,一年几千行,量不大,但时间久了查询会慢。可以加一个inspect_date字段,每天跑之前删掉 30 天前的数据,或者按inspect_time建索引。
第三,把模型汇总接进来。巡检结果表跑完后,用 Python 读结果,调 TaoToken 的接口让模型生成一段摘要,比如“今天有 3 个库表数量比昨天多 10% 以上,2 个库巡检失败,失败原因是权限不足”。这段摘要可以发到企业微信或者邮件。模型调用就用第 2 节配好的三件套,Base URL 是 https://taotoken.net/api ,Key 从环境变量读。
第四,跨实例场景用配置表驱动。建一张dbo.InstanceList,字段instance_name、conn_str、enabled。外层循环读这张表,对每个启用的实例执行游标脚本。这样加实例只改表,不改代码。注意conn_str里的密码要加密存储,或者用 Windows 集成认证,别明文写在表里。
第五,监控游标脚本本身的执行时间。如果某个库特别大,sys.partitions查询也会慢。可以在结果表里加duration_ms字段,记录每个库的巡检耗时,超过阈值的库单独关注。我实测下来,二十多个库的巡检,大部分库在 100ms 内完成,个别大库会到 1-2 秒,整体可控。
最后说一个实际技巧:游标遍历所有数据库时,如果库数量超过 50 个,建议分批跑,比如按首字母分两批,避免一次性占用太多连接和锁。虽然sys.databases查询本身很轻,但动态 SQL 跨库查询会短暂持有锁,分批能降低对业务的影响。
整套流程走下来,你得到的是:一套统一 Key 配置、一个可复制的游标脚本模板、一张全库巡检结果表、一套排障对照。下次再加库或者加实例,改配置就行,不用重写脚本。巡检结果汇总和模型对话可以在 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 验证,长期自动化可以看 Coding Plan https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。脚本先跑通单库,再跑全量,最后接定时任务,这个顺序别跳。