#mysql #full-text-search #regexp-like
#mysql #полнотекстовый поиск #подобное регулярному выражению
Вопрос:
Требование
- Порядок результатов запроса должен быть таким: результат по заголовку> результат по содержанию> результат по тегу
- Каждый результат должен быть в последнем порядке.
Я хочу сделать это в одном запросе. Как сделать эффективный запрос?
Ниже приведены запросы, которые я сделал с помощью регулярных выражений и ПОЛНОГО ТЕКСТА.
РЕГУЛЯРНОЕ ВЫРАЖЕНИЕ
(SELECT * FROM board
WHERE
title rlike 'first' AND title rlike 'second'
ORDER BY board_id DESC LIMIT 1000)
UNION
(SELECT * FROM board
WHERE
content rlike 'first' AND content rlike 'second'
ORDER BY board_id DESC LIMIT 1000)
UNION
(SELECT * FROM board
WHERE
tag rlike 'first' AND tag rlike 'second'
ORDER BY board_id DESC LIMIT 1000)
LIMIT 1000;
ПОЛНОТЕКСТОВЫЙ
(SELECT * FROM board
WHERE
match(title) AGAINST(' "first" "second"' in boolean mode)
ORDER BY board_id DESC LIMIT 1000)
UNION
(SELECT * FROM board
WHERE
match(content) AGAINST(' "first" "second"' in boolean mode)
ORDER BY board_id DESC LIMIT 1000)
UNION
(SELECT * FROM board
WHERE
match(tag) AGAINST(' "first" "second"' in boolean mode)
ORDER BY board_id DESC LIMIT 1000)
LIMIT 1000;
Как я сейчас, РЕГУЛЯРНОЕ ВЫРАЖЕНИЕ не использует индекс, но оно быстрее, чем полнотекстовое.
Я тоже не понимаю, почему это происходит.
СОЗДАТЬ ИНСТРУКЦИЮ
CREATE TABLE `board` (
`board_id` bigint NOT NULL AUTO_INCREMENT,
`user_id` bigint NOT NULL,
`nickname` varchar(255) NOT NULL,
`category` int NOT NULL,
`title` varchar(255) NOT NULL,
`content` text NOT NULL,
`likes` int NOT NULL DEFAULT '0',
`hits` int NOT NULL DEFAULT '0',
`tag` varchar(255) DEFAULT NULL,
`create_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`modify_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`board_id`),
KEY `popular` (`create_date`,`likes`),
FULLTEXT KEY `fttitle` (`title`),
FULLTEXT KEY `ftcontent` (`content`),
FULLTEXT KEY `fttag` (`tag`)
) ENGINE=InnoDB AUTO_INCREMENT=60027 DEFAULT CHARSET=utf8;
Комментарии:
1. Пожалуйста, добавьте оператор CREATE для
board
таблицы и вывод EXPLAIN для каждого из вышеуказанных запросов. Каково ожидаемое количество строк для этой таблицы? 10, 100, 1000 или 1 000 000 строк? Я предполагаюboard_id
, что это PK дляboard
таблицы, что означало бы, что ваши предложения GROUP BY не нужны.2. @nnichols Я добавил инструкцию create и протестировал ее примерно с 60000 строками.
Ответ №1:
Регулярное выражение и объединение не будут в каком-либо определенном порядке.
В большинстве формулировок было бы лучше сначала получить идентификаторы в объединениях, а затем ОБЪЕДИНИТЬ, чтобы получить остальные столбцы из нескольких строк, которые получаются в результате.
Имейте в виду, что UNION
это означает UNION DISTINCT
и имеет дедуплицирующий проход. (Вероятно, это то, что вы хотите, хотя это медленнее, чем UNION ALL
.)
Большинство из ORDER BY
показанных вами бесполезны — они ничего не сделают, кроме (возможно) пустой траты времени. ORDER BY
и LIMIT
должны быть сопряжены для каждого подзапроса. А затем снова после выполнения UNION
. Связанная тема: http://mysql.rjweb.org/doc.php/index_cookbook_mysql#or
ответ nnichols не дедуплицируется. Это можно исправить с помощью дополнительного (внешнего) выбора, который группирует ответы и выбирает MIN(col_sort)
с GROUP BY id
, прежде чем выполнять порядок.
Еще
SELECT c.*
FROM (
SELECT id, MIN(col_sort) AS col_sort2
FROM (
SELECT id, 1 AS col_sort
FROM ...
WHERE MATCH ...
LIMIT 100
UNION ALL
SELECT id, 2 AS col_sort
FROM ...
WHERE MATCH ...
LIMIT 100
UNION ALL
SELECT id, 3 AS col_sort
FROM ...
WHERE MATCH ...
LIMIT 100
) AS a
GROUP BY id
) AS b
JOIN board AS c USING(id)
ORDER BY b.col_sort2
LIMIT 100;
Не используйте огромное число, например 1000, для ОГРАНИЧЕНИЯ. Довольно сложно поддерживать «релевантность» с помощью запросов.
Комментарии:
1. Не могли бы вы ввести пример запроса?
2. Также в моих запросах не могли бы вы сообщить мне, почему РЕГУЛЯРНОЕ ВЫРАЖЕНИЕ быстрее, чем ПОЛНОТЕКСТОВОЕ?
3. @Jacob — я безуспешно пытаюсь выполнить запрос.
FULLTEXT
является «перевернутым» индексом — при наличии «слова» у него есть список строк, содержащих это слово.REGEXP
необходимо сканировать каждую строку, следовательно, намного медленнее.4. Я имею в виду, что РЕГУЛЯРНОЕ ВЫРАЖЕНИЕ быстрее, чем ПОЛНОТЕКСТОВОЕ в моих запросах. РЕГУЛЯРНОЕ ВЫРАЖЕНИЕ занимает 0,21340025 секунды, но ПОЛНЫЙ ТЕКСТ занимает 0,68861400 секунды с 60000 строками. Теоретически ПОЛНОТЕКСТОВЫЙ текст должен быть быстрее, но это не так. Я хочу знать, почему.
5. Я протестировал ваш запрос, но он занимает 0,48452500 секунды.
Ответ №2:
Ваш существующий ПОЛНОТЕКСТОВЫЙ запрос предлагает отдельные полнотекстовые индексы для трех текстовых столбцов. Исходя из этого, я бы, вероятно, начал с —
(
SELECT *, 1 AS col_sort
FROM board
WHERE match(title) AGAINST(' "first" "second"' in boolean mode)
) UNION (
SELECT *, 2 AS col_sort
FROM board
WHERE match(content) AGAINST(' "first" "second"' in boolean mode)
) UNION (
SELECT *, 3 AS col_sort
FROM board
WHERE match(tag) AGAINST(' "first" "second"' in boolean mode)
)
ORDER BY col_sort ASC;
Разница в производительности между использованием RLIKE и ПОЛНОТЕКСТОВЫМ поиском будет зависеть от размера набора данных, как размеров столбцов, так и количества строк.
Комментарии:
1. Я протестировал ваш запрос, но он работает медленнее, чем мой запрос. Возможно, это из-за дополнительного столбца.
2. Пожалуйста, добавьте выходные данные
EXPLAIN
для ваших двух запросовSHOW INDEX FROM board;
иSELECT COUNT(*) AS total_rows, MIN(LENGTH(title)) AS min_title, MAX(LENGTH(title)) AS max_title, AVG(LENGTH(title)) AS avg_title, MIN(LENGTH(content)) AS min_content, MAX(LENGTH(content)) AS max_content, AVG(LENGTH(content)) AS avg_content, MIN(LENGTH(tag)) AS min_tag, MAX(LENGTH(tag)) AS max_tag, AVG(LENGTH(tag)) AS avg_tag FROM board;
Количество элементов и диапазон размеров будут оказывать значительное влияние на производительность. Версия RickJames занимает около 0,09 в моих 144 тыс. строк3. @nnichols — Интересно, вернулись ли ваши тестовые данные с намного меньшим количеством строк, чем у OP.