Как добиться функциональности CTE в MySQL 5 .7? - PullRequest
3 голосов
/ 20 июня 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 myapp.usersearch where username_ngram like 'Al%'
        UNION DISTINCT
        SELECT * FROM myapp.usersearch where name_ngram like 'Al%'
        UNION DISTINCT
        SELECT * FROM myapp.usersearch                            
        WHERE MATCH (username_ngram, name_ngram) AGAINST ('Al')  
        LIMIT 10

Это невероятно быстро из-за существующих индексов в username_ngram, name_ngram и FULLTEXT (username_ngram, name_ngram). Однако в контексте моего приложения мне нужно ограничить поиск пользователями, за которыми следует поисковик. Я хотел бы заменить таблицу «myapp.usersearch» подмножеством таблицы «myapp.usersearch», включая только пользователей, за которыми следит поисковик. Вот что я попытался:

    WITH

--Part 1, restrict the USERSEARCH table to just the users that are followed by searcher

        tempUserSearch AS (SELECT T1.id, T2.username_ngram, T2.name_ngram FROM
        (SELECT follows_id FROM myapp.userrelationship WHERE user_id = {user_idOfSearcher} ) AS T1 
        LEFT JOIN myapp.usersearch AS T2  ON T2.user_id = T1.follows_id)

            SELECT * FROM tempUserSearch where username_ngram like 'Al%'
            UNION DISTINCT
            SELECT * FROM tempUserSearch where name_ngram like 'Al%'
            UNION DISTINCT
            SELECT * FROM tempUserSearch                            
            WHERE MATCH (username_ngram, name_ngram) AGAINST ('Al')  
            LIMIT 10

К сожалению, MySQL 5.7 не поддерживает предложение CTE WITH.

Есть ли способ ссылаться на часть 1 запроса во всех последующих подзапросах без повторного запроса user_ids пользователей, за которыми следует человек? (в MySQL 5.7)

Обновление:

Действительно ли нет возможности ссылаться на запрос несколько раз в MySQL 5.7? Что-то кажется неправильным, поскольку это кажется мне фундаментальной задачей для любого db.

Почему бы не сделать: «x соединяется с y на a или b или c»? Скорость моего запроса подстроки зависит от следующих индексов:

index(username_ngram)

index(name_ngram)

FULLTEXT(username_ngram, name_ngram)

И использование OR не помогает никаким индексам.

1 Ответ

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

MySQL 5.7 не поддерживает общее табличное выражение; синтаксис WITH доступен только в версии 8.0.

Поскольку ваш существующий запрос выполняется быстро, фильтрация во внешнем запросе может быть жизнеспособным решением:

SELECT ur.id, ng.username_ngram, ng.name_ngram
FROM myapp.userrelationship ur
INNER JOIN (
    SELECT * FROM myapp.usersearch WHERE username_ngram LIKE 'Al%'
    UNION DISTINCT
    SELECT * FROM myapp.usersearch WHERE name_ngram LIKE 'Al}%'
    UNION DISTINCT
    SELECT * FROM myapp.usersearch WHERE MATCH (username_ngram, name_ngram) AGAINST ('Al')  
) ng ON ng.user_id = ur.follows_id
WHERE ur.user_id = {user_idOfSearcher}
ORDER BY ??
LIMIT 10

Примечания:

  • Я превратил LEFT JOIN в INNER JOIN, потому что я думаю, что это ближе к тому, что вы хотите (вы можете изменить его обратно, если он вам не подходит)

  • Вам необходимо предложение ORDER BY для go вместе с LIMIT, иначе результаты не будут детерминированными c, если в наборе результатов больше 10 строк

...