浅谈Mysql查询优化1

需求:测试库t_user_download_log(如下)没有做user_id,appVersion唯一索引,但正式库有,需要在测试库里面找出所有userId,appVersion重复的记录,相同的记录显示时间最早的,并将结果输出到另外一张表中。

/*Table structure for table `t_user_download_log` */

DROP TABLE IF EXISTS `t_user_download_log`;

CREATE TABLE `t_user_download_log` (

`uuid` varchar(32) NOT NULL,

`user_id` int(11) NOT NULL COMMENT '用户ID',

`appVersion` varchar(50) NOT NULL COMMENT 'appVersion',

`status_` tinyint(4) NOT NULL DEFAULT '0' COMMENT '标签状态',

`create_time` datetime NOT NULL COMMENT '插入时间',

PRIMARY KEY (`uuid`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

由于测试数据比较多,这里便于说明,新建一个和t_user_download_log结构一样的表t_user_test,插入一些数据做测试:

Insert into `t_user_test`(`uuid`,`user_id`,`appVersion`,`status_`,`create_time`) values ('10',1,'5.1.5',0,'2017-05-11 10:42:52'),('11',1,'5.1.5',0,'2017-05-11 11:42:52'),('12',2,'5.1.2',0,'2017-05-11 10:42:52'),('13',1,'5.1.5',0,'2017-05-10 9:42:52'),('14',3,'5.1.5',0,'2017-05-8 9:42:52')

执行select * from t_user_test:


这时候我想要的查询结果是:

方式1:使用子查询的方式(最容易想到的应该就是这种方式),实现方式如下:

select *

from (

select *

from t_user_test

order by create_time

) as a

where uuid =(

select uuid

from t_user_test as b

where a.user_id=b.user_id and a.appVersion=b.appVersion

order by create_time limit 1

)order by uuid

(使用2次子查询)

select *

from t_user_download_log

where UUID in (

select _uuid

from (

select user_id _id,

appVersion _app,

(

select UUID

from t_user_download_log l3

where l3.appVersion=_app and l3.user_id=_id

order by create_time limit 1

) _uuid

from t_user_download_log l1

) t1

) order by user_id , appVersion;

(使用3次子查询)

方法中使用了两次子查询,生成了两张临时表a和b。

这种方式最大的缺点是:查询速度慢,由于临时表的产生会导致数据库在查询的时候多出了额外的时间开销和空间开销,每次查询会多出一个数据fetch的过程,当数据量足够大的时,会耗费相当长的时间在数据fetch的过程中,因此,sql查询中应当尽量少使用子查询的方式,即使要使用也应该尽量少的使用子查询,多使用聚合函数的形式代替子查询。

使用2次子查询和使用3次子查询的性能对比:

方式2:使用聚合函数代替子查询(这种方式的关键是找到能够代替子查询的聚合函数),实现方式如下:

select  substring_index(group_concat(uuid order by create_time),',',1) as uuid,

user_id,

appVersion,

substring_index(group_concat(status_),',',1) as status_,

substring_index(group_concat(create_time order by create_time),',',1) as create_time

from t_user_test

group by user_id,appVersion

order by uuid

这里从查询性能方面对两次方式查询结果做一个对比(由于在数据量比较大的时候的对比结果比较明显,所以这里使用的是t_user_download_log表,数据量:5742条数据):

方式2中涉及的聚合函数介绍:

1.group_concat

group_concat:将指定组的字段拼接到一块,并且可以设定拼接的顺序(order by...)

语法结构:GROUP_CONCAT([DISTINCT] expr [,expr ...] [ORDER BY {unsigned_integer | col_name | expr} [ASC | DESC] [,col_name ...]] [SEPARATOR str_val])

其中,separator表示按照指定的str_val分隔符分割。

注意:

mysql中默认的group_concat长度为1024个字节

查询时不受影响,但是如果将查询结果导出到另外的一张表中就会提示

类似“Error Code: 1260. Row 631 was cut by GROUP_CONCAT()0.015 sec”的错误

解决方案:设置group_concat长度:

在mysql中执行SET GLOBAL group_concat_max_len=102400;需要重启mysql

group_concat与group的区别:

group_concat相当于是将某一个字段的所有值拼接起来,显示的是拼接后的结果

而group是按字段分组,显示的结果还是原来的数据,只不过以分组的形式显示。

例如:以上面的t_user_test这张表做例子(总共存在5条数据,即5个user_id)

select user_id,count(*) as 'user_id出现个数' from t_user_test group by user_id

显示结果如下:

select group_concat(user_id) ,count(*) as 'user_id出现个数'from t_user_test

显示结果如下:

2.substring_index:字符串截取函数,通过指定分隔符分割后按照index截取对应字段。

例如上面的substring_index(group_concat(a.create_time order by create_time),',',1)

表示使用逗号分割group_concat(a.create_time order by create_time)结果,并且使用第1个索引处的字段。

补充:mysql不严格的语义限制

在语义限制的严格sql语句中,显示结果的列一定要出现在 聚合函数或GROUP BY子句中

例如:

select ID,title from xinwen group by ID

这样的写法就会出错,因为title这个结果列并没有出现在group by分组中也没有使用聚合函数的形式。

正确写法:

select ID,title from xinwen group by ID,title

或者

select ID,count(title) from xinwen group by ID

但是在mysql中,由于存在着不严格的语义限制(mysql早期的非ONLY_FULL_GROUP_BY语义,了解可以以这个为关键词搜索查看相关文章),导致会出现类似select ID,title from xinwen group by ID语句也可以得到正确执行。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,423评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,147评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,019评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,443评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,535评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,798评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,941评论 3 407
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,704评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,152评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,494评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,629评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,295评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,901评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,742评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,978评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,333评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,499评论 2 348

推荐阅读更多精彩内容