MySQL选择慢因为Order By

时间:2016-05-14 02:51:18

标签: mysql performance select

我有以下SELECT:

SELECT a.userid,
    (SELECT name FROM photos WHERE userid=a.userid AND type='profile' LIMIT 1) AS photo,
    (SELECT attractive FROM photos WHERE userid=a.userid AND type='profile' LIMIT 1) AS attractive,
    IF(a.domain="domanin.com",1,2) AS preferredDomain,
    IF(a.domain LIKE "%domain.com",1,2) AS preferredSubDomain
FROM users AS a
WHERE a.gender = 1
    AND a.visible = 1
    AND a.active = 1
    AND a.completed = 1
    AND a.approved = 1
HAVING photo IS NOT NULL
ORDER BY preferredDomain ASC,
    attractive DESC,
    a.lastlogin DESC,
    preferredSubDomain ASC
LIMIT 100;

这需要5-6秒才能运行。

如果我删除Order By,则需要0.23秒才能运行。

我假设这是因为SELECT必须先生成内部SELECTS才能进行排序?这是正确的吗? users表有60,000个条目。

有人可以就如何更好地构建此查询提供一些建议吗?

2 个答案:

答案 0 :(得分:1)

尝试使用连接而不是嵌套查询,例如:

SELECT 
      a.userid, 
      p.name, 
      p.attractive, 
      IF(a.domain="domain.com",1,2) AS preferredDomain,
      IF(a.domain LIKE "%domain.com",1,2) AS preferredSubDomain 
FROM users AS a
JOIN photos AS p ON (p.userid = a.userid AND p.type = 'profile')
WHERE 
      a.gender = 1
      AND a.visible = 1
      AND a.active = 1
      AND a.completed = 1
      AND a.approved = 1
HAVING photo IS NOT NULL
ORDER BY preferredDomain ASC, attractive DESC, a.lastlogin DESC, preferredSubDomain ASC
LIMIT 100;

可以在此处找到有关连接语法的文档: http://dev.mysql.com/doc/refman/5.7/en/join.html

答案 1 :(得分:0)

子查询不是恶棍。使用ORDER BY必须在排序之前评估所有60K行。如果没有ORDER BY,可能需要查看更少的行。

@ StephanGarle建议使用JOIN可以加快查询速度,尤其是因为两个子查询都被JOIN替换。

photos需要INDEX(type, userid)(按任意顺序)。但更好的是拥有一个“覆盖”索引:(type, userid, name, attractive),其中前两个是任意顺序,后两个是任意顺序。

photo有两种方式NULL -

  • 该行可能存在,但name可以是NULL
  • 该行无法存在。 (即,photos中没有该用户标识符的行并输入“个人资料”。)

这是哪一个?要涵盖这两种情况,请使用LEFT JOIN

此索引(包含任何顺序的列)可能有助于提高性能:

INDEX(gender, visible, active, completed, approved)

因此...

SELECT  a.userid,
        p.name,
        p.attractive,
        IF(a.domain="domain.com", 1,2)       AS preferredDomain,
        IF(a.domain LIKE "%domain.com", 1,2) AS preferredSubDomain
    FROM  users AS a
    LEFT JOIN  photos AS p
              ON ( p.userid = a.userid
             AND   p.type = 'profile' )
    WHERE  a.gender = 1
      AND  a.visible = 1
      AND  a.active = 1
      AND  a.completed = 1
      AND  a.approved = 1
      AND  p.name IS NOT NULL
    ORDER BY  preferredDomain ASC, p.attractive DESC, a.lastlogin DESC,
              preferredSubDomain ASC
    LIMIT  100;

我仍然不确定nameLEFT是否得到正确处理。