MySQL索引与全文索引

问题描述

我对全文索引有问题,并尝试同时使用索引提示。 这是一个示例表:

CREATE TABLE IF NOT EXISTS `products` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,`idcategory` bigint(20) NOT NULL,`name` text NOT NULL,`short_description` text,`description` longtext NOT NULL,PRIMARY KEY (`id`),KEY `idcategory` (`idcategory`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

ALTER TABLE `products` ADD FULLTEXT INDEX `name_fulltext` (`name`);
ALTER TABLE `products` ADD FULLTEXT INDEX `short_description_fulltext` (`short_description`);
ALTER TABLE `products` ADD FULLTEXT INDEX `description_fulltext` (`description`);

查询运行良好:

SELECT *
FROM products USE INDEX (idcategory)
WHERE idcategory = 1

查询运行良好:

SELECT *
FROM products AS p
WHERE p.idcategory = 1
AND (
    MATCH(p.name) AGAINST ("*test*" IN BOOLEAN MODE)
    OR MATCH(p.short_description) AGAINST ("*test*" IN BOOLEAN MODE)
    OR MATCH(p.description) AGAINST ("*test*" IN BOOLEAN MODE)
)

查询错误

SELECT *
FROM products AS p USE INDEX (idcategory)
WHERE p.idcategory = 1
AND (
    MATCH(p.name) AGAINST ("*test*" IN BOOLEAN MODE)
    OR MATCH(p.short_description) AGAINST ("*test*" IN BOOLEAN MODE)
    OR MATCH(p.description) AGAINST ("*test*" IN BOOLEAN MODE)
)

Error: #1191 - Can't find FULLTEXT index matching the column list

我不明白为什么不使用索引以及为什么要寻找匹配的FULTEXT索引。 匹配什么?

从文档(https://dev.mysql.com/doc/refman/8.0/en/index-hints.html):

对于布尔模式搜索,带有FOR ORDER BY或FOR GROUP BY的索引提示将被忽略。接受带有FOR JOIN或没有FOR修饰符的索引提示。与提示如何应用于非FULLTEXT搜索相反,该提示用于查询执行的所有阶段(查找行和检索,分组和排序)。即使对非FULLTEXT索引给出了提示,也是如此。

那是什么问题? 它说即使在FULLTEXT搜索的情况下为非FULLTEXT索引给出了提示,该索引也是已获认可的。 我想念什么吗?我会误解文档吗?

谢谢阅读。

解决方法

**已编辑11/10 **

放开手,开始摆弄自己。经过一些研究和测试,我整理出了这组查询,希望可以帮助您进一步了解。 (我使用了dbfiddle。请注意,失败的查询已被注释掉,因为如果至少保留在dbfiddle上,其他任何内容都将无法运行。)

尝试运行这些命令,希望输出和注释能最好地解释。特别是,查询集7和8显示了如何设置多列FULLTEXT索引,因为我认为这可能是您最终的意图。

-------------------------------- Query Set 1 ------------------------------------
-- Only possible key is `idcategory`,because each FULLTEXT INDEX is for a single 
-- column,but using 3 different MATCH clauses,each for a different column. 
-- (i.e. Can't index into all 3 simultaneously using only a single column index.)
-- Keys/Indexes on `idcategory`.

DROP TABLE IF EXISTS `products`;

CREATE TABLE `products` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,`idcategory` bigint(20) NOT NULL,`name` text NOT NULL,`short_description` text,`description` longtext NOT NULL,PRIMARY KEY (`id`),KEY `idcategory` (`idcategory`),FULLTEXT INDEX `name_fulltext` (`name`),FULLTEXT INDEX `short_description_fulltext` (`short_description`),FULLTEXT INDEX `description_fulltext` (`description`)  
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


INSERT INTO `products` (`idcategory`,`name`,`short_description`,`description`)
VALUES (1,'test1','sdesc1','desc1'),(1,'test2','sdesc2','desc2'),(2,'test3','sdesc3','desc3');

EXPLAIN SELECT *
FROM products AS p
WHERE p.idcategory = 1
AND (
    MATCH(p.name) AGAINST ("*test*" IN BOOLEAN MODE)
    OR MATCH(p.short_description) AGAINST ("*test*" IN BOOLEAN MODE)
    OR MATCH(p.description) AGAINST ("*test*" IN BOOLEAN MODE)
);

SELECT *
FROM products AS p
WHERE p.idcategory = 1
AND (
    MATCH(p.name) AGAINST ("*test*" IN BOOLEAN MODE)
    OR MATCH(p.short_description) AGAINST ("*test*" IN BOOLEAN MODE)
    OR MATCH(p.description) AGAINST ("*test*" IN BOOLEAN MODE)
);

-------------------------------- Query Set 2 ------------------------------------

-- Possible keys are `idcategory` and `name_fulltext`. All FULLTEXT INDEXES are 
-- single column,and only 1 MATCH clause [for a single column,`name`] is 
-- specified. Keys/Indexes on `name_fulltext`. (When faced with a choice of 
-- multiple indexes,the optimizer will choose the most selective option [i.e. 
-- that option with the lowest number of rows]).

DROP TABLE IF EXISTS `products`;

CREATE TABLE `products` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,'desc3');

EXPLAIN SELECT *
FROM products AS p
WHERE p.idcategory = 1
AND (
    MATCH(p.name) AGAINST ("*test*" IN BOOLEAN MODE)
);

SELECT *
FROM products AS p
WHERE p.idcategory = 1
AND (
    MATCH(p.name) AGAINST ("*test*" IN BOOLEAN MODE)
);

-------------------------------- Query Set 3 ------------------------------------

-- Same as Query Set 2,but explicitly call for INDEX `name_fulltext`. Now only
-- possible key is name_fulltext.

DROP TABLE IF EXISTS `products`;

CREATE TABLE `products` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,'desc3');

EXPLAIN SELECT *
FROM products AS p USE INDEX (`name_fulltext`)
WHERE p.idcategory = 1 
AND (
    MATCH(p.name) AGAINST ("*test*" IN BOOLEAN MODE)
);

SELECT *
FROM products AS p USE INDEX (`name_fulltext`)
WHERE p.idcategory = 1
AND (
    MATCH(p.name) AGAINST ("*test*" IN BOOLEAN MODE)
);

-------------------------------- Query Set 4 ------------------------------------
----------------------------------- FAILS ---------------------------------------
-- Same as Query Set 1 but trying to force INDEX `name_fulltext`. Doesn't work
-- because `name_fulltext` only indexes a single column,but MATCH / AGAINST
-- has three different clauses,each specifying a different column. (And hence
-- why the only possible key is `idcategory`,as shown in output of EXPLAIN,-- Query Set 1.)

--DROP TABLE IF EXISTS `products`;

--CREATE TABLE `products` (
--  `id` bigint(20) NOT NULL AUTO_INCREMENT,--  `idcategory` bigint(20) NOT NULL,--  `name` text NOT NULL,--  `short_description` text,--  `description` longtext NOT NULL,--  PRIMARY KEY (`id`),--  KEY `idcategory` (`idcategory`),--  FULLTEXT INDEX `name_fulltext` (`name`),--  FULLTEXT INDEX `short_description_fulltext` (`short_description`),--  FULLTEXT INDEX `description_fulltext` (`description`)  
--) ENGINE=InnoDB DEFAULT CHARSET=utf8;


--INSERT INTO `products` (`idcategory`,`description`)
--VALUES (1,'desc3');

--EXPLAIN SELECT *
--FROM products AS p USE INDEX (`name_fulltext`)
--WHERE p.idcategory = 1
--AND (
--    MATCH(p.name) AGAINST ("*test*" IN BOOLEAN MODE)
--    OR MATCH(p.short_description) AGAINST ("*test*" IN BOOLEAN MODE)
--    OR MATCH(p.description) AGAINST ("*test*" IN BOOLEAN MODE)
--);

--SELECT *
--FROM products AS p USE INDEX (`name_fulltext`)
--WHERE p.idcategory = 1
--AND (
--    MATCH(p.name) AGAINST ("*test*" IN BOOLEAN MODE)
--    OR MATCH(p.short_description) AGAINST ("*test*" IN BOOLEAN MODE)
--    OR MATCH(p.description) AGAINST ("*test*" IN BOOLEAN MODE)
--);


-------------------------------- Query Set 5 ------------------------------------
----------------------------------- FAILS ---------------------------------------

-- Trying to force keying/indexing on `idcategory` instead of `name_fulltext`. 
-- Doesn't work because `idcategory` is not established as a FULLTEXT INDEX,-- which is required for MATCH / AGAINST? It keyed on this field when not 
-- explicitly calling USE INDEX and no other index was possible,and when
-- explicitly calling USE INDEX but with no MATCH / AGAINST call,so
-- this behavior seems a bit inconsistent.

--DROP TABLE IF EXISTS `products`;

--CREATE TABLE `products` (
--  `id` bigint(20) NOT NULL AUTO_INCREMENT,'desc3');

--EXPLAIN SELECT *
--FROM products AS p USE INDEX (`idcategory`)
--WHERE p.idcategory = 1 
--AND (
--    MATCH(p.name) AGAINST ("*test*" IN BOOLEAN MODE)
--);

--SELECT *
--FROM products AS p USE INDEX (`idcategory`)
--WHERE p.idcategory = 1
--AND (
--   MATCH(p.name) AGAINST ("*test*" IN BOOLEAN MODE)
--);

-------------------------------- Query Set 6 ------------------------------------
----------------------------------- FAILS ---------------------------------------

-- We can't make `idcategory` a FULLTEXT INDEX of course,because it is a 
-- bigint(20). (Could try making this KEY a text field instead,-- see if error persists...)

--DROP TABLE IF EXISTS `products`;

--CREATE TABLE `products` (
--  `id` bigint(20) NOT NULL AUTO_INCREMENT,--  FULLTEXT INDEX `idcategory_fulltext` (`idcategory`) 
--) ENGINE=InnoDB DEFAULT CHARSET=utf8;


--INSERT INTO `products` (`idcategory`,'desc3');

--EXPLAIN SELECT *
--FROM products AS p USE INDEX (`idcategory`)
--WHERE p.idcategory = 1 
--AND (
--    MATCH(p.name) AGAINST ("*test*" IN BOOLEAN MODE)
--);

--SELECT *
--FROM products AS p USE INDEX (`idcategory`)
--WHERE p.idcategory = 1
--AND (
--    MATCH(p.name) AGAINST ("*test*" IN BOOLEAN MODE)
--);

-------------------------------- Query Set 7 ------------------------------------

-- A multi-column index. The MATCH / AGAINST search functions like an 'OR' 
-- clause for all three columns. (i.e. if the text pattern is present in any
-- of the three columns,a match is found). Must specify all three columns in MATCH
-- clause,because the FULLTEXT INDEX includes all three.
-- Keys/Indexes on 'name_sdesc_desc_fulltext` (multi-column fulltext index),-- but shows `idcategory` as possible index.
-- (Perhaps when all is said and done this is the desired behavior?)

DROP TABLE IF EXISTS `products`;

CREATE TABLE `products` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,FULLTEXT INDEX `name_sdesc_desc_fulltext` (`name`,`description`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


INSERT INTO `products` (`idcategory`,'desc3');

EXPLAIN SELECT *
FROM products AS p
WHERE p.idcategory = 1
AND (
    MATCH(p.name,p.short_description,p.description) AGAINST ("*test*" IN BOOLEAN MODE)
);

SELECT *
FROM products AS p
WHERE p.idcategory = 1
AND (
    MATCH(p.name,p.description) AGAINST ("*test*" IN BOOLEAN MODE)
);

-------------------------------- Query Set 8 ------------------------------------

-- Same as Query Set 7,but explicitly calls for `name_sdesc_desc` FULLTEXT INDEX.
-- Now,only possible index is `name_sdesc_desc_fulltext`.

DROP TABLE IF EXISTS `products`;

CREATE TABLE `products` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,'desc3');

EXPLAIN SELECT *
FROM products AS p USE INDEX (`name_sdesc_desc`)
WHERE p.idcategory = 1
AND (
    MATCH(p.name,p.description) AGAINST ("*test*" IN BOOLEAN MODE)
);

SELECT *
FROM products AS p USE INDEX (`name_sdesc_desc`)
WHERE p.idcategory = 1
AND (
    MATCH(p.name,p.description) AGAINST ("*test*" IN BOOLEAN MODE)
);