3

クエリは次のとおりです。

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.uidandを表示したいのですが、その場合は NULL を表示します (したがって、左外部結合)。接続は次のようにする必要があります。firstpoint_organisations.pridusersprid

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

したがって、各ユーザー (users) にはユーザー ID (uid) があり、関連付けられている組織 (firstpoint_users_organisation) にはノード ID (nid) があり、この関連付けを保存します。組織の詳細は firstpoint_organisations に保存されます。

したがって、すべてのユーザーには がありますが、pridそうでない場合は NULL が表示されます。

ここで、 で INNER JOIN を実行するfirstpoint_users_organisationsと、firstpoint_organisationsクエリ速度が向上します (上記のクエリは 0.02 秒で実行されます)。しかし、両方を LEFT OUTER JOIN に切り替えて、すべてのユーザーを取得できるようにするpridか、 noを取得するpridと、上記のクエリの実行に約 90 秒かかります。

このクエリを高速化するためにできることはありますか? 約あります。テーブルには 70,000 行ありusersますが、LIMIT 3 を使用しても、INNER JOIN を LEFT OUTER JOIN にするのに恐ろしいほどの時間がかかります。興味深いことに、LIMIT 30 でクエリを実行するのに同じ時間がかかるため、クエリに根本的な問題があると思います。

要求に応じて説明:

+----+-------------+-------+--------+---------------+---------+---------+-----------------------+-------+----------------------------------------------+
| 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)
4

2 に答える 2

1

uid のインデックスを使用するクエリは無意味です ( uid > 11 人を除くすべてのユーザーが含まれるため)。そのため、そのインデックスには 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他の種類の左結合がないため削除されました
  • 結合条件を囲む角かっこは必要ないため、削除しました
于 2013-10-07T16:01:53.320 に答える
1

そのためには、u.status、u.uid に一意のインデックスを使用します。これは、mysql がフルスキャンを確認する必要があり、どのエントリが status = 1 であるかを確認する必要があるためです。

その後はもっと速いといいのですが;)

于 2013-10-07T15:58:06.777 に答える