这些SQL错误用法,如果经常犯,说明你的水平还很low

执行计划:

±—±-------------------±------±------±--------------±--------±--------±------±-----±----------------------------------------------------+

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |

±—±-------------------±------±------±--------------±--------±--------±------±-----±----------------------------------------------------+

| 1 | PRIMARY | o | index | | PRIMARY | 8 | | 24 | Using where; Using temporary |

| 2 | DEPENDENT SUBQUERY | | | | | | | | Impossible WHERE noticed after reading const tables |

| 3 | DERIVED | o | ref | idx_2,idx_5 | idx_5 | 8 | const | 1 | Using where; Using filesort |

±—±-------------------±------±------±--------------±--------±--------±------±-----±----------------------------------------------------+

重写为JOIN之后,子查询的选择模式从DEPENDENT SUBQUERY变成DERIVED,执行速度大大加快,从7秒降低到2毫秒。

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’

执行计划简化为:

±—±------------±------±-----±--------------±------±--------±------±-----±----------------------------------------------------+

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |

±—±------------±------±-----±--------------±------±--------±------±-----±----------------------------------------------------+

| 1 | PRIMARY | | | | | | | | Impossible WHERE noticed after reading const tables |

| 2 | DERIVED | o | ref | idx_2,idx_5 | idx_5 | 8 | const | 1 | Using where; Using filesort |

±—±------------±------±-----±--------------±------±--------±------±-----±----------------------------------------------------+

4、混合排序


MySQL不能利用索引进行混合排序。但在某些场景,还是有机会使用特殊方法提升性能的。

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

执行计划显示为全表扫描:

±—±------------±------±-------±------------±--------±--------±--------------±--------±+

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra

±—±------------±------±-------±------------±--------±--------±--------------±--------±+

| 1 | SIMPLE | a | ALL | idx_orderid | NULL | NULL | NULL | 1967647 | Using filesort |

| 1 | SIMPLE | o | eq_ref | PRIMARY | PRIMARY | 122 | a.orderid | 1 | NULL |

±—±------------±------±-------±--------±--------±--------±----------------±--------±+

由于is_reply只有0和1两种状态,我们按照下面的方法重写后,执行时间从1.58秒降低到2毫秒。

SELECT *

FROM ((SELECT *

FROM my_order o

INNER JOIN my_appraise a

ON a.orderid = o.id

AND is_reply = 0

ORDER BY appraise_time DESC

LIMIT 0, 20)

UNION ALL

(SELECT *

FROM my_order o

INNER JOIN my_appraise a

ON a.orderid = o.id

AND is_reply = 1

ORDER BY appraise_time DESC

LIMIT 0, 20)) t

ORDER BY is_reply ASC,

appraisetime DESC

LIMIT 20;

5、EXISTS语句


MySQL对待EXISTS子句时,仍然采用嵌套子查询的执行方式。如下面的SQL语句:

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

执行计划为:

±—±-------------------±------±-----±----±-----------------------------------------±--------±------±--------+ -----+

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |

±—±-------------------±------±-----+ -----±-----------------------------------------±--------±------±--------+ -----+

| 1 | PRIMARY | n | ALL | | NULL | NULL | NULL | 1086041 | Using where |

| 1 | PRIMARY | sra | ref | | idx_user_id | 123 | const | 1 | Using where |

| 2 | DEPENDENT SUBQUERY | m | ref | | idx_message_info | 122 | const | 1 | Using index condition; Using where |

±—±-------------------±------±-----+ -----±-----------------------------------------±--------±------±--------+ -----+

去掉exists更改为join,能够避免嵌套子查询,将执行时间从1.93秒降低为1毫秒。

SELECT *

FROM my_neighbor n

INNER JOIN message_info m

ON n.id = m.neighbor_id

AND m.inuser = ‘xxx’

LEFT JOIN my_neighbor_apply sra

ON n.id = sra.neighbor_id

AND sra.user_id = ‘xxx’

WHERE n.topic_status < 4

AND n.topic_type <> 5

新的执行计划:

±—±------------±------±-------+ -----±-----------------------------------------±--------+ -----±-----+ -----+

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |

±—±------------±------±-------+ -----±-----------------------------------------±--------+ -----±-----+ -----+

| 1 | SIMPLE | m | ref | | idx_message_info | 122 | const | 1 | Using index condition |

| 1 | SIMPLE | n | eq_ref | | PRIMARY | 122 | ighbor_id | 1 | Using where |

| 1 | SIMPLE | sra | ref | | idx_user_id | 123 | const | 1 | Using where |

±—±------------±------±-------+ -----±-----------------------------------------±--------+ -----±-----+ -----+

6、条件下推


外部查询条件不能够下推到复杂的视图或子查询的情况有:

聚合子查询;

含有LIMIT的子查询;

UNION 或UNION ALL子查询;

输出字段中的子查询;

如下面的语句,从执行计划可以看出其条件作用于聚合子查询之后:

SELECT *

FROM (SELECT target,

Count(*)

FROM operation

GROUP BY target) t

WHERE target = ‘rm-xxxx’

±—±------------±-----------±------±--------------±------------±--------±------±-----±------------+

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |

±—±------------±-----------±------±--------------±------------±--------±------±-----±------------+

| 1 | PRIMARY | | ref | <auto_key0> | <auto_key0> | 514 | const | 2 | Using where |

| 2 | DERIVED | operation | index | idx_4 | idx_4 | 519 | NULL | 20 | Using index |

±—±------------±-----------±------±--------------±------------±--------±------±-----±------------+

确定从语义上查询条件可以直接下推后,重写如下:

SELECT target,

Count(*)

FROM operation

WHERE target = ‘rm-xxxx’

GROUP BY target

执行计划变为:

±—±------------±----------±-----±--------------±------±--------±------±-----±-------------------+

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |

±—±------------±----------±-----±--------------±------±--------±------±-----±-------------------+

| 1 | SIMPLE | operation | ref | idx_4 | idx_4 | 514 | const | 1 | Using where; Using index |

±—±------------±----------±-----±--------------±------±--------±------±-----±-------------------+

7、提前缩小范围


先上初始SQL语句:

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

该SQL语句原意是:先做一系列的左连接,然后排序取前15条记录。从执行计划也可以看出,最后一步估算排序记录数为90万,时间消耗为12秒。

±—±------------±------±-------±--------------±--------±--------±----------------±-------±---------------------------------------------------+

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |

±—±------------±------±-------±--------------±--------±--------±----------------±-------±---------------------------------------------------+

| 1 | SIMPLE | o | ALL | NULL | NULL | NULL | NULL | 909119 | Using where; Using temporary; Using filesort |

| 1 | SIMPLE | u | eq_ref | PRIMARY | PRIMARY | 4 | o.uid | 1 | NULL |

| 1 | SIMPLE | p | ALL | PRIMARY | NULL | NULL | NULL | 6 | Using where; Using join buffer (Block Nested Loop) |

±—±------------±------±-------±--------------±--------±--------±----------------±-------±---------------------------------------------------+

由于最后WHERE条件以及排序均针对最左主表,因此可以先对my_order排序提前缩小数据量再做左连接。SQL重写后如下,执行时间缩小为1毫秒左右。

SELECT *

FROM (

SELECT *

FROM my_order o

WHERE ( o.display = 0 )

AND ( o.ostaus = 1 )

ORDER BY o.selltime DESC

LIMIT 0, 15

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值