淺談Mysql查詢優(yōu)化1

需求:測試庫t_user_download_log(如下)沒有做user_id,appVersion唯一索引,但正式庫有论巍,需要在測試庫里面找出所有userId,appVersion重復(fù)的記錄,相同的記錄顯示時間最早的窜醉,并將結(jié)果輸出到另外一張表中拌汇。

/*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 '標(biāo)簽狀態(tài)',

`create_time` datetime NOT NULL COMMENT '插入時間',

PRIMARY KEY (`uuid`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

由于測試數(shù)據(jù)比較多,這里便于說明谱净,新建一個和t_user_download_log結(jié)構(gòu)一樣的表t_user_test,插入一些數(shù)據(jù)做測試:

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')

執(zhí)行select * from t_user_test:


這時候我想要的查詢結(jié)果是:

方式1:使用子查詢的方式(最容易想到的應(yīng)該就是這種方式)窑邦,實現(xiàn)方式如下:

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壕探。

這種方式最大的缺點是:查詢速度慢冈钦,由于臨時表的產(chǎn)生會導(dǎo)致數(shù)據(jù)庫在查詢的時候多出了額外的時間開銷和空間開銷,每次查詢會多出一個數(shù)據(jù)fetch的過程李请,當(dāng)數(shù)據(jù)量足夠大的時瞧筛,會耗費相當(dāng)長的時間在數(shù)據(jù)fetch的過程中厉熟,因此,sql查詢中應(yīng)當(dāng)盡量少使用子查詢的方式较幌,即使要使用也應(yīng)該盡量少的使用子查詢揍瑟,多使用聚合函數(shù)的形式代替子查詢。

使用2次子查詢和使用3次子查詢的性能對比:

方式2:使用聚合函數(shù)代替子查詢(這種方式的關(guān)鍵是找到能夠代替子查詢的聚合函數(shù))乍炉,實現(xiàn)方式如下:

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

這里從查詢性能方面對兩次方式查詢結(jié)果做一個對比(由于在數(shù)據(jù)量比較大的時候的對比結(jié)果比較明顯月培,所以這里使用的是t_user_download_log表,數(shù)據(jù)量:5742條數(shù)據(jù)):

方式2中涉及的聚合函數(shù)介紹:

1.group_concat

group_concat:將指定組的字段拼接到一塊恩急,并且可以設(shè)定拼接的順序(order by...)

語法結(jié)構(gòu):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個字節(jié)

查詢時不受影響衷恭,但是如果將查詢結(jié)果導(dǎo)出到另外的一張表中就會提示

類似“Error Code: 1260. Row 631 was cut by GROUP_CONCAT()0.015 sec”的錯誤

解決方案:設(shè)置group_concat長度:

在mysql中執(zhí)行SET GLOBAL group_concat_max_len=102400;需要重啟mysql

group_concat與group的區(qū)別:

group_concat相當(dāng)于是將某一個字段的所有值拼接起來此叠,顯示的是拼接后的結(jié)果

而group是按字段分組,顯示的結(jié)果還是原來的數(shù)據(jù)随珠,只不過以分組的形式顯示灭袁。

例如:以上面的t_user_test這張表做例子(總共存在5條數(shù)據(jù),即5個user_id)

select user_id,count(*) as 'user_id出現(xiàn)個數(shù)' from t_user_test group by user_id

顯示結(jié)果如下:

select group_concat(user_id) ,count(*) as 'user_id出現(xiàn)個數(shù)'from t_user_test

顯示結(jié)果如下:

2.substring_index:字符串截取函數(shù)窗看,通過指定分隔符分割后按照index截取對應(yīng)字段茸歧。

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

表示使用逗號分割group_concat(a.create_time order by create_time)結(jié)果,并且使用第1個索引處的字段显沈。

補充:mysql不嚴格的語義限制

在語義限制的嚴格sql語句中软瞎,顯示結(jié)果的列一定要出現(xiàn)在 聚合函數(shù)或GROUP BY子句中

例如:

select ID,title from xinwen group by ID

這樣的寫法就會出錯,因為title這個結(jié)果列并沒有出現(xiàn)在group by分組中也沒有使用聚合函數(shù)的形式拉讯。

正確寫法:

select ID,title from xinwen group by ID涤浇,title

或者

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

但是在mysql中,由于存在著不嚴格的語義限制(mysql早期的非ONLY_FULL_GROUP_BY語義魔慷,了解可以以這個為關(guān)鍵詞搜索查看相關(guān)文章)只锭,導(dǎo)致會出現(xiàn)類似select ID,title from xinwen group by ID語句也可以得到正確執(zhí)行。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末院尔,一起剝皮案震驚了整個濱河市蜻展,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌邀摆,老刑警劉巖纵顾,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異隧熙,居然都是意外死亡片挂,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進店門贞盯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來音念,“玉大人,你說我怎么就攤上這事躏敢∶品撸” “怎么了?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵件余,是天一觀的道長讥脐。 經(jīng)常有香客問我,道長啼器,這世上最難降的妖魔是什么旬渠? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮端壳,結(jié)果婚禮上告丢,老公的妹妹穿的比我還像新娘。我一直安慰自己损谦,他們只是感情好岖免,可當(dāng)我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著照捡,像睡著了一般颅湘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上栗精,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天闯参,我揣著相機與錄音,去河邊找鬼悲立。 笑死赢赊,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的级历。 我是一名探鬼主播释移,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼寥殖!你這毒婦竟也來了玩讳?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤嚼贡,失蹤者是張志新(化名)和其女友劉穎熏纯,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體粤策,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡樟澜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片秩贰。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡霹俺,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出毒费,到底是詐尸還是另有隱情丙唧,我是刑警寧澤,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布觅玻,位于F島的核電站想际,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏溪厘。R本人自食惡果不足惜胡本,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望畸悬。 院中可真熱鬧侧甫,春花似錦、人聲如沸傻昙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽妆档。三九已至僻爽,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間贾惦,已是汗流浹背胸梆。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留须板,地道東北人碰镜。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像习瑰,于是被迫代替她去往敵國和親绪颖。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,601評論 2 353

推薦閱讀更多精彩內(nèi)容