2013-10-07 5 views
3

Вот запрос:Как я могу улучшить скорость этого запроса MySQL?

SELECT 
u.uid as UID, 
fuo.uid as FUO_UID, 
fo.prid as FO_NAME 
FROM 
users u 
LEFT OUTER JOIN firstpoint_users_organisations fuo ON (u.uid=fuo.uid) 
LEFT OUTER JOIN firstpoint_organisations fo ON (fo.nid=fuo.nid) 
WHERE 
u.status=1 AND u.uid>1 
ORDER BY u.uid 
LIMIT 3; 

скрижали:

users 
+------------------+------------------+------+-----+---------+----------------+ 
| Field   | Type    | Null | Key | Default | Extra   | 
+------------------+------------------+------+-----+---------+----------------+ 
| uid    | int(10) unsigned | NO | PRI | NULL | auto_increment | 
| name    | varchar(60)  | NO | UNI |   |    | 
| status   | tinyint(4)  | NO |  | 0  |    | 
+-----------------------------------------------------------------------------+ 

firstpoint_users_organisations 
+-------+------------------+------+-----+---------+-------+ 
| Field | Type    | Null | Key | Default | Extra | 
+-------+------------------+------+-----+---------+-------+ 
| nid | int(10) unsigned | NO | PRI | 0  |  | 
| uid | int(10) unsigned | NO | PRI | 0  |  | 
+-------+------------------+------+-----+---------+-------+ 

firstpoint_organisations 
+----------+------------------+------+-----+---------+-------+ 
| Field | Type    | Null | Key | Default | Extra | 
+----------+------------------+------+-----+---------+-------+ 
| nid  | int(10) unsigned | NO | PRI | 0  |  | 
| prid  | varchar(32)  | NO |  |   |  | 
+------------------------------------------------------------+ 

Я хочу показать users.uid и firstpoint_organisations.prid для каждой строки в users, даже если некоторые пользователи не будут иметь prid, в котором Я показываю NULL (следовательно, левые внешние соединения). Соединение должно быть следующим:

users 
uid -  firstpoint_users_organisations 
    \---->uid 
      nid -   firstpoint_organisations 
       \-------->nid 
          prid 

Таким образом, каждый пользователь (пользователи) имеет идентификатор пользователя (UID), и организации, они связаны с (firstpoint_users_organisation) имеет идентификатор узла (НДИ) и сохраняет эту ассоциацию , Детали организации затем сохраняются в firstpoint_organization.

Таким образом, у каждого пользователя будет prid, но если они этого не сделают, покажите NULL.

Теперь, если я делаю INNER JOIN на firstpoint_users_organisations, а затем на firstpoint_organisations, я получаю хорошую скорость запроса (указанный запрос выполняется за 0,02 секунды). Но когда я переключаю оба на LEFT OUTER JOIN, поэтому я могу получить всех пользователей, prid или нет prid, для выполнения вышеуказанного запроса требуется ~ 90 секунд.

Есть ли что-нибудь, что я могу сделать, чтобы ускорить этот запрос? Есть ок. 70 000 строк в таблице users, но даже с LIMIT 3, делая INNER JOIN LEFT OUTER JOIN занимает ужасное время. Интересно, что запрос занимает столько же времени, что и LIMIT 30, поэтому я думаю, что в моем запросе есть что-то принципиально неправильное.

EXPLAIN по запросу:

+----+-------------+-------+--------+---------------+---------+---------+-----------------------+-------+----------------------------------------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref     | rows | Extra          | 
+----+-------------+-------+--------+---------------+---------+---------+-----------------------+-------+----------------------------------------------+ 
| 1 | SIMPLE  | u  | range | PRIMARY  | PRIMARY | 4  | NULL     | 13152 | Using where; Using temporary; Using filesort | 
| 1 | SIMPLE  | fuo | index | NULL   | PRIMARY | 8  | NULL     | 3745 | Using index         | 
| 1 | SIMPLE  | fo | eq_ref | PRIMARY  | PRIMARY | 4  | dbdb-dbdb_uat.fuo.nid |  1 |            | 
+----+-------------+-------+--------+---------------+---------+---------+-----------------------+-------+----------------------------------------------+ 
3 rows in set (0.00 sec) 
+2

Пожалуйста, пост' EXPLAIN' этого заявления. – Kermit

+1

Один вопрос к вашей таблице firstpoint_users_organisations - возможно ли иметь 2 ПЕРВЫХ ключа? Мне кажется, это немного странно. – Wikunia

+0

Я добавил EXPLAIN, спасибо. 2 первичных ключа в таблице FUO выглядят странно, спасибо за то, что вы заметили. – njp

ответ

1

Ваш запрос бесцельно (потому что uid > 1 будет включать в себя все, кроме одного из пользователей), используя индекс UID, так что используйте IGNORE INDEX подсказку для этого индекса:

SELECT 
    u.uid as UID, 
    fuo.uid as FUO_UID, 
    fo.prid as FO_NAME 
FROM users u IGNORE INDEX (uid) 
LEFT JOIN firstpoint_users_organisations fuo ON u.uid=fuo.uid 
LEFT JOIN firstpoint_organisations fo ON fo.nid=fuo.nid 
WHERE u.status=1 
AND u.uid > 1 
ORDER BY u.uid 
LIMIT 3 

Вы должны указать индекс на users(status), что может дать вам некоторое преимущество, если имеется достаточно строк со статусом!= 1

Это вполне ожидаемо, что изменение LIMIT не будет иметь никакого эффекта, так как 70000 строки должны быть отсортированы перед тем предел применяется знать , которые ряды первые ряды, чтобы вернуться - предел мало влияет , за исключением того, что меньше строки возвращаются клиент (меньше запятой IO)


Я верю в «меньше коды хорошо», поэтому от строго стиля точки зрения я удалил неосновной код из ваш запрос:

  • удалены OUTER, потому что нет никакого другого вида слева присоединиться
  • удалены скобок вокруг условий соединения, потому что вам не нужны «Em
1

Я хотел бы использовать уникальный индекс u.status, u.uid для этого, потому что MySQL должен сделать FullScan, чтобы посмотреть, какие записи имеет статус = 1 Я думаю, ,

Я надеюсь, что это быстрее, потом;)

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