[英]How to optimize Mysql Select Query with Union?
我正在使用InnoDb存儲引擎處理mysql數據庫。 我的表結構如下:
表名稱:archiveincomingsms
的索引詳細信息:archiveincomingsms
表名稱:archiveoutgoingsms
的索引詳細信息:archiveoutgoingsms
以上是我的表結構及其索引詳細信息!
兩個表的最小記錄分別超過10億條。
現在的問題是,當我想執行以下SqlQuery時:
( SELECT id AS ID, `recieved_datetime` `Date`, 'MT' AS Type, src_adress AS Msisdn,
TEXT as text, CHAR_LENGTH(TEXT) AS QtyOfSymbols, 'OK' AS `Status`
FROM archiveincomingsms
WHERE 1=1
AND recieved_datetime BETWEEN '2015-06-14 00:00:00'
AND '2015-07-14 23:59:59'
)
UNION ALL
( SELECT id AS ID, `send_date` `Date`, 'MO' AS Type, scr_adress AS Msisdn,
TEXT as text, CHAR_LENGTH(TEXT) AS QtyOfSymbols, 'OK' AS `Status`
FROM archiveoutgoingsms
WHERE 1=1
AND send_date BETWEEN '2015-06-14 00:00:00'
AND '2015-07-14 23:59:59'
)
ORDER BY `Date` ASC
LIMIT 0 ,100
以上查詢需要30秒鍾以上的時間才能從表中獲取數據。 還有一個是我也必須為分頁目的計算行數,這還需要30秒鍾以上的時間。
總體而言,執行需要超過1分鍾的時間。 有什么合適的方法可以優化時間嗎? 我必須記下最多5秒。 ! 怎么可能呢? 我正在使用mysql數據庫!
我看不到索引。
請查詢結果:
EXPLAIN (SELECT id AS ID,`recieved_datetime` `Date`,'MT' AS Type,src_adress AS Msisdn,TEXT as text,CHAR_LENGTH(TEXT) AS QtyOfSymbols,'OK' AS `Status` FROM archiveincomingsms
WHERE 1=1 AND recieved_datetime BETWEEN '2015-06-14 00:00:00' AND '2015-07-14 23:59:59')
UNION ALL
(SELECT id AS ID,`send_date` `Date`,'MO' AS Type,scr_adress AS Msisdn,TEXT as text,CHAR_LENGTH(TEXT) AS QtyOfSymbols,'OK' AS `Status` FROM archiveoutgoingsms
WHERE 1=1 AND send_date BETWEEN '2015-06-14 00:00:00' AND '2015-07-14 23:59:59') ORDER BY `Date` ASC LIMIT 0 ,100
什么時候使用
ORDER BY `id` ASC
插入
ORDER BY `Date` ASC
?
這是此查詢說明的結果!
圖片可以通過在新標簽頁中打開來查看! 這里太小了!
MySQL不知道在UNION的第一部分中不使用索引recieved_datetime
您可以告訴MYSQL強制使用索引
查詢1:
EXPLAIN (SELECT id AS ID,`recieved_datetime` `Date`,'MT' AS Type,src_adress AS Msisdn,TEXT as text,CHAR_LENGTH(TEXT) AS QtyOfSymbols,'OK' AS `Status` FROM archiveincomingsms FORCE INDEX(recieved_datetime)
WHERE 1=1 AND recieved_datetime BETWEEN '2015-06-14 00:00:00' AND '2015-07-14 23:59:59')
UNION ALL
(SELECT id AS ID,`send_date` `Date`,'MO' AS Type,scr_adress AS Msisdn,TEXT as text,CHAR_LENGTH(TEXT) AS QtyOfSymbols,'OK' AS `Status` FROM archiveoutgoingsms
WHERE 1=1 AND send_date BETWEEN '2015-06-14 00:00:00' AND '2015-07-14 23:59:59') ORDER BY `Date` ASC LIMIT 0 ,100
查詢2:
(SELECT id AS ID,`recieved_datetime` `Date`,'MT' AS Type,src_adress AS Msisdn,TEXT as text,CHAR_LENGTH(TEXT) AS QtyOfSymbols,'OK' AS `Status` FROM archiveincomingsms FORCE INDEX(recieved_datetime)
WHERE 1=1 AND recieved_datetime BETWEEN '2015-06-14 00:00:00' AND '2015-07-14 23:59:59')
UNION ALL
(SELECT id AS ID,`send_date` `Date`,'MO' AS Type,scr_adress AS Msisdn,TEXT as text,CHAR_LENGTH(TEXT) AS QtyOfSymbols,'OK' AS `Status` FROM archiveoutgoingsms
WHERE 1=1 AND send_date BETWEEN '2015-06-14 00:00:00' AND '2015-07-14 23:59:59') ORDER BY `Date` ASC LIMIT 0 ,100
怎么用時間戳代替日期時間2015-06-14 00:00:00
請提供SHOW CREATE TABLE archiveincomingsms;
-您所提供的內容更加清晰易讀。 另外,它還顯示引擎和索引。
對於UNION
,您需要
INDEX(send_date),
INDEX(received_date)
但是,由於您要查看一個月的數據量,因此這可能是表格的重要部分。 如果它超過了表的20%,它將進行一次表掃描(或兩次掃描),這意味着UNION並沒有幫助。
旁注:代替
AND send_date BETWEEN '2015-06-14 00:00:00'
AND '2015-07-14 23:59:59'
這樣會更加干凈和容易:
AND send_date >= '2015-06-14'
AND send_date < '2015-06-14' + INTERVAL 1 MONTH
並且會自動處理短短的月份和年份邊界。
表名很難區分。 他們都看起來像
archivemiscellanyms
哦,我還沒有完成(現在我凝視着表名以查看它們是否不同)...
將ORDER BY...LIMIT
添加到UNION
每個部分:
( SELECT ... ORDER BY received_datetime LIMIT 100 )
UNION ALL
( SELECT ... ORDER BY send_date LIMIT 100 )
ORDER BY date LIMIT 0, 100
如果要“分頁”(彎曲),請在第3頁上執行此操作:
( SELECT ... ORDER BY received_datetime LIMIT 300 )
UNION ALL
( SELECT ... ORDER BY send_date LIMIT 300 )
ORDER BY date LIMIT 200, 100
現在索引將可用。 但是,隨着用戶從一頁到另一頁,它將變得越來越慢。
你也許可以加快由“記住你離開的地方”后續頁面。 看我的分頁博客 ;
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.