2009-10-22 4 views
0

Этот запрос выполняется очень медленно. Принимает между 9 и 10 секунд ...Любой способ ускорить этот запрос?

SELECT DISTINCT a.* 
FROM addresses a 
LEFT JOIN contacts c 
ON c.id = a.contact_id 
LEFT JOIN organizations o 
ON o.id = a.organization_id 
ORDER BY c.last_name, c.first_name, o.name 
LIMIT 0, 24 

Если я закомментируйте пункт ORDER BY запрос работает гораздо быстрее - около 5 миллисекунд. Но мне нужен ORDER BY для поддержки поиска поисковых запросов. И пользователям нужны адреса для сортировки по контактам и организации.


Структура таблицы

addresses 
--------- 
id int NOT NULL 
contact_id int  # could be NULL 
organization_id int # could be NULL 

contacts 
-------- 
id int NOT NULL 
first_name varchar(255) 
last_name varchar(255) 

organizations 
------------- 
id int NOT NULL 
name varchar(255) 

Они все таблицы InnoDB.

У меня есть эти индексы на контакты таблице:

KEY `idx_contacts_first_name` (`first_name`), 
    KEY `idx_contacts_last_name` (`last_name`), 
    KEY `idx_contacts_first_name_last_name` (`first_name`,`last_name`) 

И на организации таблицы:

KEY `idx_organization_name` (`name`) 

Количество данных

Addresses:  22,271 
Contacts:  17,906 
Organizations: 8,246 

ОПИСАТЬ OUTPUT

mysql> DESCRIBE 
    -> SELECT DISTINCT a.* 
    -> FROM addresses a 
    -> LEFT JOIN contacts c 
    -> ON c.id = a.contact_id 
    -> LEFT JOIN organizations o 
    -> ON o.id = a.organization_id 
    -> ORDER BY c.last_name, c.first_name, o.name 
    -> LIMIT 0, 24; 
+----+-------------+-------+--------+---------------+---------+---------+--------------------------------------------+-------+---------------------------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref          | rows | Extra       | 
+----+-------------+-------+--------+---------------+---------+---------+--------------------------------------------+-------+---------------------------------+ 
| 1 | SIMPLE  | a  | ALL | NULL   | NULL | NULL | NULL          | 22387 | Using temporary; Using filesort | 
| 1 | SIMPLE  | c  | eq_ref | PRIMARY  | PRIMARY | 4  | contactdb_v2_development.a.contact_id  |  1 | Distinct      | 
| 1 | SIMPLE  | o  | eq_ref | PRIMARY  | PRIMARY | 4  | contactdb_v2_development.a.organization_id |  1 | Distinct      | 
+----+-------------+-------+--------+---------------+---------+---------+--------------------------------------------+-------+---------------------------------+ 
3 rows in set (0.00 sec) 

ответ

1

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

+0

Что вы подразумеваете под «сделайте свой заказ и подкачку на этом уровне»? Вы предлагаете выполнить сортировку в коде приложения после извлечения данных из БД? – sleske

+0

Да. Для набора данных в 20 000 строк, который, вероятно, не изменится так сильно, было бы целесообразно вытащить данные, отсортировать их и кешировать. –

+0

Да, я думаю, что буду использовать что-то вроде этого. Может показаться странным получить все записи и предложить их для подкачки, но это не приведет к болезненным сложностям интерфейса. – Ethan

1

Попробуйте добавить этот индекс:

idx_contacts_last_name_first_name (last_name, first_name)

BTW: Вы можете удалить idx_contacts_first_name, так как он дублирует и если добавить этот индекс можно удалить idx_contacts_last_name.

1

Попробуйте изменить SQL на что-то вроде следующего:

SELECT a.column1, a.column2, ... 
FROM addresses a 
LEFT JOIN contacts c 
ON c.id = a.contact_id 
LEFT JOIN organizations o 
ON o.id = a.organization_id 
GROUP BY a.column1, a.column2, ... 
ORDER BY c.last_name, c.first_name, o.name 
LIMIT 0, 24 

Я нашел GROUP BY гораздо быстрее, чем DISTINCT вообще, хотя я не знаю почему это было бы.

2

Я попробовал ваш пример с похожим объемом данных, а на моем скромном ноутбуке (Pentium M 1,7 ГГц) запрос занимает менее секунды (при первом запуске, а затем работает еще меньше).

Вы случайно случайно забыли ПК в колонке id? Вы не упомянули об этом, поэтому просто спрашивайте ... если вы забудете это, производительность, очевидно, будет ужасной - не говоря уже о том, что каждый администратор базы данных будет сжиматься за столами без ПК.

В противном случае, попробуйте следующее:

DESCRIBE <your query> 

Это даст вам план запроса в MySQL.Опубликуйте, что (отредактируйте свой вопрос), и должно быть яснее, что так долго.

О дальнейшей мысли:

Запрос всегда будет проблематичная производительность, поскольку вы просите базу данных для чтения и сортировок всех адресов и отображать их. ORDER BY означает, что он должен читать все, прежде чем что-либо вернуть, поэтому он будет всегда медленным. В чем смысл разбора всей базы данных? Будут ли пользователи просматривать несколько тысяч записей?

Рассмотрите, например, разрешая поисковый запрос. С условием WHERE запрос будет намного быстрее.

+0

Проверенные ПК. Они установлены. Я вставил вывод DESCRIBE. Приложение предоставляет форму поиска (точнее, форму фильтрации). Список всех адресов - это состояние по умолчанию, когда параметры фильтрации не введены. Было бы неудобно, хотя и не невозможно, иметь что-то другое, кроме всех Адресов, состояние по умолчанию. Я рассматриваю этот вариант, хотя я озадачен тем, что он работает быстрее на вашем компьютере. Если бы я мог показать «все адреса», которые были бы наименее запутанными для пользователей. – Ethan

0

Давайте посмотрим.

  • Адрес: 22271
  • Контакты: 17906
  • Организация: 8246

адресует LEFT JOIN контактов гр дает около 20 000 * 20 000 ~ 400 миллионов сравнений в течение приблизительно 20 000 результатов

Организации ЛЕВОГО ПРИСОЕДИНЕНИЯ дают около 10000 * 20000 ~ 200 миллионов сравнений для около 20 000 результатов

, который мы сортируем в основном на строках контактов, а затем отбрасываем все, кроме 24 из них. Кажется, отличительность адресов имеет минимальное значение.

Так как мы в основном сортировкой по контактам, как насчет мы делаем подвыбор о контактах, сохраняя несколько больше (скажем, с коэффициентом около 4), чем нам нужно:

SELECT * FROM contacts ORDER BY last_name, first_name LIMIT 100 

Тогда Присоединяюсь к тем их адреса, сохраняя верхнюю сотню или около того

 SELECT a.* 
     FROM (SELECT * FROM contacts ORDER BY last_name, first_name LIMIT 0, 100) AS c 
    LEFT JOIN addresses a 
     ON c.id = a.contact_id 
     LIMIT 0, 100 

Затем присоединиться к этим организациям

SELECT * 
    FROM (
     SELECT * 
      FROM (SELECT * FROM contacts ORDER BY last_name, first_name LIMIT 0, 100) AS c 
    LEFT JOIN addresses a 
      ON c.id = a.contact_id 
     LIMIT 0, 100 
     ) AS ca LEFT JOIN organizations o 
     ON o.id = ca.organization_id 
ORDER BY ca.last_name, ca.first_name, o.name 
    LIMIT 0, 24 

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

Смежные вопросы