ARTICLE DETAIL

资讯详情

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

Oracle 分区(Partitions)与存储过程(Procedure)实战:用 TaoToken 统一 Key 打通 SQL 调试链路

Oracle 分区(Partitions)与存储过程(Procedure)实战:用 TaoToken 统一 Key 打通 SQL 调试链路 1. 分区裁剪失效的现场一条 SQL 为什么扫了全表先说一个我最近遇到的真实场景。某张按天分区的设备状态表DWT_EQP_STATE_A数据量大概 4 亿行分区名从PART_240101一直排到PART_240630。业务方反馈明明只查 3 月 15 日一天的数据SQL 却跑了 40 多分钟。把执行计划拉出来一看PARTITION RANGE ALL也就是所有分区全扫了一遍。这就是典型的分区裁剪失效。Oracle 分区表的核心价值在于「只读需要的分区」一旦裁剪失效分区表就退化成一张普通大表甚至因为分区元数据开销比普通表还慢。分区裁剪失效的常见原因有这么几类第一类是谓词写法不匹配分区键。比如分区键是STAT_DATEDATE 类型你写WHERE TO_CHAR(STAT_DATE,yyyymmdd) 20240315函数包在分区键外面优化器无法做静态裁剪只能全分区扫描。正确写法是WHERE STAT_DATE DATE 2024-03-15 AND STAT_DATE DATE 2024-03-16。第二类是绑定变量窥探导致的执行计划漂移。存储过程里用动态 SQL 拼EXECUTE IMMEDIATE如果拼出来的 SQL 带绑定变量第一次硬解析时窥探到的值可能让优化器选了全分区计划后续复用这个坏计划。第三类是分区键类型隐式转换。分区键是VARCHAR2你传数字进去Oracle 做隐式转换裁剪同样失效。第四类是统计信息过期。分区表的分区级统计信息如果长期没收集优化器对分区边界判断失准也可能放弃裁剪。这个场景下日常维护要做三件事一是定期巡检哪些 SQL 发生了全分区扫描二是用存储过程批量维护分区加分区、删历史分区、重命名三是把慢 SQL 的执行计划对比出来。而这三件事里写 Procedure 调试动态 SQL 是最费时间的——因为动态 SQL 是字符串拼出来的报错信息往往只告诉你「ORA-00933: SQL 命令未正确结束」具体拼错在哪一行得自己猜。我试过把数据库客户端的 AI 辅助能力接进来让模型帮我读 Procedure 源码、定位拼接错误、生成执行计划对比脚本效率提升很明显。下面就把整条链路拆开讲先讲 TaoToken 的前置准备再给可复制的分区维护 Procedure 模板然后是执行计划对比脚本最后是常见报错排查。2. TaoToken 前置统一 Key 与 Base URL 的配置方式TaoToken 是一个大模型 API 的统一接入层官网地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 端点是 https://taotoken.net/api 。它的作用是你不需要分别去申请多家模型的 Key也不用在数据库客户端、IDE、命令行工具里各配一套地址统一用 TaoToken 的 Base URL 和一把 Key 就能调通。对 Oracle DBA 来说这个统一层的价值在于你日常用的数据库客户端比如 DBeaver、Navicat、DataGrip如果支持 AI 辅助或者你用的命令行工具比如 Claude Code、Cline需要接模型都可以把 Base URL 指向 TaoTokenKey 用同一把。这样你在调试 Procedure 的时候不管是在 IDE 里问模型还是在终端里让 Agent 帮你改脚本走的是同一条链路不用来回切换配置。前置准备分三步。第一步拿到 API Key。访问 https://taotoken.net/api-keys 登录后创建一个 Key。建议按用途分 Key比如「oracle-debug」一把、「coding-agent」一把方便后续排查是哪个客户端出的问题。Key 的格式通常是一串以sk-开头的字符串复制下来存好后面配置要用。第二步确认 Base URL。TaoToken 的 API 根地址是https://taotoken.net/api注意这里不要加 UTM 参数UTM 只用于官网跳转统计。不同客户端对 Base URL 的写法要求不一样有的要求写到/api为止有的要求写到/api/v1这个后面在具体配置里会说明。第三步选模型 ID。TaoToken 支持多家模型你在配置里需要填一个 Model ID。常见的比如claude-sonnet-4-5、gpt-4o这类。具体支持哪些模型可以在模型对话页面 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 里看页面上会列出当前可用的模型和对应的 ID。这里要强调一个概念Base URL Key Model ID 是三件套缺一不可。很多接入失败的情况不是 Key 错了而是 Base URL 写成了官网地址https://taotoken.net/而不是 API 地址https://taotoken.net/api或者 Model ID 填了一个不存在的名字。后面第五节会专门对照真实报错讲这个。如果你是要长期做编码和 Agent 任务比如让模型持续帮你维护 Procedure 脚本、跑分区巡检可以考虑 Coding Plan地址是 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 。它适合那种需要反复调用、上下文比较长的场景比按次调用更划算。配置完成后建议先做一次连通性验证确认 Key 和 Base URL 是通的再去接数据库客户端。验证方法在第四节给。3. 可复制的分区维护 Procedure 模板与配置片段这一节给三块内容一是分区维护的 Procedure 模板加分区、删历史分区、重命名二是执行计划对比脚本三是把客户端 Base URL 改到 TaoToken 的配置片段。3.1 按月自动加分区的 Procedure先建一张配置表记录哪些表需要自动加分区、分区类型、表空间CREATE TABLE ADD_TABLE_PARTITION_CONF ( TABLE_NAME VARCHAR2(100), PARTITION_TYPE VARCHAR2(1), -- M 按月, D 按天 TABLESPACE_NAME VARCHAR2(50), START_DATE VARCHAR2(20), END_DATE VARCHAR2(20) ); INSERT INTO ADD_TABLE_PARTITION_CONF VALUES (DWT_EQP_STATE_A,M,TBS_DATA,202401,202412); COMMIT;然后是按月加分区的 Procedure逻辑是读配置表里PARTITION_TYPEM的表取当前最大分区的高值往后补到ADD_MONTHS(SYSDATE,5)CREATE OR REPLACE PROCEDURE PRO_ADD_PARTITION_BY_MONTH AS V_START_DATE VARCHAR2(30); V_END_DATE VARCHAR2(30); V_TABLE_NAME VARCHAR2(100); V_PARTITION_NAME VARCHAR2(30); V_PARTITION_EXIST VARCHAR2(30); V_EXEC_SQL VARCHAR2(400); CURSOR CUR_STR IS SELECT TABLE_NAME, START_DATE, END_DATE, TABLESPACE_NAME FROM ADD_TABLE_PARTITION_CONF WHERE PARTITION_TYPE M; BEGIN FOR CUR_RESULT IN CUR_STR LOOP V_START_DATE : TO_CHAR(SYSDATE,yyyymm)||01080000; V_END_DATE : TO_CHAR(ADD_MONTHS(SYSDATE,5),yyyymm)||01080000; V_TABLE_NAME : UPPER(CUR_RESULT.TABLE_NAME); SELECT NVL(TO_CHAR(MAX(HIGH_VALUE_IN_DATE_FORMAT),yyyymmddhh24miss), V_END_DATE) INTO V_PARTITION_EXIST FROM ( SELECT TABLE_NAME, PARTITION_NAME, TO_DATE(TRIM( FROM REGEXP_SUBSTR( EXTRACTVALUE(DBMS_XMLGEN.GETXMLTYPE( select high_value from user_tab_partitions where table_name||TABLE_NAME|| and partition_name ||PARTITION_NAME||), //text()), .*?)), syyyy-mm-dd hh24:mi:ss) HIGH_VALUE_IN_DATE_FORMAT FROM USER_TAB_PARTITIONS WHERE TABLE_NAME V_TABLE_NAME ) WHERE HIGH_VALUE_IN_DATE_FORMAT TO_DATE(V_START_DATE,yyyymmdd hh24:mi:ss); WHILE V_START_DATE V_END_DATE LOOP IF V_PARTITION_EXIST V_START_DATE THEN V_PARTITION_NAME : PART_||TO_CHAR(ADD_MONTHS(TO_DATE(V_START_DATE,yyyymmdd hh24:mi:ss),1),yyyymm); V_EXEC_SQL : ALTER TABLE ||V_TABLE_NAME|| ADD PARTITION ||V_PARTITION_NAME|| VALUES LESS THAN(TO_DATE(||V_START_DATE||,yyyymmddhh24miss)) TABLESPACE ||CUR_RESULT.TABLESPACE_NAME; EXECUTE IMMEDIATE V_EXEC_SQL; END IF; V_START_DATE : TO_CHAR(ADD_MONTHS(TO_DATE(V_START_DATE,yyyymmdd hh24:mi:ss),1),yyyymmddhh24miss); END LOOP; END LOOP; END PRO_ADD_PARTITION_BY_MONTH; /这段代码的关键点HIGH_VALUE是 Oracle 内部存储的字符串需要用DBMS_XMLGEN.GETXMLTYPE配合正则把它转成日期才能和V_START_DATE比较。如果你直接拿HIGH_VALUE字符串比大小会得到错误结果。3.2 删除历史分区的 Procedure保留最近 N 个分区超出的先TRUNCATE PARTITION ... DROP STORAGE回收空间再DROP PARTITIONCREATE OR REPLACE PROCEDURE PRO_DROP_TABLE_PARTITION( TAB_NAME IN VARCHAR2, INPUT_NUM IN NUMBER : 30 ) AS V_TRUNC_SQL VARCHAR2(200); V_DROP_SQL VARCHAR2(200); CURSOR CUR_RESULTS IS SELECT TABLE_NAME, PARTITION_NAME FROM (SELECT TABLE_NAME, PARTITION_NAME, ROW_NUMBER() OVER(PARTITION BY TABLE_NAME ORDER BY PARTITION_POSITION DESC) RK FROM USER_TAB_PARTITIONS WHERE TABLE_NAME TAB_NAME AND PARTITION_POSITION 1) WHERE RK INPUT_NUM; BEGIN FOR CUR_ROW IN CUR_RESULTS LOOP V_TRUNC_SQL : ALTER TABLE ||CUR_ROW.TABLE_NAME|| TRUNCATE PARTITION ||CUR_ROW.PARTITION_NAME|| DROP STORAGE; V_DROP_SQL : ALTER TABLE ||CUR_ROW.TABLE_NAME|| DROP PARTITION ||CUR_ROW.PARTITION_NAME; EXECUTE IMMEDIATE V_TRUNC_SQL; EXECUTE IMMEDIATE V_DROP_SQL; END LOOP; END PRO_DROP_TABLE_PARTITION; /注意PARTITION_POSITION 1是为了跳过 MAXVALUE 分区如果有的话避免误删。3.3 执行计划对比脚本分区裁剪是否生效靠执行计划说话。下面这个脚本把同一条 SQL 在「函数包分区键」和「范围谓词」两种写法下的计划都拉出来对比-- 写法一函数包分区键裁剪失效 EXPLAIN PLAN FOR SELECT COUNT(*) FROM DWT_EQP_STATE_A WHERE TO_CHAR(STAT_DATE,yyyymmdd) 20240315; SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY); -- 写法二范围谓词裁剪生效 EXPLAIN PLAN FOR SELECT COUNT(*) FROM DWT_EQP_STATE_A WHERE STAT_DATE DATE 2024-03-15 AND STAT_DATE DATE 2024-03-16; SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);看Pstart和Pstop两列如果显示的是具体分区号比如Pstart15, Pstop15说明裁剪生效如果显示Pstart1, Pstop180或者KEY说明全分区扫描。3.4 客户端 Base URL 配置片段如果你用的是支持 OpenAI 兼容接口的客户端配置通常是一个 JSON 或 TOML。以常见的settings.json为例{ ai.provider: openai-compatible, ai.baseUrl: https://taotoken.net/api, ai.apiKey: sk-你的TaoTokenKey, ai.model: claude-sonnet-4-5 }如果你用的是 Cline 这类带 MCP 的工具配置里同样要写全三件套{ mcpServers: { taotoken: { baseUrl: https://taotoken.net/api, apiKey: sk-你的TaoTokenKey, model: claude-sonnet-4-5 } } }如果你用的是 Codex 的auth.json格式类似{ base_url: https://taotoken.net/api, api_key: sk-你的TaoTokenKey, model: claude-sonnet-4-5 }三件套里Base URL 写https://taotoken.net/apiKey 写你创建的那把Model ID 写模型对话页面里列出的名字。三个都对才能通。4. 验证请求与成功结果从连通性到分区巡检跑通配置写完先别急着接数据库先做一次纯 API 连通性验证。用 curl 发一个最小请求curl -X POST https://taotoken.net/api/v1/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer sk-你的TaoTokenKey \ -d { model: claude-sonnet-4-5, messages: [{role:user,content:回复 OK 两个字母即可}], max_tokens: 10 }如果返回的 JSON 里有choices字段且message.content是OK说明 Key、Base URL、Model ID 三件套是通的。如果返回 401说明 Key 有问题如果返回 404说明 Base URL 路径写错了如果返回model not found说明 Model ID 不对。连通性验证通过后把数据库客户端接进来。以 DBeaver 为例在 AI 设置里把 Provider 选成 OpenAI 兼容Base URL 填https://taotoken.net/apiKey 填 TaoToken 的 KeyModel 填claude-sonnet-4-5。保存后在 SQL 编辑器里选中一段 Procedure 代码右键让 AI 解释如果能正常返回说明客户端接入成功。接下来跑分区巡检。先执行加分区 ProcedureBEGIN PRO_ADD_PARTITION_BY_MONTH; END; /执行完查一下分区情况SELECT TABLE_NAME, PARTITION_NAME, PARTITION_POSITION, TO_CHAR(HIGH_VALUE) AS HIGH_VALUE FROM USER_TAB_PARTITIONS WHERE TABLE_NAME DWT_EQP_STATE_A ORDER BY PARTITION_POSITION;如果看到PART_202407、PART_202408这些新分区被加出来了说明加分区逻辑跑通。然后跑执行计划对比确认裁剪生效。把 3.3 的两段EXPLAIN PLAN分别执行对比Pstart和Pstop。如果范围谓词那条显示Pstart15, Pstop15说明裁剪正常。最后跑慢 SQL 定位。用下面这条查最近执行过的、发生全分区扫描的 SQLSELECT SQL_ID, SQL_TEXT, PARSING_SCHEMA_NAME, ELAPSED_TIME/1000000 AS ELAPSED_SEC FROM V$SQL WHERE SQL_TEXT LIKE %DWT_EQP_STATE_A% AND ELAPSED_TIME/1000000 60 ORDER BY ELAPSED_TIME DESC;拿到 SQL_ID 后用DBMS_XPLAN.DISPLAY_CURSOR看实际执行计划SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(你的SQL_ID, NULL, ALLSTATS LAST));如果Pstart/Pstop显示全分区就把这条 SQL 的写法改掉或者检查是不是绑定变量窥探导致的计划漂移。到这里整条链路就跑通了TaoToken 连通 → 客户端接入 → 分区巡检 → 执行计划对比 → 慢 SQL 定位。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth这一节对照真实报错逐个说原因和解法。报错一401 Unauthorized。返回体通常是{error:{message:Invalid API key,type:invalid_request_error}}。原因有三种Key 复制时带了空格或换行Key 已经失效或被删除Key 前面的Bearer前缀漏了。解法重新去 https://taotoken.net/api-keys 复制一次 Key确认 curl 命令里Authorization: Bearer sk-xxx格式完整中间只有一个空格。报错二local proxy failed。这个报错通常出现在客户端里意思是客户端尝试走本地代理但失败了。原因可能是客户端配置了系统代理而系统代理指向了一个不可用的地址。解法在客户端设置里关掉「使用系统代理」或者把代理模式改成「直连」。注意这里说的是客户端自身的代理设置不是让你去配什么网络工具只是把客户端里那个多余的代理开关关掉。报错三reading choices 相关报错。比如Cannot read properties of undefined (reading choices)。这是客户端在解析 API 返回时没找到choices字段。原因通常是 Base URL 写错了请求打到了官网首页而不是 API 端点返回的是 HTML 而不是 JSON。解法确认 Base URL 是https://taotoken.net/api不是https://taotoken.net/。如果客户端要求写到/v1就写https://taotoken.net/api/v1。报错四OAuth 相关报错。比如OAuth token exchange failed。这个报错一般出现在 Claude Code 这类工具里原因是工具默认走 OAuth 登录流程而你用的是 API Key 模式。解法在工具的配置里把认证方式从 OAuth 改成 API Key填入 TaoToken 的 Key 和 Base URL。如果工具同时支持两种模式确认当前选中的是 API Key 模式。报错五ORA-00933 SQL 命令未正确结束。这是 Oracle 侧的报错出现在动态 SQL 拼接时。原因通常是拼接出来的 SQL 字符串里少了空格或者多了引号。解法在EXECUTE IMMEDIATE之前先把V_EXEC_SQL打印出来看DBMS_OUTPUT.PUT_LINE(V_EXEC_SQL);把打印出来的 SQL 复制到 SQL 窗口里单独执行报错位置一目了然。这也是为什么前面模板里我把INSERT INTO TMP1那行注释掉了——调试阶段用DBMS_OUTPUT更直接。报错六ORA-14074 分区边界必须递增。出现在加分区时新分区的高值小于等于已有分区的高值。原因通常是HIGH_VALUE解析出错导致V_PARTITION_EXIST判断失准。解法检查DBMS_XMLGEN.GETXMLTYPE那段正则确认HIGH_VALUE被正确转成了日期。可以单独跑一下那段子查询看HIGH_VALUE_IN_DATE_FORMAT列的值对不对。报错七model not found。出现在 API 调用时说明 Model ID 填错了。解法去 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 看当前可用的模型 ID复制准确的名称填进去。注意大小写和连字符claude-sonnet-4-5和claude-sonnet-4.5是不一样的。排查顺序建议先确认三件套Base URL Key Model ID都对再看客户端自身的代理和认证模式最后看 Oracle 侧的 SQL 拼接。大部分接入问题都出在前两步。6. 把调试链路固定下来从分区巡检到慢 SQL 定位整条链路跑通之后建议把它固化成日常动作。我的做法是每天凌晨跑一次PRO_ADD_PARTITION_BY_MONTH保证未来 5 个月的分区都在每周跑一次PRO_DROP_TABLE_PARTITION把超过 30 个的历史分区清掉每次发版前跑一次执行计划对比脚本确认核心 SQL 的Pstart/Pstop是收敛的。慢 SQL 定位这块可以把V$SQL查询和DBMS_XPLAN.DISPLAY_CURSOR包成一个巡检 Procedure每天把全分区扫描的 SQL 落一张表第二天早上看报表。这样不用等到业务方反馈才发现问题。至于 TaoToken 在其中的角色它解决的是「调试动态 SQL 时没人帮你读代码」的问题。Procedure 里的动态 SQL 是字符串拼的报错信息不直观把源码贴给模型让它帮你找拼接错误、生成对比脚本比自己在DBMS_OUTPUT里一行行打印快得多。配置上记住三件套Base URL 用https://taotoken.net/apiKey 用 https://taotoken.net/api-keys 创建的Model ID 用模型对话页面里列出的。三个都对链路就通。最后留一个实用技巧在 Procedure 里调试动态 SQL 时把EXECUTE IMMEDIATE换成先DBMS_OUTPUT.PUT_LINE再执行打印出来的 SQL 直接复制到客户端里跑报错位置比在 Procedure 里清晰十倍。这个习惯能帮你省下大量猜错的时间。
返回列表