织梦批量移动栏目SQL实战:reid字段3秒改完
DedeCMS后台拖拽改栏目层级一次几小时,SQL一行3秒搞定。保哥过去5年帮23个客户做栏目重构沉淀的实战:dede_arctype表reid与topid字段含义、批量UPDATE的安全写法、文章表与arctiny表的同步处理、80个SKU子栏目重构案例、URL 301重定向保SEO、清缓存6层顺序、各版本字段差异、批量生成SQL的Python脚本与binlog审计。
本文目录
- 为什么后台拖动是噩梦
- dede_arctype 表结构关键字段
- 核心 SQL 一行搞定
- topid 字段必须同步更新
- 关联数据同步:文章表的处理
- 执行前必须备份
- 实战案例:80 个 SKU 子栏目的重构
- URL 301 重定向:保 SEO 排名
- 清缓存的多个层级
- 不同 DedeCMS 版本的差异
- 排序权重 sortrank 的处理
- 常见的几个反复出现的坑
- 用脚本批量生成 UPDATE 语句
- 常见问题解答
- UPDATE dede_arctype 改 reid 后前台栏目结构没变化怎么处理
- 批量移动栏目后文章的归属会自动跟着变吗
- topid 字段是什么作用,必须改吗
- 批量移动后 URL 变了,SEO 流量会受影响吗
- 能不能用 SQL 一次性新建多个栏目
- DedeCMS 5.7 SP2 和 5.8 之间的 dede_arctype 字段有变化吗
- 批量 UPDATE 会不会锁表影响线上访问
- 能否用 SQL 把栏目转换成单页或外链
- 从 DedeCMS 迁出的现实选择
- SQL 操作审计与日志
给 DedeCMS 老站做栏目结构重构,最磨人的是后台栏目管理那个拖拽界面——一次只能挪一个栏目,鼠标按住没准就误触到隔壁分类,确认按钮还得逐个点。一个 80 个栏目的电商站重构整个父子关系,纯手工拖拽要花 5 到 7 个小时;用 SQL 一行命令搞定只要 3 秒。本文把保哥过去 5 年帮 23 个 DedeCMS 客户做栏目重构积累的 SQL 实战、关联数据同步、URL 301 处理、缓存清理顺序系统整理出来。
覆盖版本:DedeCMS v5.7 SP2 / v5.8 / v6.0(DedeBIZ 衍生版)。表名前缀按官方默认 dede_,自己改过前缀的根据实际情况替换。
为什么后台拖动是噩梦
DedeCMS 后台 - 核心 - 网站栏目管理界面用的是 2010 年的 jQuery UI 拖拽实现。在一个 80 个栏目的层级树上拖拽时有三个具体痛点:
- 拖到目标位置高亮指示不准:经常拖到 A 栏目想放到 B 栏目下面,结果实际进了 C。每次拖完都要肉眼核对当前位置。
- 多层级展开/折叠状态频繁丢失:拖完一次树状结构会重新渲染,所有展开节点折叠回去,再拖下一个又要重新展开点几次。
- 批量同类操作每次都重复:把 30 个 SKU 子栏目从"服装-男装"挪到"服装-户外",前 29 个都做完了第 30 个一手抖拖错位置——前面的工作全部要重新核对。
保哥实测过:23 个客户里有 11 个做过完整的栏目重构。后台拖拽的平均耗时是每 100 个栏目 6 到 8 小时。SQL 一次性操作的平均耗时是每 100 个栏目 2 到 4 分钟(含备份、执行、验证、清缓存)。差距 100 倍以上。
dede_arctype 表结构关键字段
DedeCMS 的所有栏目都存在 dede_arctype 表里。理解几个核心字段是写 SQL 的前提:
| 字段 | 类型 | 含义 | 修改栏目层级时的作用 |
|---|---|---|---|
| id | mediumint unsigned | 栏目 ID(主键) | UPDATE 时 WHERE 用 |
| reid | mediumint unsigned | 父栏目 ID | 这就是要批量改的核心字段 |
| topid | mediumint unsigned | 顶级栏目 ID(多级嵌套时记最上层) | 改 reid 后需要同步更新 |
| sortrank | smallint unsigned | 同级栏目排序权重 | 批量移动后可能需要重排 |
| typename | varchar(60) | 栏目名称 | 用来定位栏目 |
| typedir | varchar(60) | 栏目目录名(静态生成时用) | 移动后要重新生成静态需要更新 |
| ispart | tinyint | 0 列表 / 1 单页 / 2 外链 | 不影响移动操作 |
| channeltype | smallint | 内容模型 ID(默认 1 是普通文章) | 不影响移动操作 |
核心是 reid 字段。改 reid 等于改父栏目。改完后再同步 topid(顶级父)字段,整个栏目层级就重构完成。
核心 SQL 一行搞定
最简单的批量移动语句:
UPDATE dede_arctype SET reid = 100 WHERE id IN (12, 15, 18, 22, 25, 31, 35, 42);这条 SQL 把 id 为 12、15、18、22、25、31、35、42 的 8 个栏目全部挂到 id 为 100 的父栏目下面。括号里栏目 ID 数量没有硬限制,一次塞几百个都行(MySQL 单条 SQL 的 IN 列表上限是 max_allowed_packet 决定的,几千个 ID 通常没问题)。
如果你想把全部子栏目从一个父栏目挪到另一个父栏目:
UPDATE dede_arctype SET reid = 100 WHERE reid = 50;这条 SQL 把所有 reid=50 的子栏目改成 reid=100。常见场景是父栏目重组:原来"服装"下面有男装/女装/童装三个子栏目,现在要把这三个挪到"零售-服装"父栏目下面,一行搞定。
topid 字段必须同步更新
很多教程到 UPDATE reid 就结束了,这是错的。DedeCMS 内部除了 reid 还维护 topid 字段记录"最顶层的父栏目 ID",用于面包屑导航和栏目树构建。如果只改了 reid 没改 topid,前台显示会出现栏目树错乱、面包屑不对、列表页内容混乱。
同步更新 topid 的写法(假设你已经把 reid 改成 100,且 100 这个栏目是顶层栏目即 reid=0):
UPDATE dede_arctype SET topid = 100 WHERE reid = 100;如果 100 不是顶层栏目,比如它本身的 reid 是 50,那 topid 应该是 50 的 topid。这种情况下用嵌套查询:
UPDATE dede_arctype t1
SET t1.topid = (SELECT t2.topid FROM (SELECT topid FROM dede_arctype WHERE id = 100) t2)
WHERE t1.reid = 100;注意 MySQL 不允许直接在 UPDATE 的子查询里查同一张表,要用衍生表(再套一层 SELECT)。
更保险的做法是分两步:先查出 100 的 topid 值,再写一条 UPDATE 直接赋值。
关联数据同步:文章表的处理
DedeCMS 的文章数据存在 dede_archives 表里,每篇文章有一个 typeid 字段记录所属栏目。如果只是改 dede_arctype 的层级关系,dede_archives.typeid 不需要动——文章的"直接所属栏目"没变,只是这个栏目挂到不同父栏目下了。
但有些场景下确实需要把文章批量从一个栏目挪到另一个栏目(不是改层级而是真的换栏目归属):
-- 把原本属于栏目 50 的所有文章挪到栏目 100
UPDATE dede_archives SET typeid = 100 WHERE typeid = 50;同时还需要同步更新 dede_arctiny 表(DedeCMS 的内容索引表):
UPDATE dede_arctiny SET typeid = 100 WHERE typeid = 50;dede_arctiny 这张表很多新手都忽略——它存的是文章 ID 与栏目、发布时间的简略关系,是 DedeCMS 内部做列表分页和性能优化用的。两张表的 typeid 必须保持一致,否则前台列表会少文章。
执行前必须备份
SQL 操作的爽快和它的危险性是同等的。一条错的 UPDATE 让全站栏目结构错乱,恢复要花一天。执行前必须备份。
最简单的备份方式:
-- Linux
mysqldump -u root -p dedecms_db dede_arctype dede_archives dede_arctiny > backup_$(date +%Y%m%d_%H%M%S).sql
-- Windows
mysqldump -u root -p dedecms_db dede_arctype dede_archives dede_arctiny > backup_20260512.sql如果不能命令行 mysqldump,phpMyAdmin 导出这三张表也行。备份文件先下载到本地保存。
备份后建议在 phpMyAdmin 里做一次"试跑"——把 UPDATE 改成 SELECT 看看影响的行:
SELECT id, reid, topid, typename FROM dede_arctype WHERE id IN (12, 15, 18, 22, 25, 31, 35, 42);核对返回的栏目是不是你预期要改的那批。确认无误再执行真正的 UPDATE。
实战案例:80 个 SKU 子栏目的重构
保哥 2024 年帮一个母婴电商客户重构栏目。客户原来的结构是:
母婴用品(id=1)
├── 奶粉(id=10)
│ ├── 婴儿奶粉(id=101)
│ ├── 幼儿奶粉(id=102)
│ └── 学生奶粉(id=103)
├── 尿不湿(id=20)
│ └── ...(25 个 SKU 子栏目)
└── 童装(id=30)
└── ...(50 个 SKU 子栏目)客户的需求是按"使用场景"重组:婴儿期、幼儿期、儿童期三个新顶级,把所有 SKU 子栏目按目标年龄段重新分配。
第一步:先在 dede_arctype 里新增三个顶级栏目"婴儿期 0-1 岁"(id=200)、"幼儿期 1-3 岁"(id=201)、"儿童期 3-12 岁"(id=202)。后台手动加,3 个栏目大约 2 分钟。
第二步:导出当前 dede_arctype 表,在 Excel 里标注每个 SKU 子栏目应该归到哪个新顶级。80 个 SKU 大约 30 分钟。这步是脑力工作不是技术问题。
第三步:写 SQL 批量更新。三条 UPDATE:
-- 婴儿期 0-1 岁
UPDATE dede_arctype SET reid=200, topid=200 WHERE id IN (101, 202, 305, 410, ...);
-- 幼儿期 1-3 岁
UPDATE dede_arctype SET reid=201, topid=201 WHERE id IN (102, 203, 307, 412, ...);
-- 儿童期 3-12 岁
UPDATE dede_arctype SET reid=202, topid=202 WHERE id IN (103, 204, ...);第四步:清理 dede_arctype 里原来的"奶粉""尿不湿""童装"三个旧父栏目——它们的所有子栏目都搬走了,但栏目本身还在。如果完全用不到了就 DELETE,否则就标记为隐藏。
第五步:DedeCMS 后台 - 生成 - 更新栏目缓存。这步至关重要——DedeCMS 在 data/cache/ 目录下有栏目结构缓存,SQL 直接改库后缓存还是旧的。不清缓存前台列表会出错。
第六步:DedeCMS 后台 - 生成 - 重新生成所有栏目静态页面。这步只在你用了静态化(HTML 生成)的站点才需要。
整个操作(含备份、SQL、清缓存、重生成)约 50 分钟。客户原本预算 8 小时,实际省下 6 小时多。
URL 301 重定向:保 SEO 排名
栏目重构最容易忽略的副作用是 URL 变化。原本"母婴用品/奶粉/婴儿奶粉"的栏目 URL 是 /list/10/101/ 或者 /naifen/yinger-naifen/,重构后变成 /list/200/101/ 或 /yingerqi/yinger-naifen/。Google 已经收录的老 URL 全部 404,SEO 流量会断崖。
处理方法:在 Nginx(或 IIS)加 301 重定向规则。Nginx 例子:
location / {
# 旧栏目 URL 301 到新栏目
rewrite ^/naifen/yinger-naifen/(.*)$ /yingerqi/yinger-naifen/$1 permanent;
rewrite ^/naifen/youer-naifen/(.*)$ /youerqi/youer-naifen/$1 permanent;
# ...每个变化的栏目一条规则
}如果你的栏目 URL 数量超过 100,不要手写 rewrite——保哥的做法是先用 SQL 把变化前后的 URL 对应关系导出成 CSV,再写一个 Python 脚本生成 Nginx 配置或 .htaccess 规则。
更现代的做法是在 DedeCMS 里加一个轻量级 PHP 处理器,按数据库映射表做 301 跳转——这样不需要重启 Nginx。
清缓存的多个层级
DedeCMS 的栏目数据有多层缓存,SQL 改完不清缓存前台不会变。按以下顺序清理:
- 栏目缓存:DedeCMS 后台 - 系统 - 更新栏目缓存(或 data/cache/inc_catalog_base.inc 文件直接删)。
- 文档关键词缓存:data/keywords.dat。
- 导航菜单缓存:data/cache/inc_menu.inc 文件。
- 整站静态文件:如果用了 HTML 静态化,去后台"一键更新网站"重新生成。
- 浏览器缓存:Ctrl+F5 强制刷新自己测试。
- CDN 缓存:如果用了 CDN,去 CDN 控制台批量刷新栏目页面。
第 1 步是最关键的——保哥见过太多人改完 SQL 看前台没变就以为 SQL 没生效,反复重跑 SQL 把数据搞乱,根因就是没清栏目缓存。
不同 DedeCMS 版本的差异
DedeCMS 5.7 SP2、5.8、6.0(DedeBIZ 版)在 arctype 表上几乎没有差异,UPDATE SQL 通用。但有几个细节版本依赖:
- v5.7 SP2 之前的版本没有 topid 字段,只有 reid——这种老版本只需要改 reid 不需要管 topid。
- v5.8 引入了 ishidden 字段控制栏目是否隐藏——批量移动时如果某些栏目要标记隐藏,多加一个 UPDATE ishidden=1。
- v6.0 DedeBIZ 版加了"管理员组栏目权限"机制——栏目移动后要看权限表(dede_admintype 等)有没有跟着同步。
升级版本前先看一下 dede_arctype 的表结构是否变化(DESCRIBE dede_arctype 看一下),有新字段不要慌,写 SQL 时把新字段的值也带上即可。
排序权重 sortrank 的处理
批量移动栏目后,新父栏目下原本可能已经有几个子栏目,加上挪过来的批新栏目,整体排序可能错乱。sortrank 字段的值是数字,数字越大显示越靠前。
批量重排序的写法:
-- 按 ID 升序重设 sortrank
SET @rank = 0;
UPDATE dede_arctype SET sortrank = (@rank := @rank + 1) WHERE reid = 100 ORDER BY id ASC;这条 SQL 把 reid=100 下面的所有子栏目按 id 升序重新分配 sortrank 1、2、3、...。
如果你想按栏目名拼音排序,那 ORDER BY 改成 typename ASC。
常见的几个反复出现的坑
- UPDATE 漏写 WHERE:UPDATE dede_arctype SET reid=100 不加 WHERE 会把所有栏目的 reid 都改成 100,瞬间全站栏目层级崩溃。执行前一定要看清楚 WHERE 条件是否完整。
- reid 指向了不存在的栏目 ID:UPDATE reid=999 但 999 这个栏目不存在,会让对应子栏目变成"孤儿",后台和前台都看不到。执行前确认目标 ID 存在。
- 循环引用:UPDATE 让栏目 A 的 reid 指向栏目 B,但 B 的 reid 是 A——这种循环引用会让前台递归遍历时栈溢出。执行前画一下层级图避免循环。
- topid 没同步:前面提过的坑,再强调一次——只改 reid 不改 topid,面包屑和栏目树会乱。
- 跨服务器 SQL 直接复制:从开发环境 dump 出的 SQL 拿到生产环境直接跑,因为栏目 ID 不一致全部错位。SQL 移植要先核对两边的 ID 对应关系。
用脚本批量生成 UPDATE 语句
当要批量移动的栏目数量超过 30 个,手写 IN(...) 列表容易出错。保哥的工程化做法是用 Excel 或者 CSV 维护"栏目 ID 到目标父栏目"的映射表,然后用脚本生成 SQL。Python 的例子:
import csv
# CSV 字段:栏目 ID, 新父栏目 ID, 新顶层 ID
with open('mapping.csv') as f:
reader = csv.DictReader(f)
by_parent = {}
for row in reader:
key = (int(row['new_reid']), int(row['new_topid']))
by_parent.setdefault(key, []).append(int(row['id']))
for (reid, topid), ids in by_parent.items():
ids_str = ','.join(map(str, ids))
print(f"UPDATE dede_arctype SET reid={reid}, topid={topid} WHERE id IN ({ids_str});")跑这个脚本会按"目标父栏目"分组输出多条 UPDATE。生成的 SQL 复制到 phpMyAdmin 或者命令行 mysql 执行即可。500 个栏目映射用脚本生成大概 1 秒,手写要半天还容易出错。
常见问题解答
UPDATE dede_arctype 改 reid 后前台栏目结构没变化怎么处理
99% 是栏目缓存没清。DedeCMS 后台 - 系统 - 更新栏目缓存按一下;如果还不行直接删除 data/cache/inc_catalog_base.inc 文件,DedeCMS 下次访问时会重新生成。如果你用了 HTML 静态化,还需要去后台"一键更新网站"重新生成栏目静态页面。Ctrl+F5 刷新自己测试,看不到就开无痕窗口验证。
批量移动栏目后文章的归属会自动跟着变吗
不会。dede_arctype 改的只是栏目层级关系(reid 父子关系)。文章存在 dede_archives 表里,每篇文章的 typeid 字段记录直接所属的栏目 ID。这个字段不会因为父栏目变了而自动更新——文章依然属于原栏目,只是这个栏目挂到了新父栏目下面。如果你需要批量把文章从一个栏目搬到另一个栏目,需要单独 UPDATE dede_archives SET typeid=新栏目 WHERE typeid=旧栏目,并且同步更新 dede_arctiny 表。
topid 字段是什么作用,必须改吗
topid 字段记录的是"最顶层父栏目的 ID",DedeCMS 用它做面包屑导航和栏目树递归优化。如果只改 reid 不改 topid,前台显示会出现栏目树错乱、面包屑指向错误的顶层栏目。所以批量移动栏目后必须同步更新 topid。最简单的写法是先查目标父栏目的 topid 值,然后 UPDATE dede_arctype SET topid=查到的值 WHERE reid=目标父栏目 ID。
批量移动后 URL 变了,SEO 流量会受影响吗
会,而且影响很大。Google 已经索引的老 URL 全部 404,对应的搜索流量会消失。处理方法是在 Nginx 或 IIS 配 301 重定向把老 URL 跳到新 URL,保留权重传递。批量栏目重构后立刻配置 301,最好在重构前先导出老 URL 清单备用。301 配好后 Google 通常需要 2 到 6 周完成索引切换。
能不能用 SQL 一次性新建多个栏目
可以,用 INSERT。比如 INSERT INTO dede_arctype (reid, topid, typename, typedir, ispart, channeltype) VALUES (0, 0, '婴儿期 0-1 岁', 'yingerqi', 0, 1), (0, 0, '幼儿期 1-3 岁', 'youerqi', 0, 1)。但要注意 typedir 字段如果重复会导致 URL 冲突,typename 重复 DedeCMS 不会阻止但管理上会混乱。批量新建栏目后还要去后台"更新栏目缓存"才生效。
DedeCMS 5.7 SP2 和 5.8 之间的 dede_arctype 字段有变化吗
大部分字段相同。5.8 新增了 ishidden 字段(控制栏目是否在前台菜单隐藏,但内容依然能访问)和 cross 字段(跨栏目调用配置)。批量移动 SQL 用的 reid、topid 字段两个版本都有。如果你的目标是从 5.7 升 5.8,先升级 DedeCMS 再做栏目重构,避免字段不一致的兼容问题。
批量 UPDATE 会不会锁表影响线上访问
会短暂锁定 dede_arctype 表。但即便是 1000 个栏目的批量 UPDATE,MySQL InnoDB 引擎的锁定时间也通常在 100 毫秒以内。对正在加载栏目列表的用户来说几乎无感知。如果你的站点流量极大(每秒数百次栏目查询),建议在低峰时段执行;普通中小站点直接执行没有可见影响。
能否用 SQL 把栏目转换成单页或外链
可以。ispart 字段 0 表示列表栏目(默认),1 表示单页(content 字段存内容),2 表示外链(typeurl 字段存目标 URL)。批量转换 UPDATE dede_arctype SET ispart=2, typeurl='https://example.com/' WHERE id IN (10, 20, 30)。注意转成单页后 content 字段也要写入内容否则前台空白;转成外链后栏目本身的子栏目和文章会失效。
从 DedeCMS 迁出的现实选择
这一节虽然不直接关系到批量移动栏目的 SQL,但保哥每次帮客户重构栏目时几乎都会被问到:"这次重构后值不值得继续用 DedeCMS"。给一个实用的答案。
DedeCMS 官方在 2018 年宣告停止商业授权销售(创始人离世后维护团队解散),目前的 5.7 SP2 和 5.8 都是社区维护版本。2026 年还在用 DedeCMS 的站点主要分三类:
- 老内容站:靠 SEO 流量吃饭的内容站,DedeCMS 的栏目和文章结构对 SEO 友好(URL 简洁、HTML 干净),换平台就是 SEO 重置。这类站点建议继续用 DedeCMS 直到流量不足以支撑维护成本。
- 个人博客和企业宣传站:访问量小、改动需求低的站点。继续用 DedeCMS 边际成本低。
- 电商分类站:流量集中在商品类目页的站点。如果电商功能要扩展(支付、物流、会员),DedeCMS 力不从心,建议迁到 WordPress + WooCommerce 或者其他现代电商。
迁出 DedeCMS 的最佳路径是 WordPress——两者都是 PHP 系,数据库结构虽然不同但可以写转换脚本。保哥手里有现成的 dedecms-to-wordpress 转换脚本,处理 5000 篇文章的小站迁移大约 4 到 6 小时。栏目层级、文章、附件、评论都能迁;TAG 标签需要手工核对;自定义字段需要额外映射。
不建议迁去 Drupal、Joomla 等其他 CMS——这些平台的国内社区已经太小,遇到问题求助困难。Magento、Shopify 适合纯电商但不适合内容站。Hugo、Jekyll 等静态站生成器适合个人博客但不适合需要后台运营的站点。WordPress 是绝大多数 DedeCMS 用户最自然的迁移目标。
SQL 操作审计与日志
正经的生产站点必须做 SQL 操作审计。DedeCMS 自己不提供这个功能,但你可以在 MySQL 层面开 general log 或 binary log 记录所有执行过的 SQL,遇到问题能回溯查证。
开启 binary log 的简单配置(在 my.cnf 的 [mysqld] 段添加):
log-bin = /var/lib/mysql/mysql-bin
binlog-format = ROW
expire_logs_days = 14开启后所有 UPDATE / INSERT / DELETE 都会记录到 binlog 里,可以用 mysqlbinlog 命令查看。如果你执行了一条错误的 UPDATE,binlog 能帮你定位"哪一刻发生了什么"并按行级别恢复数据。binlog 也是 MySQL 主从复制的基础设施。
对小站点来说 binlog 占用磁盘约 1% 到 5% 的数据量(按 ROW 格式),代价小但安全保障大。强烈建议开启。
FAQPage + Article AI 引用友好版
DedeCMS后台拖拽改栏目层级一次几小时,SQL一行3秒搞定。保哥过去5年帮23个客户做栏目重构沉淀的实战:dede_arctype表reid与topid字段含义、批量UPDATE的安全写法、文章表与arctiny表的同步处理、80个SKU子栏目重构案例、URL 301重定向保SEO、清缓存6层顺序、各版本字段差异、批量生成SQL的Python脚本与binlog审计。
- 织梦SQL
- dede_arctype
- DedeCMS
- 栏目移动
- reid字段
- 织梦CMS教程
title: 织梦批量移动栏目SQL实战:reid字段3秒改完 author: 张文保 (Paul Zhang) — PatPat SEO 经理 url: https://zhangwenbao.com/dedecms-column-batch-sql-statements-sub-column.html published: 2017-01-12 modified: 2026-05-16 source-type: First-hand expert commentary language: zh-CN license: CC BY-NC-SA 4.0 (要求保留原文链接与作者归属)
本文标题:《织梦批量移动栏目SQL实战:reid字段3秒改完》
本文链接:https://zhangwenbao.com/dedecms-column-batch-sql-statements-sub-column.html
版权声明:本文原创,转载请注明出处和链接。许可协议: CC BY-NC-SA 4.0