пытаясь оптимизировать MySQL запрос, но когда я добавляю ORDER BY, это занимает много времени - PullRequest
0 голосов
/ 21 января 2011

это мой запрос

SELECT U.id AS user_id,C.name AS country,
                CASE
                WHEN U.facebook_id > 0 THEN CONCAT(F.first_name,' ',F.last_name)
                WHEN U.twitter_id > 0 THEN T.name
                WHEN U.regular_id > 0 THEN CONCAT(R.first,' ',R.last)
                END AS name,
                FROM user U LEFT OUTER JOIN regular R
                ON U.regular_id = R.id
                LEFT OUTER JOIN twitter T
                ON U.twitter_id = T.id
                LEFT OUTER JOIN facebook F
                ON U.facebook_id = F.id
                LEFT OUTER JOIN country C
                ON U.country_id = C.id
                WHERE (CONCAT(F.first_name,' ',F.last_name) LIKE '%' OR T.name LIKE '%' OR CONCAT(R.first,' ',R.last) LIKE '%') AND U.active = 1
                LIMIT 100

это действительно быстро, но в ОБЪЯСНЕНИИ он не показывает мне, что использует INDEXES (есть индексы) но когда я добавляю ORDER BY 'name' до LIMIT, это занимает много времени, почему? есть способ ее решить?

таблиц: пользователи 150000, обычные 50000, facebook 50000, twitter 50000, страна 250 и растет!

Ответы [ 4 ]

1 голос
/ 26 января 2011

Вам необходимо создать первые 100 записи из каждой таблицы имен отдельно, затем объединить результаты, объединить их с user и country, упорядочить и ограничить вывод:

SELECT  u.id AS user_id, c.name AS country, n.name
FROM    (
        SELECT  facebook_id AS id, CONCAT(F.first_name, ' ', F.last_name) AS name
        FROM    facebook
        ORDER BY
                first_name, last_name
        LIMIT 100
        UNION ALL
        SELECT  twitter_id, name
        FROM    twitter
        WHERE   twitter_id NOT IN
                (
                SELECT  facebook_id
                FROM    facebook
                )
        ORDER BY
                name
        LIMIT 100
        UNION ALL
        SELECT  regular_id, CONCAT(R.first, ' ', R.last)
        FROM    regular
        WHERE   regular_id NOT IN
                (
                SELECT  facebook_id
                FROM    facebook
                )
                AND 
                regular_id NOT IN
                (
                SELECT  twitter_id
                FROM    twitter
                )
        ORDER BY
                first, last
        LIMIT 100
        ) n
JOIN    user u
ON      u.id = n.id
JOIN    country с
ON      c.id = u.country_id

Создатьследующие индексы:

facebook (first_name, last_name)
twitter (name)
regular (first, last)

Обратите внимание, что этот запрос несколько отличается от исходного: в этом запросе 'Ronnie James Dio' будет отсортирован после 'Ronnie Scott'.

1 голос
/ 21 января 2011

Это занимает много времени, потому что это составной столбец, а не столбец таблицы.Столбец name является результатом выбора регистра, и в отличие от простого выбора с множественным объединением, MySQL должен использовать другой алгоритм сортировки для данных такого типа.

Я говорю здесь по незнанию, ноВы можете сохранить данные во временной таблице, а затем отсортировать их.Это может идти быстрее, поскольку вы можете создавать индексы для него, но это будет не так быстро из-за другого типа хранилища.

ОБНОВЛЕНИЕ 2011-01-26

CREATE TEMPORARY TABLE `short_select` 
       SELECT U.id AS user_id,C.name AS country,
            CASE
            WHEN U.facebook_id > 0 THEN CONCAT(F.first_name,' ',F.last_name)
            WHEN U.twitter_id > 0 THEN T.name
            WHEN U.regular_id > 0 THEN CONCAT(R.first,' ',R.last)
            END AS name,
            FROM user U LEFT OUTER JOIN regular R
            ON U.regular_id = R.id
            LEFT OUTER JOIN twitter T
            ON U.twitter_id = T.id
            LEFT OUTER JOIN facebook F
            ON U.facebook_id = F.id
            LEFT OUTER JOIN country C
            ON U.country_id = C.id
            WHERE (CONCAT(F.first_name,' ',F.last_name) LIKE '%' OR T.name LIKE '%' OR CONCAT(R.first,' ',R.last) LIKE '%') AND U.active = 1
            LIMIT 100;

ALTER TABLE `short_select` ADD INDEX(`name`); --add successive columns if you are going to order by them as well.

SELECT * FROM `short_select`
    ORDER BY 'name'; -- same as above

Помните, что временные таблицы удаляются при прекращении соединения, поэтому у вас нет для их очистки, но вы все равно должны .

1 голос
/ 21 января 2011

Не зная структуры вашей БД и не предполагая, что у вас есть все нужные индексы для всего.Оператор Order By требует некоторого переменного количества времени для сортировки элементов, возвращаемых запросом (индекс или нет).Если это всего 10 строк, это будет казаться почти мгновенным, если вы получите 2000 строк, это будет немного медленнее, если вы сортируете 15k строк, объединенных по нескольким таблицам, для сортировки возвращаемого результата потребуется некоторое время.Также убедитесь, что вы добавляете индексы в поля, по которым вы сортируете.Возможно, вы захотите взять желаемый результат и сохранить все в предварительно отсортированной таблице заглушек для более быстрого запроса позже (если вы часто запрашиваете этот отсортированный набор результатов)

0 голосов
/ 21 января 2011

Использование функций в столбцах предотвращает использование индексов.

CONCAT(F.first_name,' ',F.last_name)

Результат функции не индексируется, хотя отдельные столбцы могут быть. Либо вам нужно переписать условия для запроса столбцов имен по отдельности, либо сохранить и проиндексировать результат этой функции (например, столбец «полное имя»).

Индекс [user.active] вряд ли поможет вам, если большинство пользователей активны.

Я не знаю, для чего предназначено ваше приложение, но мне интересно, не было ли проще, если вы исключили внешние ключи из таблицы User и вместо этого поместили UserID в качестве внешнего ключа в другие таблицы. 1008 *

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...