我应该让数据结构保持原样吗?如果是这样,我应该如何构造 sql 以允许对所有三列进行全文搜索 - 标题、描述和标签?
那是不可能的。索引只能跨越单个表的列。
或者最好去掉keywordsTable,并在dataTable的“tags”列中用逗号分隔实际标签?
这肯定是最简单的解决方案。您目前并没有真正从为标签提供自己的身份中获得任何好处,因为您不能使用外键并对其进行索引。
然而,MySQL 的 FULLTEXT 索引对于标签系统来说并不理想:
- 默认情况下,它不会索引短于四个字母的单词;
- 默认情况下,它有很多(很多)停用词,它不会索引您可能想要用于标签的停用词;
- 它的效率会低于普通索引;
- 它只在 MyISAM 中有效,在所有其他方面,它都是比 InnoDB 差得多的数据库引擎。除非您确实必须这样做,否则您今天不应该真正使用 MyISAM。
您可以通过更改 MySQL 配置来修复最小字长和停用词。不过,这将使您的索引更大。如果您在将要部署您的应用程序的任何地方都控制数据库,并且如果您仅在全文搜索素材中使用标签作为“额外词”而不是完整的分类系统,那么这可能是一个可接受的解决方案。
否则...数据库中以逗号分隔的任何内容都是可疑的 IMO。通常最好使用一对多连接表来表达一个实体有很多标签的想法。然后,您可以使用简单的索引来帮助查找,而不是有限的 FULLTEXT 索引方案,这将更快、更可靠,并允许您使用 InnoDB 和外键。例如:
dataTable
ID (primary key)
title
desc
dataTags
ID (foreign key -> dataTable)
tagName (index this column)
(如果您希望标签具有独立身份,您仍然可以在此基础上使用 tagID->tagName 映射。不过我不确定它是否对您的情况有用。)
如果您需要从上述一对多关系中获取逗号分隔的列表,您可以使用 MySQL 特定的 GROUP_CONCAT 函数来完成。
SELECT dataTable.*, GROUP_CONCAT(dataTags.tagName)
FROM dataTable
JOIN dataTags ON dataTags.ID=dataTable.ID
GROUP BY dataTable.ID;
剩下的就是标题和描述的全文索引。不幸的是,这确实需要您将它们放在 MyISAM 表中。
您可能还考虑将“规范”副本保留在主表中(可能在 ACID 安全的 InnoDB 表中),并存储所有标题、说明和标签的单独副本一起放在一个全文索引的 MyISAM 表中,纯粹是为了全文搜索。这确实意味着每次更改主数据时都必须进行额外更新(尽管如果您失败或必须回滚事务,至少现在错误的只是相对不重要的搜索诱饵),但优点是您可以申请对其进行额外处理,例如词干和标点处理,而 MySQL 的 FULLTEXT 索引器不会自行处理。