U盘PE| w764位旗舰版下载 | U盘装win7系统 | U盘启动 |win7pe | win10下载 |加入收藏土豆PE官网U盘PE,U盘装win7系统,win7pe,U盘启动,U盘装系统,w764位旗舰版下载站!
当前位置:主页 > 帮助中心 > 常见问题解答 >

MySQL中有几种常见的 SQL 错误用法?

来源:http://www.tudoupe.com时间:2022-07-23

MySQL在2016年保持了一个强劲的增长数据库流行趋势。越来越多的客户在MySQL数据库上建立应用程序,它甚至从Oracle转移到MySQL。还有一些客户在使用MySQL数据库时遇到一些问题,例如慢响应时间,CPU打满等情况。阿里云RDS专家服务团队帮助云客户解决许多紧急问题。下面总结了apsaraDB专家诊断报告中出现的一些常见SQL问题:供大家参考。

常见SQL错误用法

1. LIMIT 句子

页面查询是最常见的场景之一,但它也常常是最容易陷入麻烦的地方。例如,下列简单的句子,DBA通常想将组合索引添加到类型、名称、 create_time字段中。这样条件序列可以有效地用于索引,性能迅速提升。

SELECT*
FROMoperation
WHERE type='SQLStats'
AND name='SlowLog'
ORDER BYcreate_time
LIMIT 1000,10;

好吧,可能90%以上的DBA解决该问题就到此为止。但当 LIMIT 子句变成 “LIMIT 1000000,10” 时,程序员仍然会抱怨:我只取10条记录为什么还是慢?

知道数据库不知道第一百万条的记录从何处开始,即使有指数, 它必须首先计算.出现这种性能问题,在大多数情况下,程序员是懒惰的。浏览前面的数据,或者大数据批量输出等等,可以使用前面的页面的最大值作为参数作为查询条件。SQL被重新设计如下:

SELECT*
FROMoperation
WHERE type='SQLStats'
AND name='SlowLog'
ANDcreate_time >'2017-03-16 14:00:00'
ORDER BYcreate_timelimit 10;

在新的设计下,查询时间基本上是固定的,随数据量增加而不会改变。

2. 隐式转换

另一个常见的错误是SQL文中查询变量和字段定义类型不匹配。

mysql> explain extendedSELECT*
>FROMmy_balance b
>WHEREb.bpn =14000000123
>ANDb.isverifiedISNULL ;
mysql> show warnings;
| Warning |1739| Cannot use ref accessonindex'bpn' due to type or collation conversion on field 'bpn'

其中,字段 bpn 定义为 varchar(20), MySQL 的策略是将字符串转换成数字并再比较它们。

上面的可能是应用程序框架自动填充的参数,而不是程序员原来的想法。 现在应用程序框架非常繁琐,使用方便,而且要小心它可能自己挖出一个洞。

3.相关更新和删除

虽然MySQL5.6引入了物化特性,但需要特别注意它目前仅仅针对查询语句的优化。对于更新或删除需要手工重写成JOIN。

例如,在下面的更新声明中,MySQL实际上执行了一个循环/嵌入子查询(DEPENDENT SUBQUERY),其执行时间已知。

UPDATEoperation o
SET status='applying'
WHEREo.idIN(SELECT id
FROM(SELECTo.id,
o.status
FROMoperation o
WHEREo.group =123
ANDo.statusNOT IN('done')
ORDER BYo.parent,
o.id
LIMIT 1) t);

执行计划:

+----+--------------------+-------+-------+---------------+---------+---------+-------+------+-----------------------------------------------------+
| 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”改为“ DEIVED”,执行速度从7秒提高到2毫秒。

UPDATEoperation o
JOIN(SELECTo.id,
o.status
FROMoperation o
WHEREo.group =123
ANDo.statusNOT IN('done')
ORDER BYo.parent,
o.id
LIMIT 1) t
ONo.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*
FROMmy_order o
INNER JOINmy_appraise aONa.orderid = o.id
ORDER BYa.is_replyASC,
a.appraise_timeDESC
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*
FROMmy_order o
INNER JOINmy_appraise a
ONa.orderid = o.id
ANDis_reply =0
ORDER BYappraise_timeDESC
LIMIT 0,20)
UNIONALL
(SELECT*
FROMmy_order o
INNER JOINmy_appraise a
ONa.orderid = o.id
ANDis_reply =1
ORDER BYappraise_timeDESC
LIMIT 0,20)) t
ORDER BYis_replyASC,
appraisetimeDESC
LIMIT 20;

5.有句子

MySQL在处理EXISTS子句子时仍然使用嵌入式查询执行方法,例如以下的SQL声明:

SELECT*
FROMmy_neighbor n
LEFT JOINmy_neighbor_apply sra
ONn.id = sra.neighbor_id
ANDsra.user_id ='xxx'
WHEREn.topic_status <4
AND EXISTS(SELECT 1
FROMmessage_info m
WHEREn.id = m.neighbor_id
ANDm.inuser ='xxx')
ANDn.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 |
+----+--------------------+-------+------+ -----+------------------------------------------+---------+-------+---------+ -----+

删除加入现有更改,避免嵌入式查询,并执行从1.93秒减少到1毫秒的时间。

SELECT*
FROMmy_neighbor n
INNER JOINmessage_info m
ONn.id = m.neighbor_id
ANDm.inuser ='xxx'
LEFT JOINmy_neighbor_apply sra
ONn.id = sra.neighbor_id
ANDsra.user_id ='xxx'
WHEREn.topic_status <4
ANDn.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的子问题;

  • 联合国或联合国所有子问题;

  • 输出领域中的子问题;

例如,从执行计划中可以看出,它的条件在多项式查询后运行:

SELECT*
FROM(SELECTtarget,
Count(*)
FROMoperation
GROUP BYtarget) t
WHEREtarget ='rm-xxxx'
+----+-------------+------------+-------+---------------+-------------+---------+-------+------+-------------+
|id| select_type |table|type| possible_keys |key| key_len |ref|rows| Extra       |
+----+-------------+------------+-------+---------------+-------------+---------+-------+------+-------------+
|1| PRIMARY     | <derived2> |ref| <auto_key0>   | <auto_key0> |514| const |2|Using where|
|2| DERIVED     | operation  |index| idx_4         | idx_4       |519|NULL|20|Using index|
+----+-------------+------------+-------+---------------+-------------+---------+-------+------+-------------+

为了确定查询条件是否可以直接从语义中推导,重新写如下:

SELECTtarget,
Count(*)
FROMoperation
WHEREtarget ='rm-xxxx'
GROUP BYtarget

执行计划变为:

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

关于MySQL外部条件不能下推的详细解释说明请参考以前文章:MySQL · 性能优化 · 条件下推到物化表

7. 提前缩小范围

首先,SQL声明:

SELECT*
FROMmy_order o
LEFT JOINmy_userinfo u
ONo.uid = u.uid
LEFT JOINmy_productinfo p
ONo.pid = p.pid
WHERE( o.display =0)
AND( o.ostaus =1)
ORDER BYo.selltimeDESC
LIMIT 0,15

SQL声明最初的意思是:首先创建一系列左键连接,然后序列前15个记录。 如从执行计划中可以看出,最后一步估计序列记录的数目为90,00,时间消耗为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*
FROMmy_order o
WHERE( o.display =0)
AND( o.ostaus =1)
ORDER BYo.selltimeDESC
LIMIT 0,15
) o
LEFT JOINmy_userinfo u
ONo.uid = u.uid
LEFT JOINmy_productinfo p
ONo.pid = p.pid
ORDER BYo.selltimeDESC
limit 0,15

再次检查执行计划:在子查询实物化后参加JOIN(select_type=DERIVED)。虽然估计的线扫描时间仍然为90,00,但使用索引和LIMIT分项后实际执行时间变得较小。

+----+-------------+------------+--------+---------------+---------+---------+-------+--------+----------------------------------------------------+
| id |select_type| table      |type| possible_keys |key| key_len |ref| rows   |Extra|
+----+-------------+------------+--------+---------------+---------+---------+-------+--------+----------------------------------------------------+
|
1 | PRIMARY     |<derived2>| ALL    |NULL| NULL    |NULL| NULL  | 15 | Using temporary; Using filesort                    |
|  1 |PRIMARY| u          |eq_ref| PRIMARY       |PRIMARY| 4       |o.uid|      1 |NULL|
|
1 | PRIMARY     |p| ALL    |PRIMARY| NULL    |NULL| NULL  | 6 | Using where; Using join buffer (Block Nested Loop) |
|  2 |DERIVED| o          |index| NULL          |idx_1| 5       |NULL| 909112 |Using where|
+----+-------------+------------+--------+---------------+---------+---------+-------+--------+----------------------------------------------------+

8. 中等 结果 除去

让我们再看一下最初优化的下面的例子(左连接中的主表优先查询条件):

SELECTa.*,
c.allocated
FROM(
SELECTresourceid
FROMmy_distribute d
WHEREisdelete =0
ANDcusmanagercode ='1234567'
ORDER BYsalecodelimit 20) a
LEFT JOIN
(
SELECTresourcesid,sum(ifnull(allocation,0) *12345) allocated
FROMmy_resources
GROUP BYresourcesid) c
ONa.resourceid = c.resourcesid

那么该语句还存在其它问题吗?不难看出子查询 c 是全表聚合查询,在表数量特别大的情况下会导致整个语句的性能下降。

事实上,对于子问题c,左连结的最终结果集只关心可以匹配主资源ID的数据。 因此,我们可以如下重写句子,执行时间从原来的2秒下降到2毫秒。

SELECTa.*,
c.allocated
FROM(
SELECTresourceid
FROMmy_distribute d
WHEREisdelete =0
ANDcusmanagercode ='1234567'
ORDER BYsalecodelimit 20) a
LEFT JOIN
(
SELECTresourcesid,sum(ifnull(allocation,0) *12345) allocated
FROMmy_resources r,
(
SELECTresourceid
FROMmy_distribute d
WHEREisdelete =0
ANDcusmanagercode ='1234567'
ORDER BYsalecodelimit 20) a
WHEREr.resourcesid = a.resourcesid
GROUP BYresourcesid) c
ONa.resourceid = c.resourcesid

但是子查询 a 在我们的SQL语句中出现了多次。这种写法不仅存在额外的开销,还使得整个语句显的繁杂。使用WITH语句再次重写:

WITH a AS
(
SELECTresourceid
FROMmy_distribute d
WHEREisdelete =0
ANDcusmanagercode ='1234567'
ORDER BYsalecodelimit 20)
SELECTa.*,
c.allocated
FROMa
LEFT JOIN
(
SELECTresourcesid,sum(ifnull(allocation,0) *12345) allocated
FROMmy_resources r,
a
WHEREr.resourcesid = a.resourcesid
GROUP BYresourcesid) c
ONa.resourceid = c.resourcesid

AliSQL即将推出语法, 请期待.

总结

数据库编译器生成执行计划,确定SQL的实际执行方法。但是编译器只是想提供服务,并非所有的数据库编译器都是漂亮的。上述的大部分情形,其他数据库也有性能问题。了解数据库编译器的特点,才能避规其短处,编写高性能SQL声明。

在设计数据模型和编译SQL文时,程序员将算法的概念或意识引入。

编译复杂的SQL文档需要使用 WITH文档的习惯。 一个简单而有思想的SQL文档也可以减少数据库的负担。

解决云数据库的问题(不局限于SQL问题),并一直寻求阿里云工厂专家的帮助。

Copyright © 2012-2014 Www.tudoupe.Com. 土豆启动 版权所有 意见建议:tdsky@tudoupe.com

土豆系统,土豆PE,win7系统下载,win7 64位旗舰版下载,u盘启动,u盘装系统,win10下载,win10正式版下载,win10 RTM正式版下载,win8下载,电脑蓝屏,IE11修复,网络受限,4K对齐,双系统,隐藏分区,系统安装不了,U盘装系统,笔记本装系统,台式机装系统,diskgenius运用,GHSOT装系统,U盘修复,U盘技巧,U盘速度,U盘不能格式化,U盘复制发生错误,U盘加密,U盘选购,开机黑屏,蓝屏,进不了系统,上不了网,打不开程序,点击无反应,系统设置,PE个性化,PE添加网络,PE维护系统

点击这里给我发消息