** 11/10 编辑**
崩溃并开始摆弄自己。经过一些研究和测试,我整理了这组查询,希望能帮助阐明一些额外的信息。 (我使用了dbfiddle。请注意,失败的查询会被注释掉,因为如果留在其中,则不会运行其他任何东西,至少在dbfiddle 上。)
尝试运行这些,希望输出和 cmets 能最好地解释。特别是,查询集 7 和 8 展示了如何设置多列 FULLTEXT INDEX,因为我认为这可能是您的最终意图。
-------------------------------- 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,
`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)
);
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,
`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 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`, `name`, `short_description`, `description`)
--VALUES (1, 'test1', 'sdesc1', 'desc1'), (1, 'test2', 'sdesc2', 'desc2'), (2, 'test3', 'sdesc3', '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,
-- `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 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,
-- `idcategory` bigint(20) NOT NULL,
-- `name` text NOT NULL,
-- `short_description` text,
-- `description` longtext NOT NULL,
-- PRIMARY KEY (`id`),
-- KEY `idcategory` (`idcategory`),
-- FULLTEXT INDEX `idcategory_fulltext` (`idcategory`)
--) 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 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,
`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_sdesc_desc_fulltext` (`name`, `short_description`, `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, p.short_description, p.description) AGAINST ("*test*" IN BOOLEAN MODE)
);
SELECT *
FROM products AS p
WHERE p.idcategory = 1
AND (
MATCH(p.name, p.short_description, 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,
`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_sdesc_desc_fulltext` (`name`, `short_description`, `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 USE INDEX (`name_sdesc_desc`)
WHERE p.idcategory = 1
AND (
MATCH(p.name, p.short_description, 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.short_description, p.description) AGAINST ("*test*" IN BOOLEAN MODE)
);