Как ограничить набор запросов подмножеством идентификаторов в MySQL? - PullRequest
1 голос
/ 19 июня 2020

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

USERSEARCH
-----------------------------------------------
user_id(FK)    username_ngram          name_ngram
1              "AleBoy leBoy eBoy..."  "Ale le e"
2              "craze123 raze123 ..."  "Craze raze aze ze e"
3              "john1990 ohn1990 ..."  "John ohn hn n"
4              "JJ_1 J_1 _1 1"         "JJ"


USERRELATIONSHIP
-----------------------------------------------
user_id(FK)    follows_id(FK)
2              1
2              3

Следующий запрос выполняется, поскольку кто-то только что набрал «Al»:

SELECT * FROM rage.usersearch where username_ngram like 'Al%' --1
UNION DISTINCT
SELECT * FROM rage.usersearch where name_ngram like 'Al%'  --2
UNION DISTINCT
SELECT * FROM rage.usersearch                             --3
WHERE MATCH (username_ngram, name_ngram) AGAINST ('Al')  
LIMIT 10

Индексы

index(user_id)
index(username_ngram)
index(name_ngram)
FULLTEXT(username_ngram, name_ngram)

Есть ли способ ограничить приведенный выше запрос, чтобы он смотрел только на это подмножество user_ids (без запроса их 3 раза для каждого подзапроса) ?

SELECT follows_id FROM rage.userrelationship WHERE user_id={user_id of user doing the searching} 

Ответы [ 2 ]

1 голос
/ 19 июня 2020

MySQL может использовать только один ИНДЕКС для каждой ссылки на таблицу. Он также может использовать только одно сканирование диапазона для каждого индекса. Таким образом, ни два отдельных индекса в двух столбцах, ни составной индекс в обоих столбцах не помешают полному сканированию таблицы. И индекс FULLTEXT не работает с LIKE. Лучшее, что вы можете сделать для оптимизации этого запроса, - это объединить два отдельных поиска в запросе UNION:

SELECT user_id FROM myapp.usersearch WHERE username_ngram LIKE '{string}%'
UNION DISTINCT
SELECT user_id FROM myapp.usersearch WHERE name_ngram LIKE '{string}%'

Теперь механизм может использовать INDEX(username_ngram) для первой части запроса и INDEX(name_ngram) для второй.

0 голосов
/ 21 июня 2020

Я не вижу полезности материала ngram, если вы будете получать первые несколько букв строки.

В этот момент это оптимально:

SELECT ... WHERE name LIKE 'Al%'
    LIMIT 10;

с INDEX(name).

Если вам нужно использовать UNION ALL вместе с LIMIT, сделайте следующее:

( SELECT ... ORDER BY .. LIMIT 10 )
UNION ALL
( SELECT ... ORDER BY .. LIMIT 10 )
ORDER BY .. LIMIT 10

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

Если вы будете использовать OFFSET (возможно, не для этого приложения), см. здесь, чтобы узнать, как это сделать работа: http://mysql.rjweb.org/doc.php/index_cookbook_mysql#or

...