200-Study | SQL学习| MySQL 优化案例

MySQL的优化案例都来自:
MySQL · 性能优化 · MySQL常见SQL错误用法

Case 1: limit 100000000,1

SELECT * 
FROM   operation 
WHERE  type = 'SQLStats' 
       AND name = 'SlowLog' 
ORDER  BY create_time 
LIMIT  1000, 10; 

DBA(data base administrator)会选择采用建立组合索引的形式来增加访问速度。
关于检索的理解,检索是以空间换取时间的一个策略。作用是在使用where语句进行过滤的时候可以加快查询进度,对于and这种组合过滤条件则需要创建组合索引。原理尚不清楚。
但是这里的limit如果后面第一个参数非常大,这样的查询会非常慢。把每一页的最大值比如#2017-03-16 14:00:00#当成查询条件就可以

SELECT   * 
FROM     operation 
WHERE    type = 'SQLStats' 
AND      name = 'SlowLog' 
AND      create_time > '2017-03-16 14:00:00' 
ORDER BY create_time limit 10;

Case 2: where bpn(varchar(20)) = 10211313

mysql> explain extended SELECT * 
     > FROM   my_balance b 
     > WHERE  b.bpn = 14000000123 
     >       AND b.isverified IS NULL ;
mysql> show warnings;
| Warning | 1739 | Cannot use ref access on index 'bpn' due to type or collation conversion on field 'bpn'

因为指定的bpn类型为字符串行,类型不匹配时,索引失效;查询速度可想而知。

case 3: subquery -> derive

UPDATE operation o 
SET    status = 'applying' 
WHERE  o.id IN (SELECT id 
                FROM   (SELECT o.id, 
                               o.status 
                        FROM   operation o 
                        WHERE  o.group = 123 
                               AND o.status NOT IN ( 'done' ) 
                        ORDER  BY o.parent, 
                                  o.id 
                        LIMIT  1) t);

先执行子查询->父查询->祖父查询,按常理subquery是比类似的join 运算要慢的。所以,以上的语句可以写成…

update operation o
join (select o.id
            ,o.status
     from operation o
     where o.group = 123 and o.status not in ('done')
     order by o.parent
             ,o.id
     limit 1) t
on o.id = t.id
set status = 'applying'

日常的查询虽然用不到update但是这里我们还是要有意识多用join少用子查询。

case 4: 复合排序(特定情况)

SELECT * 
FROM   my_order o 
       INNER JOIN my_appraise a ON a.orderid = o.id 
ORDER  BY a.is_reply ASC, 
          a.appraise_time DESC 
limit 0,20
;

如果字段is_reply仅仅只有0,1两个值时,那么可以选择筛选+union的操作。

select *
from

((select *
from my_order o
join my_appraise a on a.orderid = o.id
where a.is_reply = 0
order by a.appraise_time desc
limit 0,20)

union

(select *
from my_order o
join my_appraise a on a.orderid = o.id
where a.is_reply = 1
order by a.appraise_time desc
limit 0,20))
order by a.is_reply asc
        ,appraise_time desc
limit 20
;

case 5:

SELECT *
FROM   my_neighbor n 
       LEFT JOIN my_neighbor_apply sra 
              ON n.id = sra.neighbor_id 
                 AND sra.user_id = 'xxx' 
WHERE  n.topic_status < 4 
       AND EXISTS(SELECT 1 
                  FROM   message_info m 
                  WHERE  n.id = m.neighbor_id 
                         AND m.inuser = 'xxx') 
       AND n.topic_type <> 5 

原理类似case 3,勤用join少用subquery。而且,把inner join放在left join前面,可以节省时间。(但是如果inner join的数据量要大很多的话,除外。)

select *
from
    my_neighbor n
left join
    my_neighbor_apply sra
on 
    n.id = sra.neighbor_id
    and sra.user_id = 'xxx'
inner join
    message_info m
on
    n.id = m.neighbor_id
    and m.inuser = 'xxx'
where
    n.topic_status<4
    and n.topic_type <> 5

case 6: 查询条件放哪里?

SELECT * 
FROM   (SELECT target, 
               Count(*) 
        FROM   operation 
        GROUP  BY target) t 
WHERE  target = 'rm-xxxx'

可以优化成

select
    target
    ,count(*)
from
    operation
where
    target = 'rm-xxx'
group by
    1

至少不符合条件的记录可以被筛选出去,不纳入计算了。

case 7:

SELECT * 
FROM   my_order o 
       LEFT JOIN my_userinfo u 
              ON o.uid = u.uid
       LEFT JOIN my_productinfo p 
              ON o.pid = p.pid 
WHERE  ( o.display = 0 ) 
       AND ( o.ostaus = 1 ) 
ORDER  BY o.selltime DESC 
LIMIT  0, 15 

** 明显 my_order 这张表是可以提前筛选的。**

select
    *
from
(select
    *
from
    my_order o
where
    o.display = 0
    and o.status = 1
order by
    o.selltime desc
limit
    0,15) tmpo
left join
    my_userinfo u
on
    tmpo.uid = u.uid
left join
    my_productinfo
on
    tmpo.pid = p.pid
order by
    o.selltime desc
limit
    0,15    

uid和pid分别是my_userinfo和my_productinfo的唯一主键。

case 8:

SELECT    a.*, 
          c.allocated 
FROM      ( 
              SELECT   resourceid 
              FROM     my_distribute d 
                   WHERE    isdelete = 0 
                   AND      cusmanagercode = '1234567' 
                   ORDER BY salecode limit 20) a 
LEFT JOIN 
          ( 
              SELECT   resourcesid, sum(ifnull(allocation, 0) * 12345) allocated 
              FROM     my_resources 
                   GROUP BY resourcesid) c 
ON        a.resourceid = c.resourcesid

c表中我们只关心a中有的resourceid!!!! 这句话写得贼好。所以,改进措施…

with a as
(select
    resourceid
from
    my_distribute d
where
    isdelete = 0
    and cusmanagercode = '1234567'
order by
    salecode
limit
    20
)
select
    a.*
    ,c.allocated
from
    a
left join
    (select
        resourcesid
        ,sum(ifnull(allocation,0)*12345) allocated
    from
        my_resources
        ,a
    where
        a.resourcesid = my_resources.my_resources
    group by
        1) c
on
   a.resourcesid = mr.resourcesid
group by
    1
    

你可能感兴趣的:(SQL)