下面我将从基础语法、常用场景和安全注意事项三个方面,为你详细讲解如何使用 SQL 调用栏目。

基础语法
在 DedeCMS 模板中,使用 SQL 调用主要通过 {dede:sql} 标签实现,其基本结构如下:
{dede:sql sql='你的SQL语句'}
<!-- 循环体,使用[field:字段名/]来输出数据 -->
<a href="[field:typedir/]">[field:typename/]</a>
{/dede:sql}
核心参数说明:
sql='...': 这是必需的参数,用于填写你的 SQL 查询语句。itemindex='数字': 可选,用于给循环体中的变量命名前缀,itemindex='my',那么字段名就变成了[field:my.name/],在多表查询、字段名可能冲突时非常有用。islocal='yes': 可选,设置为yes时,可以将查询结果缓存到本地,提高页面加载速度,但对于栏目列表这类不常变动的数据,可以开启。
常用场景与示例
DedeCMS 的栏目信息主要存储在 dede_arctype 表中,我们的 SQL 查询主要围绕这张表进行。
场景1:获取指定ID的栏目信息
如果你想获取某一个特定栏目的详细信息(比如栏目名称、链接等),可以直接查询 dede_arctype 表。

示例:获取栏目ID为 1 的栏目信息
{dede:sql sql='SELECT id, typename, typedir, description FROM dede_arctype WHERE id = 1'}
<h2>[field:typename/]</h2>
<p>栏目链接:<a href="[field:typedir/]">[field:typename/]</a></p>
<p>栏目描述:[field:description function="htmlspecialchars(@me)"/]</p>
{/dede:sql}
代码解析:
SELECT id, typename, typedir, description FROM dede_arctype WHERE id = 1:查询dede_arctype表,获取id为1的栏目的id、typename(栏目名)、typedir(栏目链接)、description(栏目描述)。[field:typename/]:输出栏目名称。[field:typedir/]:输出栏目链接,通常需要加上你的网站域名。function="htmlspecialchars(@me)":对输出内容进行 HTML 转义,防止 XSS 攻击,是一个良好的安全习惯。
场景2:获取指定层级的所有栏目
获取所有顶级栏目(topid 为 0 的栏目)。
示例:获取所有顶级栏目

<ul>
{dede:sql sql='SELECT id, typename, typedir FROM dede_arctype WHERE topid = 0 ORDER BY sortrank ASC'}
<li><a href="[field:typedir/]">[field:typename/]</a></li>
{/dede:sql}
</ul>
代码解析:
WHERE topid = 0:这是关键条件,topid字段表示栏目的父级栏目ID,0代表顶级。ORDER BY sortrank ASC:按照sortrank(排序权重)字段进行升序排列,确保栏目按你设定的顺序显示。
场景3:获取指定栏目的所有子栏目
获取栏目ID为 5 的所有直接子栏目。
示例:获取栏目ID为 5 的所有子栏目
<ul>
{dede:sql sql='SELECT id, typename, typedir FROM dede_arctype WHERE reid = 5 ORDER BY sortrank ASC'}
<li><a href="[field:typedir/]">[field:typename/]</a></li>
{/dede:sql}
</ul>
代码解析:
WHERE reid = 5:reid字段表示栏目的父级栏目ID,这个条件用于筛选出父级栏目ID为5的所有子栏目。
场景4:获取指定栏目及其所有子栏目(递归查询)
这是一个比较复杂的需求,因为 SQL 本身不支持递归查询(除非使用特定数据库的递归语法),在 DedeCMS 中,我们通常利用系统内置的函数来实现。
示例:获取当前栏目及其所有子栏目(推荐方法)
{dede:channel type='son' currentstyle='
<li class="current"><a href~typename~</a></li>
'}
<li><a href='[field:typedir/]'>[field:typename/]</a></li>
{/dede:channel}
为什么推荐这个方法?
{dede:channel}是 DedeCMS 专门为栏目调用设计的标签,它内部已经处理了递归逻辑,性能更好,代码更简洁。type='son'表示调用当前栏目的子栏目。currentstyle用于高亮显示当前栏目。
如果必须用 SQL 实现(不推荐)
你需要先通过 PHP 获取所有子栏目的 ID,然后再拼凑 SQL 查询,这非常麻烦且容易出错,因此强烈建议优先使用 {dede:channel}。
场景5:跨表查询(获取栏目及其文章数)
这是一个非常实用的场景,例如在首页制作一个热门栏目列表,显示每个栏目下的文章数量。
这需要同时查询 dede_arctype(栏目表)和 dede_archives(文章表)。
示例:获取顶级栏目及其对应的文章总数
<ul>
{dede:sql sql='SELECT a.id, a.typename, a.typedir, COUNT(b.id) as article_count FROM dede_arctype as a LEFT JOIN dede_archives as b ON a.id = b.typeid WHERE a.topid = 0 GROUP BY a.id ORDER BY a.sortrank ASC'}
<li>
<a href="[field:typedir/]">[field:typename/]</a>
(文章数: [field:article_count/])
</li>
{/dede:sql}
</ul>
代码解析:
SELECT a.id, a.typename, ...:给dede_arctype表起一个别名a,方便引用。FROM dede_arctype as a LEFT JOIN dede_archives as b ON a.id = b.typeid:将dede_arctype表(别名为a)和dede_archives表(别名为b)进行左连接,连接条件是栏目的id等于文章的typeid。COUNT(b.id) as article_count:对每个栏目分组后,统计b.id(即文章ID)的数量,并将结果命名为article_count。GROUP BY a.id:按栏目ID进行分组,这是COUNT聚合函数所必需的。[field:article_count/]:输出计算出的文章总数。
安全注意事项
直接在模板中写 SQL 存在巨大的安全风险,最常见的就是 SQL 注入,攻击者可以通过修改 URL 参数来恶意的拼接 SQL 语句,从而窃取、修改甚至删除你的数据库数据。
如何防范?
-
永远不要直接使用外部变量 绝对不要将 URL 中的 GET 参数直接拼接到 SQL 语句中。
错误示范(高危!):
{dede:sql sql='SELECT * FROM dede_arctype WHERE id = ~id~'}URL 是
...?id=1 OR 1=1,SQL 就会被篡改。 -
使用 DedeCMS 内置的
GetOne或Execute函数(高级用法) 在更复杂的情况下,你可以在PHP文件中进行数据库查询,而不是在模板里,PHP 文件中的查询可以更好地进行参数过滤。示例(在
plus/my_list.php文件中):<?php require_once(dirname(__FILE__)."/../include/common.inc.php"); // 安全地获取ID参数 $id = isset($id) && is_numeric($id) ? intval($id) : 0; if($id > 0) { $query = "SELECT * FROM dede_arctype WHERE id = $id"; $row = $dsql->GetOne($query); // 使用DedeCMS的数据库对象进行查询 if(is_array($row)) { // 将查询结果赋值给模板 $GLOBALS['typeinfo'] = $row; } } // 引入模板文件 include(DEDETEMPLATE.'/plus/my_list.htm'); ?>模板文件 (
my_list.htm):{dede:getdata name='typeinfo'} <h2>[field:typename/]</h2> {/dede:getdata}这种方式通过
intval()或is_numeric()对输入参数进行了严格的类型检查,从根本上杜绝了 SQL 注入。
| 场景 | 推荐标签/方法 | 备注 |
|---|---|---|
| 获取单个栏目 | {dede:sql} |
简单直接,注意SQL注入风险。 |
| 获取顶级/子栏目列表 | {dede:channel} |
强烈推荐,系统自带,功能强大,性能好。 |
| 递归查询栏目 | {dede:channel} |
内置支持,无需手动写复杂SQL。 |
| 跨表查询(如栏目文章数) | {dede:sql} |
功能灵活,但SQL语句较复杂,需注意安全。 |
| 复杂动态查询 | PHP文件 + {dede:sql} 或 {dede:getdata} |
最安全、最灵活的方式,适合开发复杂模块。 |
对于大多数常规的栏目调用需求,优先使用 {dede:channel} 标签,只有在它无法满足的特殊需求下,才谨慎使用 {dede:sql},并务必牢记安全第一,杜绝SQL注入。
