1. 为什么要把 SQLPA 的输入源改到 TaoToken
先说清楚这篇要解决的事。Oracle 的 SQL 调优链路里,SQL Performance Analyzer(SQLPA)负责对比数据库变更前后的 SQL 执行表现,而它的输入源之一就是 SQL Tuning Set(STS)。STS 里装的是从 Cursor Cache、AWR、SQL Trace 等渠道采集来的 SQL 语句、执行上下文和执行统计。问题在于,很多团队把 STS 采集、SQLPA 分析、报告解读这一整条链路都放在本地脚本里跑,一旦涉及调用大模型做 SQL 文本理解、执行计划解读、改写建议生成,就会遇到 Key 散落各处、模型切换麻烦、调用记录无法统一的问题。
我这次要做的,就是把 SQLPA 这条调优链路的“输入源”从零散的本地配置,改成指向 TaoToken 的统一 Key/API 通道。TaoToken 是一个面向开发者的模型调用聚合入口,你可以把它理解成一个统一的 API 网关:Base URL 固定,Key 统一管理,模型 ID 按需切换。对于 Oracle SQL 调优这种需要反复调用模型做 SQL 解析、计划对比、改写建议的场景,统一通道能省掉大量重复配置。
适合谁看?如果你正在用 STS + SQLPA 做 Oracle SQL 调优,并且希望把调优过程中的 SQL 文本分析、执行计划解读交给模型来处理,同时不想在每个脚本里硬编码不同的 Key 和地址,那这篇就是写给你的。下面我会从 STS 采集开始,一步步把输入源切到 TaoToken,并给出可复制的配置片段和一次 SQLPA 解析结果的验证动作。
核心检索词先摆出来:Oracle STS SQLPA 输入源配置、SQL 调优统一 API 通道、TaoToken 接入 Oracle 调优链路。这三个词贯穿全文,你照着做就能跑通。
2. TaoToken 前置准备:Key、Base URL 与模型 ID
在动 Oracle 之前,先把 TaoToken 这边的三件套准备好。所谓三件套,就是 Base URL、API Key、Model ID。这三样在后续所有配置里都会出现,缺一不可。
Base URL 固定为https://taotoken.net/api,注意这里不加任何 UTM 参数,就是纯 API 地址。API Key 需要你登录 TaoToken 控制台创建,创建入口在控制台的 API Keys 页面。Model ID 则根据你要用的模型来填,比如做 SQL 文本理解和改写建议,可以选一个擅长代码和结构化文本的模型。
我试过把 Key 直接写进 shell 脚本,结果换机器就失效,后来改成环境变量就稳了。你可以这样操作:
export TAOTOKEN_BASE_URL="https://taotoken.net/api" export TAOTOKEN_API_KEY="sk-你的实际Key" export TAOTOKEN_MODEL_ID="你的模型ID"把这三行写进~/.bashrc或~/.zshrc,然后source一下。这样后续无论是 Python 脚本还是 curl 命令,都能直接引用,不用每次手输。
如果你更习惯用配置文件,也可以写一个taotoken.env:
TAOTOKEN_BASE_URL=https://taotoken.net/api TAOTOKEN_API_KEY=sk-你的实际Key TAOTOKEN_MODEL_ID=你的模型ID然后在脚本里source taotoken.env即可。注意不要把 Key 提交到 Git 仓库,建议加进.gitignore。
这里要提醒一句:TaoToken 是统一的模型调用通道,不是数据库连接工具,也不是 Oracle 客户端。它负责的是把 SQL 文本、执行计划这些内容送给模型做分析,然后把结果拿回来。Oracle 那边的 STS 采集、SQLPA 任务执行,还是靠数据库自身的包来完成。两者是配合关系,不是替代关系。
准备好三件套之后,先做一次最简单的连通性验证,确认 Key 和地址没问题:
curl -s -X POST "$TAOTOKEN_BASE_URL/v1/chat/completions" \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "'"$TAOTOKEN_MODEL_ID"'", "messages": [{"role": "user", "content": "回复 OK"}], "max_tokens": 10 }'如果返回里有choices字段,说明通道通了。这一步很关键,因为后面 SQLPA 解析结果要送进模型时,如果通道不通,你会以为是 Oracle 的问题,其实是 Key 或地址配错了。
3. 可复制配置:把 SQLPA 输入源指向 TaoToken 通道
这一节是全文的核心。我要把 STS 采集到的 SQL 语句,通过 SQLPA 任务分析后,稳定送入 TaoToken 通道做进一步解析。整个链路分三段:STS 采集、SQLPA 分析、结果送入 TaoToken。
先看 STS 采集。在 scott 用户下执行,临时授予 DBA 角色:
-- 创建测试表并收集统计信息 drop table scott.zzt_sts_t; create table scott.zzt_sts_t as select * from scott.emp; exec dbms_stats.gather_table_stats( ownname => 'SCOTT', tabname => 'ZZT_STS_T', method_opt => 'for all columns', estimate_percent => '100', degree => '2', granularity => 'all', cascade => TRUE ); -- 创建 STS BEGIN DBMS_SQLTUNE.CREATE_SQLSET( sqlset_name => 'ZZT_SQL_TUNING_SET_OLD', sqlset_owner => 'SCOTT', description => 'test' ); END; / -- 从 Cursor Cache 加载 SQL 到 STS DECLARE zzt_cur_sqlarea DBMS_SQLTUNE.SQLSET_CURSOR; BEGIN OPEN zzt_cur_sqlarea FOR SELECT VALUE(p) FROM TABLE(DBMS_SQLTUNE.SELECT_CURSOR_CACHE( basic_filter => 'parsing_schema_name = ''SCOTT''', attribute_list => 'all')) p; DBMS_SQLTUNE.LOAD_SQLSET( sqlset_name => 'ZZT_SQL_TUNING_SET_OLD', populate_cursor => zzt_cur_sqlarea, sqlset_owner => 'SCOTT', load_option => 'INSERT', update_option => 'REPLACE', update_condition => 'new.executions >= old.executions', update_attributes => 'ALL', ignore_null => TRUE, commit_rows => NULL ); END; /采集完成后,用SELECT_SQLSET确认 STS 里有数据:
SELECT sql_id, sql_text, parsing_schema_name, elapsed_time FROM TABLE(DBMS_SQLTUNE.SELECT_SQLSET( sqlset_name => 'ZZT_SQL_TUNING_SET_OLD', sqlset_owner => 'SCOTT'));接下来创建 SQLPA 分析任务,输入源就是上面这个 STS:
var v_out char(50) begin :v_out := dbms_sqlpa.create_analysis_task( sqlset_name => 'ZZT_SQL_TUNING_SET_OLD', basic_filter => null, order_by => null, top_sql => 9, task_name => 'ZZT_SPA_TASK', description => 'zzt test', sqlset_owner => 'SCOTT' ); end; / print v_out运行分析任务,先跑变更前:
begin DBMS_SQLPA.EXECUTE_ANALYSIS_TASK( task_name => 'ZZT_SPA_TASK', execution_type => 'TEST EXECUTE', execution_name => 'test_spa_task_before', execution_params => dbms_advisor.arglist('time_limit', 3600), execution_desc => 'zzt test' ); end; /然后做数据库变更,比如加主键、收集统计信息,再跑变更后:
begin DBMS_SQLPA.EXECUTE_ANALYSIS_TASK( task_name => 'ZZT_SPA_TASK', execution_type => 'TEST EXECUTE', execution_name => 'test_spa_task_after', execution_params => dbms_advisor.arglist('time_limit', 3600), execution_desc => 'zzt test' ); end; /最后做对比分析:
begin DBMS_SQLPA.EXECUTE_ANALYSIS_TASK( task_name => 'ZZT_SPA_TASK', execution_type => 'COMPARE', execution_name => 'test_spa_task_compare', execution_params => dbms_advisor.arglist('comparison_metric', 'BUFFER_GETS') ); end; /到这里,SQLPA 的分析结果已经出来了。现在关键一步:把 SQLPA 任务中标记为 IMPROVED 的 SQL 加载到新 STS,然后把这些 SQL 文本和执行计划送入 TaoToken 通道做解析。
-- 创建新 STS BEGIN DBMS_SQLTUNE.CREATE_SQLSET( sqlset_name => 'ZZT_SQL_TUNING_SET_NEW', sqlset_owner => 'SCOTT', description => 'test' ); END; / -- 从 SQLPA 任务加载 IMPROVED 的 SQL DECLARE zzt_cur_sqlarea DBMS_SQLTUNE.SQLSET_CURSOR; BEGIN OPEN zzt_cur_sqlarea FOR SELECT VALUE(p) FROM TABLE(DBMS_SQLTUNE.SELECT_SQLPA_TASK( task_name => 'ZZT_SPA_TASK', task_owner => 'SCOTT', execution_name => 'test_spa_task_compare', level_filter => 'IMPROVED' )) p; DBMS_SQLTUNE.LOAD_SQLSET( sqlset_name => 'ZZT_SQL_TUNING_SET_NEW', populate_cursor => zzt_cur_sqlarea, sqlset_owner => 'SCOTT', load_option => 'INSERT', update_option => 'REPLACE', update_condition => 'new.executions >= old.executions', update_attributes => 'ALL', ignore_null => TRUE, commit_rows => NULL ); END; /现在把新 STS 里的 SQL 文本导出,通过 TaoToken 通道送给模型解析。这里给一个 Python 脚本,读取 STS 查询结果并调用 TaoToken:
import os import requests BASE_URL = os.environ["TAOTOKEN_BASE_URL"] API_KEY = os.environ["TAOTOKEN_API_KEY"] MODEL_ID = os.environ["TAOTOKEN_MODEL_ID"] sql_text = "select count(*) from scott.zzt_sts_t" payload = { "model": MODEL_ID, "messages": [ {"role": "system", "content": "你是 Oracle SQL 调优助手,请解析以下 SQL 并给出执行计划关注点。"}, {"role": "user", "content": sql_text} ], "max_tokens": 500 } resp = requests.post( f"{BASE_URL}/v1/chat/completions", headers={ "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json" }, json=payload, timeout=60 ) print(resp.status_code) print(resp.json()["choices"][0]["message"]["content"])这段脚本就是“输入源指向 TaoToken 统一 Key/API 通道”的具体落地。STS 负责采集,SQLPA 负责对比,TaoToken 负责解析。三者串起来,调优链路就完整了。
如果你用的是 Cline 或 Claude Code 这类工具做辅助,配置里同样填这三件套:Base URL 填https://taotoken.net/api,API Key 填你的 Key,Model ID 填你选的模型。Cline 的 MCP 配置里如果涉及模型调用,也是同样的三件套,不要只填 Key 不填 Base URL,否则会报 local proxy failed。
4. 验证请求:确认 SQLPA 解析结果完整可用
配置写完了,必须验证。验证分两步:先确认 SQLPA 任务本身跑通,再确认 TaoToken 通道返回的解析结果完整。
第一步,查看 SQLPA 任务的执行情况:
select * from DBA_ADVISOR_EXECUTIONS where task_name = 'ZZT_SPA_TASK' order by execution_end;你应该能看到三条记录:test_spa_task_before、test_spa_task_after、test_spa_task_compare。如果只有前两条,说明 COMPARE 没跑成功,检查comparison_metric参数。
第二步,查看新 STS 里的 SQL:
SELECT * FROM TABLE(DBMS_SQLTUNE.SELECT_SQLSET( sqlset_name => 'ZZT_SQL_TUNING_SET_NEW', sqlset_owner => 'SCOTT')) where lower(SQL_TEXT) like 'select count(*) from scott%' order by FORCE_MATCHING_SIGNATURE;如果这里有结果,说明 SQLPA 的 IMPROVED 结果成功加载到了新 STS。如果没结果,可能是level_filter => 'IMPROVED'太严格,可以改成'CHANGED'或'ALL'再试。
第三步,查看 STS 中 SQL 的执行计划:
set serveroutput off select * from table(dbms_xplan.display_sqlset( sqlset_name => 'ZZT_SQL_TUNING_SET_OLD', sql_id => 'brmcv7zkd99vj', plan_hash_value => null, format => 'allstats last', sqlset_owner => 'SCOTT'));第四步,把 SQL 文本送给 TaoToken,确认返回的解析结果里有执行计划关注点、索引建议、改写方向。用上面那段 Python 脚本,把sql_text换成你 STS 里实际的 SQL。返回的choices[0].message.content应该是一段结构化的分析文本,而不是空字符串或报错。
如果返回 401,说明 Key 不对或没带Bearer前缀。如果返回local proxy failed,说明 Base URL 填错了,检查是不是漏了/api或者多加了斜杠。如果返回reading choices相关错误,说明响应结构不对,检查模型 ID 是否正确,以及请求体里messages格式是否符合要求。
验证通过的标准很简单:SQLPA 三条执行记录齐全,新 STS 有 IMPROVED 的 SQL,TaoToken 返回的解析内容非空且包含对 SQL 的具体分析。这三条都满足,输入源切换就算成功了。
5. 本篇常见错排查:401、local proxy failed、reading choices、OAuth
这一节把踩过的坑集中列一下,你遇到报错时直接对照。
401 Unauthorized。最常见的原因是 API Key 没带对。检查Authorization头是不是Bearer sk-xxx格式,注意 Bearer 和 Key 之间有一个空格。另外确认 Key 没有过期,也没有被误删。如果你用的是环境变量,echo $TAOTOKEN_API_KEY看一下是不是空值。
local proxy failed。这个报错通常出现在 Cline、Claude Code 这类工具的配置里,原因是 Base URL 填成了本地代理地址,或者填了不完整的地址。正确做法是填https://taotoken.net/api,不要填http://localhost:xxxx,也不要填带 UTM 参数的地址。如果你在 Cline 的 MCP 配置里看到这个错,检查baseUrl字段。
reading choices 相关错误。这个报错说明请求发出去了,但响应结构不符合预期。常见原因是模型 ID 填错,或者请求体里缺少messages字段。检查你的 JSON 里model和messages是否都存在,messages是否是数组,每个元素是否有role和content。
OAuth 相关报错。如果你用的是 Codex 的auth.json,注意 TaoToken 走的是 API Key 认证,不是 OAuth。auth.json里应该填 API Key,而不是 OAuth token。如果你在 Claude Code 里配置,同样用 API Key,Base URL 填https://taotoken.net/api,Model ID 填你选的模型。三件套缺一不可。
还有一个容易忽略的点:Oracle 18C 之后,STS 系统包从DBMS_SQLTUNE变成了DBMS_SQLSET。如果你在 18C 及以上版本执行DBMS_SQLTUNE.CREATE_SQLSET报错,换成DBMS_SQLSET.CREATE_SQLSET即可。但SELECT_SQLPA_TASK和SELECT_SQLSET在部分版本里仍然在DBMS_SQLTUNE下,具体以你数据库版本的官方文档为准。
另外,STS 采集时如果SELECT_CURSOR_CACHE返回空,多执行几次select count(*) from scott.zzt_sts_t,让 SQL 进入 Cursor Cache。STS 抓不到 SQL 是新手最常见的问题,不是配置错了,是缓存里还没有。
6. 把调优链路固定下来:长期编码与 Agent 场景的 CTA
链路跑通之后,建议把配置固定下来。环境变量写进 shell 配置文件,Python 脚本封装成函数,Oracle 那边的 STS 和 SQLPA 任务名统一命名规范。这样下次做调优,直接复用,不用重新配。
如果你需要长期做 Oracle SQL 调优,或者想把 SQL 解析、执行计划解读做成 Agent 自动跑,可以看看 TaoToken 的 Coding Plan,适合持续性的编码和 Agent 场景。如果你只是想先验证模型对 SQL 的解析效果,可以直接用模型对话页面试几条 SQL。如果你在排障阶段,需要确认 Key 和地址,API Keys 页面和接入文档是最直接的入口。
调优这件事,工具链顺了,剩下的就是耐心看执行计划和统计信息。STS 采集、SQLPA 对比、TaoToken 解析,三步走稳,SQL 调优的输入源就不再是散落的脚本,而是一条可复用的通道。