適用場景邊界
- ✅ 關聯數量**有限且固定**(如標籤、分類、狀態集合,通常 < 20 個)。
- ✅ 關係為**一對多**,且**不需**頻繁 JOIN 聚合統計。
- ❌ 關聯數量無上限、需外鍵約束、需複雜聚合 → **請用關聯表**。
核心函數:FIND_IN_SET
FIND_IN_SET(str, strlist)
-- strlist 為逗號分隔字符串,如 '1,2,3'
-- 返回 1-N(找到位置),0(未找到/空串),NULL(任一參數為 NULL)
相關內容查詢:標籤系統範例
-- 表結構
content(id, tags) -- tags: '1,3,5'
tag(id, name) -- 1:php, 3:mysql, 5:java
-- 找出與 content.id=1 共享標籤的其他文章
SELECT * FROM content
WHERE id <> 1
AND FIND_IN_SET('3', tags) > 0; -- 查包含標籤 3 的文章
進階:全文檢索 MATCH AGAINST
-- 建立全文索引
ALTER TABLE content ADD FULLTEXT INDEX ft_tags (tags);
-- 相關性查詢(自動按匹配度排序)
SELECT *, MATCH(tags) AGAINST('1,3') AS relevance
FROM content
WHERE MATCH(tags) AGAINST('1,3')
AND id <> 1
ORDER BY relevance DESC;
全文檢索三大前置條件(必滿足)
| 條件 | 說明 | 解決 |
| FULLTEXT 索引 | MyISAM/InnoDB 5.6+ 支援 | ADD FULLTEXT INDEX |
| 最小詞長 ≥ 3 | 預設 ft_min_word_len=3,'1'、'12' 會被忽略 | ID 從 1000 起增,或改 ft_min_word_len=1 重建索引 |
| 停用詞干擾 | 預設 stopwords 含 'the','and' 等,若標籤名撞詞會被忽略 | ft_stopword_file='' 禁用停用詞表 |
效能對比
| 方案 | 優點 | 缺點 | 適用規模 |
LIKE '%tag%' | 無需索引 | 全表掃描、極慢、邊界匹配難 | ≤ 1 萬行 |
FIND_IN_SET | 語義清晰、可用索引(5.7+ 優化) | 大字符串解析開銷、無排序權重 | ≤ 10 萬行、標籤 < 20 |
| 全文檢索 | 內建相關性排序、倒排索引快 | 最小詞長/停用詞限制、維護成本 | 10 萬-百萬行 |
| 關聯表 + JOIN | 規範、支援聚合、外鍵、無上限 | JOIN 開銷、寫入需維護兩表 | 所有規模(標準方案) |
最佳實踐建議
- 📌 **新專案優先用關聯表**,符合範式、擴展性最強。
- 📌 存量專案若已用逗號欄位,**短期用
FIND_IN_SET 過渡**,長期規劃遷移關聯表。
- 📌 遷移腳本模板:
INSERT INTO content_tag (content_id, tag_id)
SELECT c.id, CAST(TRIM(t) AS UNSIGNED)
FROM content c
JOIN JSON_TABLE(
CONCAT('["', REPLACE(c.tags, ',', '","'), '"]'),
"$[*]" COLUMNS (t VARCHAR(50) PATH "$")
) jt;
- 📌 若堅持逗號欄位:**ID 從 1000 起增**、**禁用停用詞**、**建立 FULLTEXT 索引**、定期
OPTIMIZE TABLE 重建索引。