2012-10-01 83 views
0

我想優化一個需要運行近30分鐘的查詢。我想要做的是利用分區修剪來最小化搜索到的行。表格分區變量的範圍是來自不同表格的變量。看來,MySQL正在搜索所有分區。 (我不知道int(x)是不是改變了int的大小,我在設計這個表之前已經知道了這個表。更好的,並沒有固定的話)MySQL分區修剪變量

expectedEvent | CREATE TABLE `expectedevent` (
`id` int(11) NOT NULL AUTO_INCREMENT, 
`eventId` int(5) NOT NULL, 
`unitGroup_id` int(6) NOT NULL, 
`minOccur` int(9) NOT NULL, 
`periodInDays` int(4) NOT NULL, 
PRIMARY KEY (`id`), 
KEY `eventId` (`eventId`), 
KEY `unitGroup_id` (`unitGroup_id`), 
CONSTRAINT `expectedevent_ibfk_1` FOREIGN KEY (`unitGroup_id`) REFERENCES `unitgroup` (`id`) 

event_message | CREATE TABLE `event_message` (
`id` int(11) NOT NULL AUTO_INCREMENT, 
`unitId` varchar(15) NOT NULL, 
`eventId` smallint(6) NOT NULL, 
`eventName` varchar(50) NOT NULL, 
`gpsDateTime` datetime NOT NULL, 
`weekInfo` tinyint(4) NOT NULL, 
`odometer` int(11) NOT NULL, 
... 
KEY `id` (`id`), 
KEY `unitId` (`unitId`,`eventId`), 
KEY `eventId` (`eventId`) 
... 
!50100 PARTITION BY RANGE (weekInfo) 
ARTITION p0 VALUES LESS THAN (1) ENGINE = InnoDB, 
ARTITION p1 VALUES LESS THAN (2) ENGINE = InnoDB, 

unitGroup | CREATE TABLE `unitgroup` (
`id` int(11) NOT NULL AUTO_INCREMENT, 
`name` varchar(60) DEFAULT NULL, 

unitGroup_devices | CREATE TABLE `unitg 
`id` int(11) NOT NULL AUTO_INCREMENT, 
`unitGroup_id` int(11) NOT NULL, 
`scopeDevice_id` int(11) NOT NULL, 
PRIMARY KEY (`id`), 
KEY `unitGroup_id` (`unitGroup_id`), 
KEY `scopeDevice_id` (`scopeDevice_id`) 
... 

這個查詢需要近30分鐘(的選擇,沒有解釋):

explain partitions 
select ee.eventId, scopeDevice_id as scopeDevId, sd.unitId as unitId, count(em.id) as evtCount, minOccur, periodInDays 
from expectedEvent ee left join unitGroup ug on ee.unitGroup_id=ug.id 
left join unitGroup_devices ugd on ug.id=ugd.unitGroup_id 
left join scopeDevice sd on ugd.scopeDevice_id=sd.id 
left join event_message em on sd.unitId=em.unitId and em.eventId=ee.eventId 
where gpsDateTime>=DATE_SUB(DATE(now()),INTERVAL periodInDays DAY) 
and weekInfo>=WEEKOFYEAR(DATE_SUB(DATE(now()),INTERVAL periodInDays DAY)) 
and weekInfo <=WEEKOFYEAR(DATE(now())) 
group by ee.id, ugd.scopeDevice_id; 

| id | select_type | table | partitions                                    | type | possible_keys    | key   | key_len | ref           | rows | Extra       | 
+----+-------------+-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+--------+-----------------------------+--------------+---------+------------------------------------------------+------+---------------------------------+ 
| 1 | SIMPLE  | ee | NULL                                      | ALL | eventId,unitGroup_id  | NULL   | NULL | NULL           | 1 | Using temporary; Using filesort | 
| 1 | SIMPLE  | ug | NULL                                      | eq_ref | PRIMARY      | PRIMARY  | 4  | navsat_scope.ee.unitGroup_id     | 1 | Using where; Using index  | 
| 1 | SIMPLE  | ugd | NULL                                      | ref | unitGroup_id,scopeDevice_id | unitGroup_id | 4  | navsat_scope.ee.unitGroup_id     | 11 | Using where      | 
| 1 | SIMPLE  | sd | NULL                                      | eq_ref | PRIMARY,unitId    | PRIMARY  | 4  | navsat_scope.ugd.scopeDevice_id    | 1 | Using where      | 
| 1 | SIMPLE  | em | p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14,p15,p16,p17,p18,p19,p20,p21,p22,p23,p24,p25,p26,p27,p28,p29,p30,p31,p32,p33,p34,p35,p36,p37,p38,p39,p40 | ref | unitId,eventId    | unitId  | 19  | navsat_scope.sd.unitId,navsat_scope.ee.eventId | 682 | Using where      | 
+----+-------------+-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+--------+-----------------------------+--------------+---------+------------------------------------------------+------+---------------------------------+ 

有在expectedEvent只有一個條目,所以它基本上是相當於做了以下。 這個查詢需要大約3分鐘(選擇,不解釋):

select ee.eventId, scopeDevice_id as scopeDevId, sd.unitId as unitId, count(em.id) as evtCount, minOccur, periodInDays 
from expectedEvent ee left join unitGroup ug on ee.unitGroup_id=ug.id 
left join unitGroup_devices ugd on ug.id=ugd.unitGroup_id 
left join scopeDevice sd on ugd.scopeDevice_id=sd.id 
left join event_message em on sd.unitId=em.unitId and em.eventId=ee.eventId 
where gpsDateTime>="2012-09-29" 
and weekInfo>=WEEKOFYEAR("2012-09-29") 
and weekInfo <=WEEKOFYEAR("2012-10-01") 
group by ee.id, ugd.scopeDevice_id; 

| id | select_type | table | partitions | type | possible_keys    | key   | key_len | ref           | rows | Extra       | 
+----+-------------+-------+------------+--------+-----------------------------+--------------+---------+------------------------------------------------+------+---------------------------------+ 
| 1 | SIMPLE  | ee | NULL  | ALL | eventId,unitGroup_id  | NULL   | NULL | NULL           | 1 | Using temporary; Using filesort | 
| 1 | SIMPLE  | ug | NULL  | eq_ref | PRIMARY      | PRIMARY  | 4  | navsat_scope.ee.unitGroup_id     | 1 | Using where; Using index  | 
| 1 | SIMPLE  | ugd | NULL  | ref | unitGroup_id,scopeDevice_id | unitGroup_id | 4  | navsat_scope.ee.unitGroup_id     | 11 | Using where      | 
| 1 | SIMPLE  | sd | NULL  | eq_ref | PRIMARY,unitId    | PRIMARY  | 4  | navsat_scope.ugd.scopeDevice_id    | 1 | Using where      | 
| 1 | SIMPLE  | em | p39,p40 | ref | unitId,eventId    | unitId  | 19  | navsat_scope.sd.unitId,navsat_scope.ee.eventId | 682 | Using where      | 
+----+-------------+-------+------------+--------+-----------------------------+--------------+---------+------------------------------------------------+------+---------------------------------+ 

我的解決方法,想法在我的C#應用​​程序讀取expectedEvent表,然後再建設有實際日期查詢,而不是變量。

不過我寧願在MySQL中這樣做。我如何優化查詢? ExpectedEvent最終將包含很多行。

謝謝!

+1

準備SQL語句時,「分區修剪」完成;基本上,你需要解析時已知的值;這些不能是執行時間之前未知的值。當執行SQL語句時,MySQL首先準備執行計劃,然後執行計劃。 – spencer7593

+0

Spencer,如果您將其從評論移至答案,我會將其標記爲答案。感謝您的見解!!理解時更有意義! –

+0

看起來你還欠@spencer一個閃亮的綠色複選標記。 :) – Air

回答

2

當準備SQL語句(當MySQL爲語句生成執行計劃時),MySQL會進行「分區修剪」。爲了將「分區修剪」作爲計劃的一部分,MySQL需要解析時已知的值;對於分析時未知的值,不執行分區修剪。

當MySQL執行SQL語句時,MySQL首先準備一個執行計劃,然後執行該計劃。 「分區修剪」是在準備階段決定的執行計劃的細節。 (這就是爲什麼你的聲明與謂詞顯示分區修剪常量,但你的說法有引用欄不顯示任何修剪。)

1

你需要在WHERE子句中使用的實際值,無法獲得通過JOIN值。考慮一下,當MySQL準備查詢的執行計劃時,它不知道表中的值是什麼值爲gpsDateTime。因此無法知道它只需要查詢中的某些分區來獲取所需的數據。

在你情況下,將更快事先得到與查詢的過濾器使用的日期值,然後作出選擇查詢的時候,就像你在你的第二個例子做過使用的實際值。