如何使用表扫描优化查询?

时间:2012-07-07 00:48:03

标签: php mysql sql query-optimization innodb

这是我的网络应用程序中迄今为止最慢的查询。

SELECT prof.user_id                      AS userId,
       prof.first_name                   AS first,
       prof.last_name                    AS last,
       prof.birthdate,
       prof.class_string                 AS classes,
       prof.city,
       prof.country,
       prof.state,
       prof.images,
       prof.videos,
       u.username,
       u.avatar,
       (SELECT Count(*)
        FROM   company_member_sponsorship
        WHERE  member_id = prof.user_id
               AND status = 'sponsored') AS sponsor_count,
       (SELECT Count(*)
        FROM   member_schedules
        WHERE  user_id = prof.user_id)   AS sched_count
FROM   member_profiles prof
       LEFT JOIN users u
              ON u.id = prof.user_id
ORDER  BY ( prof.images + prof.videos * 5 + (
            CASE
              WHEN prof.expire_date > :time THEN 50
              ELSE 0
            end ) + sponsor_count * 20 + sched_count * 4
          ) DESC,
          prof.last_name ASC
LIMIT  :start, :records  

即使在所有级别上发生大量查询,网站上的其他所有内容也只需不到一秒钟即可加载。这个大概需要3-4秒。

显然,表扫描会导致速度减慢。我明白为什么;第一个表有50,000多行,第二个表有160,000多行。

有什么方法可以优化此查询以使其更快?

如果情况变得更糟,我可以随时查看我的代码,并为配置文件表中的赞助和事件保持一个统计数据,就像我对图像和视频一样,但我想避免它。

编辑:我在查询中添加了EXPLAIN的结果。

id  select_type         table                       type    possible_keys   key         key_len ref                         rows    Extra
1   PRIMARY             prof                        ALL     NULL            NULL        NULL    NULL                        44377   Using temporary; Using filesort
1   PRIMARY             u                           eq_ref  PRIMARY         PRIMARY     3       mxsponsor.prof.user_id      1   
3   DEPENDENT SUBQUERY  member_schedules            ref     user_id         user_id     3       mxsponsor.prof.user_id      6       Using index
2   DEPENDENT SUBQUERY  company_member_sponsorship  ref     member_id       member_id   3       mxsponsor.prof.user_id      2       Using where; Using index

EDIT2:

我最后通过维护会员资料中的计数来解决问题。无论在何处添加/删除赞助/事件,我都会调用一个扫描赞助/事件表的函数,并更新该成员的计数。可能仍有一种方法可以优化这样的查询,但是我们很快就会发布这个网站,所以我现在要使用快速而肮脏的解决方案。

1 个答案:

答案 0 :(得分:2)

无法保证可行,但请尝试使用joingroup by而不是内部选择:

SELECT prof.user_id      AS userId,
       prof.first_name   AS first,
       prof.last_name    AS last,
       prof.birthdate,
       prof.class_string AS classes,
       prof.city,
       prof.country,
       prof.state,
       prof.images,
       prof.videos,
       u.username,
       u.avatar,
       Count(cms.id)     AS sponsor_count,
       Count(ms.id)      AS sched_count
FROM   member_profiles prof
       LEFT JOIN users u
              ON u.id = prof.user_id
       LEFT JOIN company_member_sponsorship cms
              ON cms.member_id = prof.user_id
                 AND cms.status = 'sponsored'
       LEFT JOIN member_schedules ms
              ON ms.user_id = prof.user_id
GROUP  BY u.id
ORDER  BY ( prof.images + prof.videos * 5 + (
            CASE
              WHEN prof.expire_date > :time THEN 50
              ELSE 0
            end ) + sponsor_count * 20 + sched_count * 4
          ) DESC,
          prof.last_name ASC
LIMIT  :start, :records  

如果没有那么好,那么explain该查询会有所帮助。