news 2026/10/4 18:11:45

在Oracle存储过程中实现分页:TaoToken辅助排查分页SQL性能问题

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
在Oracle存储过程中实现分页:TaoToken辅助排查分页SQL性能问题

1. Oracle 存储过程分页到底难在哪:从 ROWNUM 到 OFFSET FETCH 的慢 SQL 现场

Oracle 存储过程分页,说白了就是「在数据库里把一张大表按页切出来,只返回当前页需要的几十条」。听起来简单,真正落到生产环境,问题往往出在三件事上:写法选错、排序字段没走索引、以及分页 SQL 拼错之后报错信息看不懂。这篇内容适合正在写 Oracle 存储过程、被分页查询拖慢响应、或者想搞清楚 ROWNUM、ROW_NUMBER、OFFSET FETCH 三种写法差异的后端和 DBA 同学。

我先把三种主流写法摆出来,你心里有个谱:

ROWNUM 双层嵌套是最经典的写法,兼容性最好,Oracle 8i 以上都能跑。它的逻辑是先用子查询把数据查出来并排序,外层用ROWNUM <= endRecord截断,再套一层用r >= startRecord取区间。注意这里有个坑:ROWNUM 是在结果集生成过程中逐行分配的,所以必须嵌套两层,否则ROWNUM > 1永远为空。

ROW_NUMBER() 分析函数写法更直观,ROW_NUMBER() OVER (ORDER BY col) AS rn先给每行编号,外层再按 rn 过滤。可读性好,但排序开销一样跑不掉,而且如果 ORDER BY 字段没有索引,全表排序照样慢。

OFFSET FETCH 是 Oracle 12c 之后才有的语法,OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY,写起来最像 MySQL 的 LIMIT。但要注意,它在深分页场景下并不会自动变快,本质还是扫描并丢弃前 N 行。

真正让分页变慢的,通常不是分页语法本身,而是「排序字段无索引 + 深分页 + 回表」。比如一张 500 万行的订单表,按create_time倒序取第 10000 页,每页 20 条,数据库需要先排序 500 万行,再丢弃前 20 万行,最后返回 20 条。这个代价是实打实的。

我在实际排查时,习惯先把存储过程里拼出来的 SQL 单独拿出来,用EXPLAIN PLAN看执行计划,确认是全表扫描还是索引范围扫描。但问题在于,很多团队的存储过程是动态拼 SQL,参数一多,你根本不知道线上那次慢查询到底拼成了什么样。这时候就需要一个统一的通道,把「调用存储过程」和「观察返回结果/报错」这两件事串起来,TaoToken 在这里的作用就是提供统一的 Key 和 API 通道,让你在验证分页结果一致性、复现报错时不用来回切换工具和账号。

下面我会先给一套可复制的分页存储过程模板,再讲怎么用执行计划对比三种写法,最后讲怎么通过 TaoToken 的接口通道去验证分页结果和定位报错。每一步都有完整命令和参数,你可以直接跟做。

2. TaoToken 前置准备:统一 Key 与 API 通道,为分页排查铺路

在开始写存储过程之前,先把 TaoToken 这条通道准备好。它的定位不是替代你的数据库客户端,而是给你一个统一的入口:当你需要调用模型对话来帮你分析执行计划、或者用 Coding Plan 跑一段脚本去批量验证分页结果时,不用每个工具单独配一套 Key。

你需要准备三样东西:Base URL、API Key、Model ID。这三件套在后面的配置片段里会反复出现,先记牢。

Base URL 用https://taotoken.net/api,注意这个地址不带任何查询参数。API Key 需要你去控制台创建,入口在https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite。创建的时候建议按用途命名,比如oracle-page-debug,方便后面区分。

Model ID 根据你的场景选。如果你只是想让模型帮你读执行计划、解释报错,用模型对话通道就行,入口在https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite。如果你要长期跑分页验证脚本、做 Agent 类的自动化排查,那更适合 Coding Plan,入口在https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite。

如果你用的是 Claude Code 这类工具,需要走 Anthropic 兼容通道,配置入口在https://taotoken.net/claude-code-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claude_code&utm_campaign=rewrite。接入文档在https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite,里面有各语言的调用示例。

这里给一个通用的配置片段,你可以直接复制到你的环境变量或者配置文件里。以 JSON 格式为例:

{ "base_url": "https://taotoken.net/api", "api_key": "sk-你的Key", "model_id": "你的模型ID", "timeout": 60 }

如果你用的是 Codex 的auth.json,结构类似,把base_url和api_key填进去即可。注意base_url后面不要加/v1之类的后缀,保持https://taotoken.net/api原样。

为什么要先做这一步?因为分页排查的痛点在于「复现」。线上报了一个ORA-00933: SQL command not properly ended,你光看日志不知道拼出来的 SQL 长什么样。这时候你可以把存储过程的入参记录下来,通过 TaoToken 的通道调用模型,让它根据你的模板和参数反推可能的 SQL 拼接结果,再对照执行计划。这比你在 PL/SQL Developer 里手动拼参数快得多。

另外,验证分页结果一致性时,你可能需要对比「存储过程返回的第 N 页」和「直接查 SQL 的第 N 页」是否一致。这个对比脚本可以用 Coding Plan 跑,把两次结果做 diff。统一 Key 的好处是,你不用在多个工具之间复制粘贴 Key,也不会因为某个工具的额度用完而中断排查。

准备好这三件套之后,我们进入正题:写分页存储过程。

3. 可复制的分页存储过程模板:ROWNUM、ROW_NUMBER、OFFSET FETCH 三版对照

这一节给你三版存储过程模板,都是可以直接复制执行的。我会把包定义、存储过程主体、以及调用方式写全。你先建包,再建过程,最后测试。

3.1 包定义

三版共用同一个包,定义游标类型:

CREATE OR REPLACE PACKAGE page_query AS TYPE cur_query IS REF CURSOR; END page_query; /

3.2 ROWNUM 双层嵌套版

这是兼容性最好的写法,适合 Oracle 11g 及以下:

CREATE OR REPLACE PROCEDURE pro_query_rownum ( p_tableName IN VARCHAR2, p_strWhere IN VARCHAR2, p_orderColumn IN VARCHAR2, p_orderStyle IN VARCHAR2, p_curPage IN OUT NUMBER, p_pageSize IN OUT NUMBER, p_totalRecords OUT NUMBER, p_totalPages OUT NUMBER, v_cur OUT page_query.cur_query ) IS v_sql VARCHAR2(4000) := ''; v_countSql VARCHAR2(4000) := ''; v_startRecord NUMBER; v_endRecord NUMBER; BEGIN -- 总记录数 v_countSql := 'SELECT COUNT(*) FROM ' || p_tableName || ' WHERE 1=1 '; IF p_strWhere IS NOT NULL AND p_strWhere <> '' THEN v_countSql := v_countSql || p_strWhere; END IF; EXECUTE IMMEDIATE v_countSql INTO p_totalRecords; -- 页大小校验 IF p_pageSize IS NULL OR p_pageSize <= 0 THEN p_pageSize := 20; END IF; -- 总页数 p_totalPages := CEIL(p_totalRecords / p_pageSize); -- 页码校验 IF p_curPage IS NULL OR p_curPage < 1 THEN p_curPage := 1; END IF; IF p_curPage > p_totalPages THEN p_curPage := p_totalPages; END IF; -- 计算区间 v_startRecord := (p_curPage - 1) * p_pageSize + 1; v_endRecord := p_curPage * p_pageSize; -- 分页 SQL v_sql := 'SELECT * FROM (' || ' SELECT A.*, ROWNUM r FROM (' || ' SELECT * FROM ' || p_tableName || ' WHERE 1=1 '; IF p_strWhere IS NOT NULL AND p_strWhere <> '' THEN v_sql := v_sql || p_strWhere; END IF; IF p_orderColumn IS NOT NULL AND p_orderColumn <> '' THEN v_sql := v_sql || ' ORDER BY ' || p_orderColumn || ' ' || p_orderStyle; END IF; v_sql := v_sql || ' ) A WHERE ROWNUM <= ' || v_endRecord || ') B WHERE r >= ' || v_startRecord; DBMS_OUTPUT.PUT_LINE('SQL: ' || v_sql); OPEN v_cur FOR v_sql; END pro_query_rownum; /

注意几个细节:p_pageSize和p_curPage是IN OUT,过程内部会修正非法值。v_sql用VARCHAR2(4000),如果你的表名和条件特别长,可以调到 32767。DBMS_OUTPUT.PUT_LINE把拼出来的 SQL 打出来,方便你复制到执行计划里分析。

3.3 ROW_NUMBER 分析函数版

适合 Oracle 11g 及以上,可读性更好:

CREATE OR REPLACE PROCEDURE pro_query_rownumber ( p_tableName IN VARCHAR2, p_strWhere IN VARCHAR2, p_orderColumn IN VARCHAR2, p_orderStyle IN VARCHAR2, p_curPage IN OUT NUMBER, p_pageSize IN OUT NUMBER, p_totalRecords OUT NUMBER, p_totalPages OUT NUMBER, v_cur OUT page_query.cur_query ) IS v_sql VARCHAR2(4000) := ''; v_countSql VARCHAR2(4000) := ''; v_startRecord NUMBER; v_endRecord NUMBER; BEGIN v_countSql := 'SELECT COUNT(*) FROM ' || p_tableName || ' WHERE 1=1 '; IF p_strWhere IS NOT NULL AND p_strWhere <> '' THEN v_countSql := v_countSql || p_strWhere; END IF; EXECUTE IMMEDIATE v_countSql INTO p_totalRecords; IF p_pageSize IS NULL OR p_pageSize <= 0 THEN p_pageSize := 20; END IF; p_totalPages := CEIL(p_totalRecords / p_pageSize); IF p_curPage IS NULL OR p_curPage < 1 THEN p_curPage := 1; END IF; IF p_curPage > p_totalPages THEN p_curPage := p_totalPages; END IF; v_startRecord := (p_curPage - 1) * p_pageSize + 1; v_endRecord := p_curPage * p_pageSize; v_sql := 'SELECT * FROM (' || ' SELECT A.*, ROW_NUMBER() OVER (ORDER BY ' || NVL(p_orderColumn, 'NULL') || ' ' || NVL(p_orderStyle, 'ASC') || ') AS rn FROM ' || p_tableName || ' WHERE 1=1 '; IF p_strWhere IS NOT NULL AND p_strWhere <> '' THEN v_sql := v_sql || p_strWhere; END IF; v_sql := v_sql || ') WHERE rn BETWEEN ' || v_startRecord || ' AND ' || v_endRecord; DBMS_OUTPUT.PUT_LINE('SQL: ' || v_sql); OPEN v_cur FOR v_sql; END pro_query_rownumber; /

这里ROW_NUMBER() OVER (ORDER BY ...)必须放在子查询里,外层才能用rn过滤。如果你把rn过滤放在同一层,Oracle 会报ORA-30483: window functions are not allowed here。

3.4 OFFSET FETCH 版

Oracle 12c 及以上:

CREATE OR REPLACE PROCEDURE pro_query_offset ( p_tableName IN VARCHAR2, p_strWhere IN VARCHAR2, p_orderColumn IN VARCHAR2, p_orderStyle IN VARCHAR2, p_curPage IN OUT NUMBER, p_pageSize IN OUT NUMBER, p_totalRecords OUT NUMBER, p_totalPages OUT NUMBER, v_cur OUT page_query.cur_query ) IS v_sql VARCHAR2(4000) := ''; v_countSql VARCHAR2(4000) := ''; v_offset NUMBER; BEGIN v_countSql := 'SELECT COUNT(*) FROM ' || p_tableName || ' WHERE 1=1 '; IF p_strWhere IS NOT NULL AND p_strWhere <> '' THEN v_countSql := v_countSql || p_strWhere; END IF; EXECUTE IMMEDIATE v_countSql INTO p_totalRecords; IF p_pageSize IS NULL OR p_pageSize <= 0 THEN p_pageSize := 20; END IF; p_totalPages := CEIL(p_totalRecords / p_pageSize); IF p_curPage IS NULL OR p_curPage < 1 THEN p_curPage := 1; END IF; IF p_curPage > p_totalPages THEN p_curPage := p_totalPages; END IF; v_offset := (p_curPage - 1) * p_pageSize; v_sql := 'SELECT * FROM ' || p_tableName || ' WHERE 1=1 '; IF p_strWhere IS NOT NULL AND p_strWhere <> '' THEN v_sql := v_sql || p_strWhere; END IF; IF p_orderColumn IS NOT NULL AND p_orderColumn <> '' THEN v_sql := v_sql || ' ORDER BY ' || p_orderColumn || ' ' || p_orderStyle; END IF; v_sql := v_sql || ' OFFSET ' || v_offset || ' ROWS FETCH NEXT ' || p_pageSize || ' ROWS ONLY'; DBMS_OUTPUT.PUT_LINE('SQL: ' || v_sql); OPEN v_cur FOR v_sql; END pro_query_offset; /

OFFSET FETCH 的坑在于:如果ORDER BY字段不唯一,分页结果可能不稳定,同一页两次查询返回不同行。所以排序字段最好带上主键,比如ORDER BY create_time DESC, id DESC。

三版建好之后,你可以用下面的匿名块测试:

SET SERVEROUTPUT ON; DECLARE v_cur page_query.cur_query; v_page NUMBER := 1; v_size NUMBER := 10; v_total NUMBER; v_pages NUMBER; v_id NUMBER; v_name VARCHAR2(100); BEGIN pro_query_rownum('YOUR_TABLE', '', 'ID', 'ASC', v_page, v_size, v_total, v_pages, v_cur); DBMS_OUTPUT.PUT_LINE('total=' || v_total || ', pages=' || v_pages); LOOP FETCH v_cur INTO v_id, v_name; EXIT WHEN v_cur%NOTFOUND; DBMS_OUTPUT.PUT_LINE(v_id || ' - ' || v_name); END LOOP; CLOSE v_cur; END; /

把YOUR_TABLE换成你的表名,列名按实际调整。跑通之后,我们进入执行计划对比。

4. 验证请求与成功结果:执行计划对比 + TaoToken 接口验证分页一致性

这一节分两步:先用EXPLAIN PLAN对比三种写法的执行计划,再用 TaoToken 的接口通道验证分页结果一致性。

4.1 执行计划对比

假设你有一张ORDERS表,CREATE_TIME上有索引,数据量 500 万行。分别对三种写法取第 1000 页,每页 20 条。

ROWNUM 版拼出来的 SQL:

SELECT * FROM ( SELECT A.*, ROWNUM r FROM ( SELECT * FROM ORDERS WHERE 1=1 ORDER BY CREATE_TIME DESC ) A WHERE ROWNUM <= 20000 ) B WHERE r >= 19981;

ROW_NUMBER 版:

SELECT * FROM ( SELECT A.*, ROW_NUMBER() OVER (ORDER BY CREATE_TIME DESC) AS rn FROM ORDERS WHERE 1=1 ) WHERE rn BETWEEN 19981 AND 20000;

OFFSET FETCH 版:

SELECT * FROM ORDERS WHERE 1=1 ORDER BY CREATE_TIME DESC OFFSET 19980 ROWS FETCH NEXT 20 ROWS ONLY;

对每条 SQL 执行:

EXPLAIN PLAN FOR <你的SQL>; SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);

你会看到类似这样的输出(简化):

| Id | Operation | Name | Rows | | 0 | SELECT STATEMENT | | 20 | |* 1 | VIEW | | 20000 | |* 2 | COUNT STOPKEY | | | | 3 | VIEW | | 20000 | | 4 | TABLE ACCESS BY INDEX| ORDERS | 5000K| | 5 | INDEX FULL SCAN | IDX_CREATE | 5000K|

关键看两点:COUNT STOPKEY表示 ROWNUM 截断生效,INDEX FULL SCAN表示走了索引。如果ORDER BY字段没索引,你会看到SORT ORDER BY,代价高很多。

三种写法在深分页时,执行计划差异不大,都是「索引扫描 + 丢弃前 N 行」。真正的优化手段是「延迟关联」:先用索引查出主键,再回表取数据。这个后面排障部分会讲。

4.2 TaoToken 接口验证分页一致性

执行计划只能告诉你「快不快」,不能告诉你「对不对」。分页结果一致性验证,是确认存储过程返回的第 N 页和直接查 SQL 的第 N 页是否完全一致。

你可以写一个 Python 脚本,通过 TaoToken 的接口通道调用模型,让它帮你生成对比逻辑,或者直接用 Coding Plan 跑对比脚本。这里给一个用requests调用的示例:

import requests import json BASE_URL = "https://taotoken.net/api" API_KEY = "sk-你的Key" MODEL_ID = "你的模型ID" headers = { "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json" } payload = { "model": MODEL_ID, "messages": [ { "role": "user", "content": "我有两个分页查询结果集,A 是存储过程返回的,B 是直接 SQL 返回的。请帮我写一段 Python 代码对比两个列表是否完全一致,如果不一致,输出差异行的主键。" } ] } resp = requests.post(f"{BASE_URL}/chat/completions", headers=headers, json=payload, timeout=60) print(resp.json()["choices"][0]["message"]["content"])

如果你用的是 Claude Code 的 Anthropic 兼容通道,请求体格式略有不同,参考接入文档里的示例。核心是三件套:Base URL 用https://taotoken.net/api,Key 用你创建的,Model ID 按场景选。

验证通过的标准是:存储过程返回的 20 条记录,和直接 SQL 返回的 20 条记录,主键集合完全一致,顺序也一致。如果顺序不一致,检查ORDER BY字段是否唯一。如果记录数不一致,检查ROWNUM或rn的边界计算。

成功结果示例:

total=5000000, pages=250000 page 1000, size 20 存储过程返回主键: [19981, 19982, ..., 20000] 直接SQL返回主键: [19981, 19982, ..., 20000] 一致性校验: PASS

到这里,分页存储过程模板、执行计划对比、结果一致性验证就串起来了。接下来讲排障。

5. 本篇常见错排查:401、local proxy failed、reading choices、OAuth 对照

这一节把分页排查和 TaoToken 调用过程中最容易遇到的报错列出来,对照真实错误信息给解法。

5.1 ORA-00933: SQL command not properly ended

这是分页 SQL 拼接最常见的错。原因通常是ORDER BY后面多了分号,或者OFFSET FETCH用在了 11g 上。检查你的v_sql最后有没有多余字符。如果是 11g,改用 ROWNUM 或 ROW_NUMBER 版。

5.2 ORA-00904: invalid identifier

通常是p_orderColumn传了不存在的列名,或者列名带了表别名但子查询里没暴露。比如你传A.CREATE_TIME,但子查询里表别名是ORDERS,就会报这个错。解法是排序字段只传列名,不带别名。

5.3 ORA-30483: window functions are not allowed here

ROW_NUMBER 版把rn过滤写在了同一层。必须嵌套子查询,外层再过滤。

5.4 401 Unauthorized

TaoToken 调用返回 401,说明 Key 不对或没带。检查Authorization头是不是Bearer sk-xxx格式,Key 有没有复制完整。如果 Key 刚创建,等几秒再试。控制台入口在https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite。

5.5 local proxy failed

这个报错通常出现在你本地网络环境有代理设置,但请求没走通。检查你的环境变量HTTP_PROXY、HTTPS_PROXY是否指向了不可用的地址。如果你在容器里跑,检查容器网络是否能访问外网。解法是清掉代理环境变量,或者确认网络策略允许访问taotoken.net。

5.6 reading choices 相关报错

如果你在解析响应时遇到reading 'choices'或choices is undefined,说明返回体不是预期的 OpenAI 格式。先打印完整响应:

print(resp.status_code) print(resp.text)

常见原因是 Model ID 填错,或者请求路径少了/chat/completions。Base URL 是https://taotoken.net/api,完整路径是https://taotoken.net/api/chat/completions。

5.7 OAuth 相关报错

如果你用 Claude Code 走 Anthropic 通道,遇到 OAuth 报错,检查你的配置文件里base_url是不是写成了https://taotoken.net/api,而不是带/v1的地址。Anthropic 兼容通道的配置参考https://taotoken.net/claude-code-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claude_code&utm_campaign=rewrite。

5.8 分页结果重复或丢失

如果同一页两次查询结果不同,检查ORDER BY字段是否唯一。解法是加上主键,比如ORDER BY CREATE_TIME DESC, ID DESC。如果记录丢失,检查ROWNUM的r >= startRecord和r <= endRecord边界,startRecord应该是(page-1)*size+1,不是(page-1)*size。

5.9 深分页慢查询

第 10000 页慢,不是分页写法的问题,是「丢弃前 N 行」的代价。解法是延迟关联:

SELECT * FROM ORDERS O JOIN ( SELECT ID FROM ( SELECT ID, ROWNUM r FROM ( SELECT ID FROM ORDERS WHERE 1=1 ORDER BY CREATE_TIME DESC ) WHERE ROWNUM <= 200000 ) WHERE r >= 199981 ) T ON O.ID = T.ID ORDER BY O.CREATE_TIME DESC;

先用索引查出主键区间,再回表取数据,减少回表次数。

排障的核心思路是:先看报错信息,定位是 SQL 拼接问题还是通道调用问题。SQL 问题看执行计划和拼接日志,通道问题看状态码和响应体。TaoToken 在这里的价值是给你一个统一的调用入口,让你在验证分页结果、复现报错时不用来回切换工具。

6. 长期编码与 Agent 排查:用 Coding Plan 把分页验证自动化

如果你只是偶尔排查一次分页问题,上面的步骤够用了。但如果你在团队里长期维护多个存储过程,每次改完都要手动验证分页一致性,那就值得把这件事自动化。

Coding Plan 适合这种场景:你写一个脚本,输入表名、排序字段、页码范围,自动跑三种分页写法,对比结果,输出差异报告。脚本通过 TaoToken 的通道调用模型,让模型帮你分析执行计划里的瓶颈,或者根据报错信息给出修复建议。

入口在https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite。配置还是那三件套:Base URL 用https://taotoken.net/api,Key 用你创建的,Model ID 选适合代码场景的。

一个实用的自动化流程是这样的:每次存储过程变更后,CI 里跑一个脚本,用EXPLAIN PLAN抓取执行计划,通过 TaoToken 让模型判断是否出现SORT ORDER BY或TABLE ACCESS FULL,如果有就告警。同时跑分页一致性校验,对比存储过程返回和直接 SQL 返回的主键集合。

这样你就不用每次改完都手动去 PL/SQL Developer 里点一遍。模型对话通道适合临时问问题,Coding Plan 适合把重复排查固化成流程。两者配合,分页问题的定位时间能从半小时压缩到几分钟。

最后给一个实用技巧:把DBMS_OUTPUT.PUT_LINE打出来的 SQL 存到一张日志表里,每次分页查询都记录入参和拼出的 SQL。出问题时直接查日志表,不用去猜参数。这张日志表加上 TaoToken 的通道,基本能覆盖分页排查的绝大多数场景。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/4 18:10:41

U-Boot移植实战:从零启动嵌入式开发板的关键流程与排坑指南

去年我拿到一块 iTOP-4412 开发板&#xff0c;第一件事不是跑 Linux&#xff0c;而是先把 U-Boot 调起来。很多刚接触嵌入式的朋友觉得“uboot 移植”是个很高大上的事&#xff0c;其实当你把整个流程走一遍就会发现&#xff0c;U-Boot 移植更像是“配置适配板级适配”&#xf…

作者头像 李华
网站建设 2026/10/4 18:08:41

DEiT实战指南:中小数据集上高效训练图像分类Transformer

简介&#xff1a;DEiT 是由 Facebook 在 2020 年提出的高效图像分类 Transformer 模型&#xff0c;通过知识蒸馏与训练策略改进&#xff0c;消除了 Transformer 难以训练的痛点&#xff0c;在仅使用 ImageNet 数据、4 块 GPU 训练三天的条件下就达到了 SOTA 水平。该压缩包围绕…

作者头像 李华
网站建设 2026/10/4 18:07:43

MRAM取代Flash与EEPROM:STM32掉电数据保存实战方案

做工业设备的嵌入式开发&#xff0c;最绕不开的老大难就是数据掉电保存。EEPROM写寿命有限&#xff0c;Flash要先擦后写又慢得让人心焦&#xff0c;特别是处理高频参数记录和故障瞬间保存这种需求&#xff0c;总得在容量、寿命、速度之间反复妥协。这两年我在几个项目里改用 Ev…

作者头像 李华
网站建设 2026/10/4 18:07:35

别再问“哪款AI写论文最牛”了:土木水利交通人的毕设辅助工具组合,建议这样配 [特殊字符]️

如果你读的是土木、水利与交通工程&#xff0c;大概率会遇到一类很典型的毕业设计&#xff1a;某市滨河路段雨水管网提标改造与内涝整治设计你需要分析区域降雨和道路积水情况&#xff0c;进行汇水区域划分、雨水流量计算、管网管径与坡度设计&#xff0c;必要时用 SWMM、InfoW…

作者头像 李华
网站建设 2026/10/4 18:02:41

智能公关平台MediaBee深度解析:数据驱动的媒体关系管理

1. 为什么传统发稿模式越来越“失灵”了先说一个我自己的感受。这两年在公关圈子里&#xff0c;有个挺明显的趋势&#xff1a;过去那种“写好新闻稿、群发给媒体、然后等着看剪报”的传统发稿流程&#xff0c;正在肉眼可见地失效。信息传播路径碎得不成样子&#xff0c;记者看邮…

作者头像 李华