MySQL поиск по нескольким индексам - PullRequest
0 голосов
/ 02 октября 2018

Я унаследовал большую, некрасивую базу данных MySQL 5.7 и собираюсь оптимизировать некоторые запросы.

В частности, пользователь хочет найти 7 разных var_char столбцов ... в 7 разных таблицах.

Очевидно, я могу объединить 7 таблиц и отфильтровать для LIKE '%search term%', но я бы хотел использовать MATCH...AGAINST a FULLTEXT index.

Я создал индексы:

ALTER TABLE table1 ADD FULLTEXT INDEX (origname);
ALTER TABLE table2 ADD FULLTEXT INDEX (byline);
ALTER TABLE table3 ADD FULLTEXT INDEX (copyright);
ALTER TABLE table4 ADD FULLTEXT INDEX (source);
ALTER TABLE table5 ADD FULLTEXT INDEX (image_title);
ALTER TABLE table6 ADD FULLTEXT INDEX (photo_info);
ALTER TABLE table7 ADD FULLTEXT INDEX (alt_text);

Я попробовал следующее, но производительность была ужасной ... есть ли хороший способ выполнить полнотекстовый поиск по нескольким таблицам?

SELECT
    MATCH(table1.name) AGAINST ('search term') as name,
    MATCH(table2.byline) AGAINST ('search term') as byline,
    MATCH(table3.copyright) AGAINST ('search term') as copyright,
    MATCH(table4.source) AGAINST ('search term') as copyright,
    MATCH(table5.image_title) AGAINST ('search term') as image_title,
    MATCH(table6.photo_info) AGAINST ('search term') as photo_info,
    MATCH(table7.alt_text) AGAINST ('search term') as alt_text
FROM table1
LEFT JOIN table2 on table2.entity_id = table1.fid
LEFT JOIN table3 on table3.entity_id = table1.fid
LEFT JOIN table4 on table4.entity_id = table1.fid
LEFT JOIN table5 on table5.entity_id = table1.fid
LEFT JOIN table6 on table6.entity_id = table1.fid
LEFT JOIN table7 ON table7.alt_text = table1.fid
WHERE
    MATCH(table1.name) AGAINST ('search term') OR
    MATCH(table2.byline) AGAINST ('search term') OR
    MATCH(table3.copyright) AGAINST ('search term') OR
    MATCH(table4.source) AGAINST ('search term') OR
    MATCH(table5.image_title) AGAINST ('search term') OR
    MATCH(table6.photo_info) AGAINST ('search term') OR
    MATCH(table7.alt_text) AGAINST ('search term');

В идеале я бы просто использовал Elasticsearch / Lucene/ Solr и т. Д., Но давайте на минутку предположим, что я не могу использовать ни одну из тех вещей, что я ограничен MySQL.Есть ли хороший способ сделать это?

Вот вывод EXPLAIN EXTENDED:

id  select_type table   partitions  type    possible_keys   key key_len ref rows    filtered    Extra   
1   SIMPLE  table1  NULL    ALL NULL    NULL    NULL    NULL    557535  100 NULL    
1   SIMPLE  table2  NULL    ref entity_id   entity_id   4   mydb.table1.fid 1   100 NULL    
1   SIMPLE  table3  NULL    ref entity_id   entity_id   4   mydb.table1.fid 1   100 NULL    
1   SIMPLE  table4  NULL    ref entity_id   entity_id   4   mydb.table1.fid 1   100 NULL    
1   SIMPLE  table5  NULL    ref entity_id   entity_id   4   mydb.table1.fid 1   100 NULL    
1   SIMPLE  table6  NULL    ref entity_id   entity_id   4   mydb.table1.fid 1   100 NULL    
1   SIMPLE  table7  NULL    ALL field_file_image_alt_text_value NULL    NULL    NULL    203374  100 Using where 

==========================================

Ответ : приведенное ниже решение Bomar является правильным подходом, но MySQL 5.7 не помогне нравится его синтаксис.Мне пришлось кодировать это следующим образом:

ВЫБРАТЬ fid, scoredName AS, origname AS "Имя файла", NULL AS Byline, NULL AS Copyright, NULL AS Source, NULL AS "Заголовок изображения", NULL AS "Информация о фотографии ", NULL AS" Alt text "

    SELECT fid, MATCH(origname) AGAINST ('search term') AS scoredName, origname, NULL AS N4, NULL AS N5, NULL AS N6, NULL AS N7, NULL AS N8, NULL AS N9
    FROM table1
    WHERE MATCH(origname) AGAINST ('search term')

    UNION

    SELECT entity_id, MATCH(byline) AGAINST ('search term') AS scoredByline, NULL AS N3, byline, NULL AS N5, NULL AS N6, NULL AS N7, NULL AS N8, NULL AS N9
    FROM table2   
    WHERE MATCH (byline) AGAINST ('search term')

    UNION

    SELECT entity_id, MATCH(copyright) AGAINST ('search term') AS scoredCopyright, NULL AS N3, NULL AS N4, copyright, NULL AS N6, NULL AS N7, NULL AS N8, NULL AS N9
    FROM table3
    WHERE MATCH (copyright) AGAINST ('search term')

    UNION

    SELECT entity_id, MATCH(source) AGAINST ('search term') AS scoredSource, NULL AS N3, NULL AS N4, NULL AS N5, source, NULL AS N7, NULL AS N8, NULL AS N9
    FROM table4
    WHERE MATCH (source) AGAINST ('search term')

    UNION

    SELECT entity_id, MATCH(image_title) AGAINST ('search term') AS scoredTitle, NULL AS N3, NULL AS N4, NULL AS N5, NULL AS N6, image_title, NULL AS N8, NULL AS N9
    FROM table5
    WHERE MATCH(image_title) AGAINST ('search term')

    UNION

    SELECT entity_id, MATCH(full_photo_info) AGAINST ('search term') AS scoredPhotoInfo, NULL AS N3, NULL AS N4, NULL AS N5, NULL AS N6, NULL AS N7, full_photo_info, NULL AS N9
    FROM table6
    WHERE MATCH(full_photo_info) AGAINST ('search term')

    UNION

    SELECT entity_id, MATCH(file_image_alt_text) AGAINST ('search term') as scoredaltText, NULL AS N3, NULL AS N4, NULL AS N5, NULL AS N6, NULL AS N7, NULL AS N8, file_image_alt_text
    FROM table7
    WHERE MATCH(file_image_alt_text) AGAINST ('search term')
) AS g
GROUP BY fid, score, origname, byline, copyright, source, "Image title", "Photo info", "Alt text"
ORDER BY score DESC;

Для объяснения: UNION просто объединяет последующие запросы в столбцы, заданные первым запросом.

Следовательно, все онидолжно иметь одинаковое количество столбцов, и поэтому вы видите все эти значения NULL.

Но у вас не может быть двух столбцов с одинаковыми именами, поэтому вы должны иметь псевдоним каждый NULL.

Далее я, вероятно, взвешиваю различные индексы, умножая более важные на постоянные.

Ответы [ 2 ]

0 голосов
/ 04 октября 2018

Создайте одну таблицу, целью которой является выполнение таких поисков.Он будет иметь один текстовый столбец, который собирает все слова из различных других таблиц, и иметь для них идентификатор JOINing.

Результирующий запрос:

SELECT ...
    FROM search_table AS st
    WHERE MATCH (st.txt) AGAINST (... IN BOOLEAN MODE)
    JOIN table1 USING(id)
    JOIN table2 USING(id)  -- or whatever is needed for the JOIN ON
    JOIN table3 USING(id)
    ...;

Предоставлено,Требуется, чтобы вы заполняли search_table при заполнении любой другой таблицы.Однако он не требует от вас очистки, если вы удаляете какой-либо идентификатор.JOIN просто не найдет строку в другой таблице.

Для txt будет установлено что-то вроде

INSERT INTO `search_table` (id, txt)
    VALUES (id,
            CONCAT_WS(' ', table1.origname,
                           table2.byline,
                            ... ) );
0 голосов
/ 02 октября 2018

OR затрудняет оптимизацию MySQL.Используйте отдельные запросы, которые объединяются с UNION.

. Предполагается, что для каждой entity_id в каждой из связанных таблиц имеется только одна строка.Если нет, исходный запрос возвращает перекрестное произведение всех совпадающих строк, в то время как это просто возвращает максимальное совпадение из каждой таблицы.

SELECT fid, MAX(name) AS name, MAX(byline) AS byline, MAX(copyright) AS copyright, MAX(source) AS source, MAX(image_title) AS image_title, MAX(photo_info) AS photo_info, MAX(alt_text) AS alt_text
    FROM (
    SELECT fid, MATCH(name) AGAINST ('search term') as name, NULL AS byline, NULL AS copyright, NULL AS source, NULL AS image_title, NULL AS photo_info, NULL AS alt_text
    FROM table1
    WHERE MATCH(name) AGAINST ('search term')
    UNION
    SELECT entity_id, NULL, MATCH(byline) AGAINST ('search term'), NULL, NULL, NULL, NULL, NULL
    FROM table2
    WHERE MATCH(byline) AGAINST ('search term')
    UNION
    SELECT entity_id, NULL, NULL, MATCH(copyright) AGAINST ('search term'), NULL, NULL, NULL, NULL
    FROM table3
    WHERE MATCH(copyright) AGAINST ('search term')
    UNION
    SELECT entity_id, NULL, NULL, NULL, MATCH(source) AGAINST ('search term'), NULL, NULL, NULL
    FROM table4
    WHERE MATCH(source) AGAINST ('search term')
    UNION
    SELECT entity_id, NULL, NULL, NULL, NULL, MATCH(image_title) AGAINST ('search term'), NULL, NULL
    FROM table5
    WHERE MATCH(image_title) AGAINST ('search term')
    UNION
    SELECT entity_id, NULL, NULL, NULL, NULL, NULL, MATCH(photo_info) AGAINST ('search term'), NULL
    FROM table6
    WHERE MATCH(photo_info) AGAINST ('search term')
    UNION
    SELECT entity_id, NULL, NULL, NULL, NULL, NULL, NULL, MATCH(alt_text) AGAINST ('search term')
    FROM table7
    WHERE MATCH(alt_text) AGAINST ('search term')
) AS u
GROUP BY fid
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...