达梦数据库(DM8)支持全文索引功能,但并不直接兼容 MySQL 的 FULLTEXT 索引语法

达梦数据库(DM8)支持全文索引功能 ,但并不直接兼容 MySQL 的 FULLTEXT 索引语法。不过,达梦提供了自己的全文索引机制,可以在一定程度上实现类似 MySQL 的全文检索能力。

达梦全文索引的特点:

  1. 使用 CREATE CONTEXT INDEX 语法 创建全文索引,而不是 MySQL 的 FULLTEXT 关键字。

  2. 支持的字段类型 包括 CHARVARCHARTEXTCLOB 等。

  3. 支持中文和英文分词 ,提供多种分词器(如 CHINESE_LEXERCHINESE_FP_LEXERENGLISH_LEXER)。

  4. 使用 CONTAINS() 函数 进行全文检索,而不是 MySQL 的 MATCH ... AGAINST 语法。

  5. 索引创建后需手动或自动同步更新,支持增量更新机制。

示例对比:

表格

复制

功能 MySQL 达梦
创建全文索引 CREATE FULLTEXT INDEX idx ON table(col) CREATE CONTEXT INDEX idx ON table(col) LEXER CHINESE_LEXER
查询 SELECT * FROM table WHERE MATCH(col) AGAINST('关键词') SELECT * FROM table WHERE CONTAINS(col, '关键词')

兼容性建议:

  • 虽然达梦不原生支持 MySQL 的 FULLTEXT 语法,但可以通过修改 SQL 层代码适配达梦的 CONTEXT INDEXCONTAINS 语法

  • 达梦支持设置兼容模式(如 COMPATIBLE_MODE=4 表示兼容 MySQL),但这主要影响语法层面全文索引仍需使用达梦自己的实现方式

结论:

✅ 达梦数据库支持全文索引功能,但不兼容 MySQL 的 FULLTEXT 索引语法 。需要使用达梦提供的 CONTEXT INDEXCONTAINS 函数来实现类似功能 。

如你正在从 MySQL 迁移至达梦,建议对涉及全文检索的 SQL 进行语法转换。需要帮助生成转换脚本或示例,可以继续问我。

下面给出一份"从建库到检索"的完整实战清单,全部用达梦自带的 SQL 工具(DM Manager、DIsql 或应用 JDBC)即可直接跑通。

你只要把表名、列名、词法器(LEXER)换成自己的就能落地。


一、前置检查

  1. 确认数据库版本 ≥ DM8(DM7 也支持,但 DM8 分词器更丰富)。

  2. 确认 ini 参数 ENABLE_INDX_CTX=1(默认就是 1,无需重启)。

  3. 确认字符集是 UTF-8 或 GB18030,否则中文分词会乱码。


二、建表并灌数据

sql

复制

复制代码
-- 1. 建一张测试表
CREATE TABLE news(
    id          INT PRIMARY KEY,
    title       VARCHAR(200),
    content     CLOB,
    pub_time     DATE
);

-- 2. 灌几行数据(CLOB 直接插字符串即可)
INSERT INTO news VALUES
(1, '达梦数据库发布DM8', '武汉达梦公司今日正式发布DM8,支持全文检索、JSON、空间数据等新特性。', DATE'2025-12-12'),
(2, 'MySQL全文索引简介', 'MySQL通过FULLTEXT索引实现全文检索,但语法与达梦不同。', DATE'2025-12-11');
COMMIT;

三、创建全文索引 语法模板

CREATE CONTEXT INDEX <索引名> ON <表名>(<列名[,...]>) [LEXER <词法器名>] [SYNC TRANSACTION|SYNC EVERY <秒数>|SYNC MANUAL];

常用词法器

CHINESE_LEXER -- 中文通用,按词切分

CHINESE_FP_LEXER -- 中文细粒度,人名/地名/机构名识别更好

ENGLISH_LEXER -- 纯英文

MIX_LEXER -- 中英混合,一份索引里两种语言都能查

示例:给 title 和 content 建一个"中文通用"索引,每次提交事务后自动同步。

sql

复制

复制代码
CREATE CONTEXT INDEX idx_news_all ON news(title, content)
LEXER CHINESE_LEXER
SYNC TRANSACTION;

执行成功后,会自动生成系统表 CTXSYS.DR$xxx 开头的五张内部表,无需理会。


四、手工同步(可选) 如果建索引时选了 SYNC MANUAL,或者批量导数后想立即搜到,可手动同步:

sql

复制

复制代码
DBMS_CTXT.SYNC_INDEX('idx_news_all', 2);   -- 2 表示并行度

五、执行检索

  1. 单字段检索

sql

复制

复制代码
SELECT id, title, SCORE(0) AS score
FROM news
WHERE CONTAINS(content, '达梦 数据库', 0) > 0   -- 0 是评分别名
ORDER BY score DESC;

说明

  • 第一个参数是要查的列,支持多列索引时写任意一列即可。

  • 第二个参数是检索串,支持布尔逻辑:空格=与,|=或,-=非,() 可嵌套。

  • 第三个参数是给 SCORE() 用的"查询编号",与 CONTAINS 第三参保持一致即可拿到相关度。

  1. 多关键词布尔示例

sql

复制

复制代码
-- 必须含"全文",且含"索引"或"检索",排除"MySQL"
SELECT id, title
FROM news
WHERE CONTAINS(content, '全文 (索引|检索) -MySQL', 0) > 0;
  1. 高亮片段(DM8 新功能)

sql

复制

复制代码
SELECT id,
       CTX_QUERY.SNIPPLEX('idx_news_all', content, '达梦', '<b>', '</b>', 0, 30) AS snippet
FROM news
WHERE CONTAINS(content, '达梦', 0) > 0;

返回结果会在关键词前后自动加 <b>


六、维护索引

  1. 删除索引

sql

复制

复制代码
DROP CONTEXT INDEX idx_news_all;
  1. 在线加列(DM8 支持)

sql

复制

复制代码
ALTER CONTEXT INDEX idx_news_all REBUILD ADD COLUMN (abstract CLOB);
  1. 查看索引是否失效

sql

复制

复制代码
SELECT idx_name, status FROM ctxsys.ctx_indexes WHERE idx_name='IDX_NEWS_ALL';
-- STATUS=VALID 表示正常;=INVALID 需 REBUILD

七、与 MySQL 语法映射速查 MySQL:

sql

复制

复制代码
SELECT * FROM t WHERE MATCH(col) AGAINST ('+keyword1 +keyword2 -keyword3' IN BOOLEAN MODE);

达梦等价:

sql

复制

复制代码
SELECT * FROM t WHERE CONTAINS(col, 'keyword1 keyword2 -keyword3', 0) > 0;

只需把 MATCH ... AGAINST 换成 CONTAINS(...),布尔符号用空格/|-即可。


八、常见报错

  1. "无法找到词法器" → 拼错 LEXER 名字,或数据库未安装 CTX 组件(标准版默认已装)。

  2. "CONTAINS 返回 0 行" → 索引还没同步,先执行 DBMS_CTXT.SYNC_INDEX。

  3. 高亮函数返回空 → 第三个参数必须与 CONTAINS 第三参一致,且索引里确实能命中。


九、性能小贴士

  • 对 CLOB 建全文索引时,把 SYNC 改成"每 N 秒"或"手动",批量导入后再一次性 SYNC,速度比每条提交都同步快 10 倍以上。

  • 检索串长度 < 2 的中文词(如"北京")默认被过滤,可在 CTXSYS.DR$xxx 表里看分词结果,必要时改用 CHINESE_FP_LEXER。

  • 大并发场景打开 INI 参数 CTX_QUERY_BUF_SIZE(默认 8 M),可显著降低物理读。


把上面 9 步跑完,你就拥有了"达梦版全文检索"。

如果还有更复杂的排序、分页、高亮、片段长度、自定义词典等需求,随时再喊我。

使用了mybatis框架查询语句怎么写

编辑

复制

分享

在 MyBatis 里调达梦的全文检索,只要把 CONTAINS(...) 当成一个普通的布尔函数写进 <where> 即可;没有方言包也不用改驱动,因为 SQL 本身完全由达梦服务端解析。下面给出最常见的 4 种写法(XML 方式),复制即可用。


一、前置约定 表:NEWS(id, title, content)

全文索引:idx_news_all(title,content)

MyBatis 接口方法:

java

复制

复制代码
List<News> search(@Param("kw") String keyword);

二、最简等值写法(返回 List<News>

xml

复制

复制代码
<select id="search" resultType="com.xxx.News">
    SELECT id, title, content, SCORE(0) score
    FROM news
    WHERE CONTAINS(content, #{kw}, 0) > 0
    ORDER BY score DESC
</select>

说明:

  • #{kw} 直接占位,MyBatis 会预编译成 ?,不存在 SQL 注入。

  • 第三个参数 0SCORE(0) 对应,想拿相关度就这么写。


三、关键词里有空格 / 布尔逻辑(动态拼接) 用户界面输入框允许空格、|、- 时,不要#{} 否则会被转义成单引号,导致布尔失效;改用 ${} 或者 Java 代码先拼好再传参。

安全写法(Java 先拼):

java

复制

复制代码
String kw = "全文 检索 -MySQL";   // 用户输入
kw = kw.replaceAll("[^\\u4e00-\\u9fa5a-zA-Z0-9|\\-\\s]", ""); // 简单过滤
List<News> list = mapper.search(kw);

XML 不变,仍用 #{kw} 即可,因为经过过滤后已安全。


四、分页 + 总记录数(两套 SQL)

  1. 列表

xml

复制

复制代码
<select id="search" resultType="com.xxx.News">
    SELECT * FROM (
        SELECT id, title, content, SCORE(0) score, rownum rn
        FROM news
        WHERE CONTAINS(content, #{kw}, 0) > 0
        ORDER BY score DESC
    ) WHERE rn BETWEEN #{start} AND #{end}
</select>
  1. 总记录数(复用同一个 <sql> 片段)

xml

复制

复制代码
<sql id="ftWhere">
    WHERE CONTAINS(content, #{kw}, 0) > 0
</sql>

<select id="count" resultType="long">
    SELECT COUNT(*) FROM news
    <include refid="ftWhere"/>
</select>

五、多字段任意命中(OR 逻辑) 索引建在 (title,content) 两列,但只想查一次:

xml

复制

复制代码
SELECT id, title
FROM news
WHERE CONTAINS(title, #{kw}, 0) > 0
   OR CONTAINS(content, #{kw}, 0) > 0

达梦会自动走同一个索引,不会双倍扫描。


六、高亮片段(返回 Map 带 <em>

xml

复制

复制代码
<select id="searchWithSnippet" resultType="map">
    SELECT id,
           title,
           CTX_QUERY.SNIPPLEX('idx_news_all',
                              content,
                              #{kw},
                              '&lt;em&gt;',  -- MyBatis 转义 <
                              '&lt;/em&gt;',
                              0,
                              60) snippet
    FROM news
    WHERE CONTAINS(content, #{kw}, 0) > 0
</select>

注意:

  • 第三个参数必须与 CONTAINS 第三参一致。

  • 标签里的 < > 写成 &lt; &gt;,否则 XML 会报错。


七、批量 in 查询 + 全文组合

xml

复制

复制代码
<select id="searchInIds" resultType="com.xxx.News">
    SELECT id, title
    FROM news
    WHERE id IN
    <foreach collection="idList" item="i" open="(" separator="," close=")">
        #{i}
    </foreach>
    AND CONTAINS(content, #{kw}, 0) > 0
</select>

达梦会先做 IN 过滤,再走全文索引,百万级数据也很快。


八、常见坑

  1. #{} 把布尔符号转义了 → 结果永远 0 行。

    解决:要么 Java 先拼好再 #{},要么用 ${} 并自行过滤危险字符。

  2. MyBatis 日志打印出来的 SQL 把 ? 当占位符,看起来不像最终语句,直接拷贝到 DIsql 跑会报错;真正发到 JDBC 的参数由驱动替换,无需担心。

  3. 高亮函数 SNIPPLEX 第二个参数必须是列名,不能是表达式/绑定参数,否则报 "列不存在"。

  4. 分页用 rownum 时,一定 先套子查询再 WHERE rn BETWEEN,否则 rownum 会先生成再过滤,结果不对。


九、完整可用小例子(Spring Boot + MyBatis)

java

复制

复制代码
@Mapper
public interface NewsMapper {
    List<News> search(@Param("kw") String keyword);
}

xml

复制

复制代码
<select id="search" resultType="com.demo.entity.News">
    SELECT id, title, content, SCORE(0) score
    FROM news
    WHERE CONTAINS(content, #{kw}, 0) > 0
    ORDER BY score DESC
    FETCH FIRST 20 ROWS ONLY   -- 达梦支持 ANSI FETCH,等价于 MySQL limit 20
</select>

Service 层直接调:

java

复制

复制代码
List<News> list = newsMapper.search("达梦 全文");

即可返回按相关度倒序的前 20 条。


把上面任意一段 XML 粘进你的 *Mapper.xml无需额外插件、无需改方言 ,就能在 MyBatis 里愉快地使用达梦全文检索了。

如果还要加权重、自定义词典、或者 Elasticsearch 混合架构,再喊我。

相关推荐
阿巴斯甜12 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker13 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952714 小时前
Andorid Google 登录接入文档
android
黄林晴15 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_1 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android