間截取全指南:日期函數(shù)、性能優(yōu)化與避坑實(shí)踐)
寫(xiě)SQL的時(shí)候凡是跟“時(shí)間”沾邊的需求十有八九都繞不開(kāi)“截取”這兩個(gè)字。不是把字符串剪幾刀而是把datetime、timestamp這類字段里藏的“年月日時(shí)分秒”按需拆出來(lái)或者干脆把整段時(shí)間歸攏到一個(gè)粒度上做統(tǒng)計(jì)。我見(jiàn)過(guò)太多人一上來(lái)就寫(xiě)WHERE CONVERT(varchar, create_time, 120) LIKE 2024-05%結(jié)果慢得一塌糊涂還查不對(duì)就是因?yàn)闆](méi)搞明白“截取時(shí)間段”三個(gè)層次的含義。這篇把從基礎(chǔ)函數(shù)到實(shí)戰(zhàn)寫(xiě)法再到性能坑一次性說(shuō)透不管你是SQL Server老用戶還是天天跟MySQL、Oracle打交道都能找到直接抄的寫(xiě)法。1. 先搞清楚“截取時(shí)間段”到底截的是什么很多新手一提“截取時(shí)間段”第一反應(yīng)就是字符串函數(shù)SUBSTRING、LEFT、RIGHT往上懟。這個(gè)思路不能說(shuō)錯(cuò)但很容易把自己繞進(jìn)去。數(shù)據(jù)庫(kù)里的時(shí)間字段本質(zhì)是一個(gè)數(shù)值存的是從某個(gè)基準(zhǔn)時(shí)間點(diǎn)開(kāi)始計(jì)數(shù)的間隔顯示成2024-05-18 14:30:00只是給人看的表象。所以真正的“截取”分三個(gè)層面拆零件、切區(qū)間、整粒度。1.1 時(shí)間字段里的“零件”年月日時(shí)分秒第一層需求最簡(jiǎn)單我給一個(gè)完整時(shí)間你幫我取年份、月份、日、小時(shí)。比如統(tǒng)計(jì)時(shí)想按年匯總業(yè)務(wù)上只關(guān)心2024后邊的月和日必須扔掉。這種拆零件操作SQL里提供了專門(mén)的日期函數(shù)常見(jiàn)的有YEAR()、MONTH()、DAY()SQL Server還有萬(wàn)能的DATEPART()。-- SQL Server / MySQL 通用寫(xiě)法 SELECT YEAR(create_time) AS 年, MONTH(create_time) AS 月, DAY(create_time) AS 日, DATEPART(HOUR, create_time) AS 小時(shí) -- SQL Server -- MySQL 用 HOUR(create_time) FROM orders;這塊的重點(diǎn)是函數(shù)返回的是整數(shù)不是日期。很多人拿YEAR()的結(jié)果再去跟字符串拼接或者反過(guò)來(lái)把字符串傳給YEAR()就容易踩類型轉(zhuǎn)換的坑。另外不同數(shù)據(jù)庫(kù)對(duì)函數(shù)名支持不一樣SQL Server有DATEPART、DATENAMEMySQL有EXTRACT(YEAR FROM ts)Oracle有TO_CHAR(ts, yyyy)。寫(xiě)法千差萬(wàn)別思路卻是同一個(gè)把時(shí)間對(duì)象里的某個(gè)分量抽出來(lái)。1.2 時(shí)間段截取的兩種典型場(chǎng)景第二層需求是“切區(qū)間”。業(yè)務(wù)經(jīng)常要的是“某一天”“某一周”“某一月”的數(shù)據(jù)而不是單獨(dú)的年或月數(shù)字。舉個(gè)例子查2024年5月所有訂單你說(shuō)這不簡(jiǎn)單嗎把月份拆出來(lái)等于5不就完事了-- 看似沒(méi)毛病的寫(xiě)法 SELECT * FROM orders WHERE MONTH(create_time) 5 AND YEAR(create_time) 2024;這寫(xiě)法結(jié)果沒(méi)錯(cuò)但性能極差因?yàn)镸ONTH(create_time)把create_time這一列整個(gè)“包”在函數(shù)里索引直接失效。這個(gè)坑后面專門(mén)講。正確的“切區(qū)間”思路是把查詢變成“從某時(shí)刻到某時(shí)刻”的范圍判斷核心是計(jì)算出區(qū)間的起止點(diǎn)。比如“2024年5月”在時(shí)間線上的真實(shí)范圍是2024-05-01 00:00:00到2024-05-31 23:59:59更嚴(yán)謹(jǐn)?shù)膶?xiě)法是 2024-05-01 AND 2024-06-01用“半開(kāi)區(qū)間”避免漏掉最后一秒的數(shù)據(jù)。1.3 字符截取 vs 日期函數(shù)我為什么建議直接用函數(shù)確實(shí)有人用字符串截取拿到想要的結(jié)果比如對(duì)2024-05-18 14:30:00用LEFT(create_time, 7)得到2024-05。但要清楚兩點(diǎn)第一這樣做依賴數(shù)據(jù)庫(kù)的時(shí)間顯示格式一旦數(shù)據(jù)庫(kù)的日期格式設(shè)置變化結(jié)果就可能錯(cuò)第二得到的2024-05是字符串不是日期后續(xù)如果再拿它做大小比較、加減運(yùn)算還得來(lái)回轉(zhuǎn)換非常麻煩。日期函數(shù)的優(yōu)勢(shì)是“語(yǔ)義明確、跨格式穩(wěn)定、后續(xù)處理空間大”。同樣是想按月份分組直接用DATE_FORMAT(create_time, %Y-%m)MySQL或者FORMAT(create_time, yyyy-MM)SQL Server一步到位返回的結(jié)果也是有序的字符串排序和顯示都好看。所以我的建議很直接能用日期函數(shù)就別用字符串硬切省心且不容易出幺蛾子。2. 主流數(shù)據(jù)庫(kù)的日期時(shí)間截取函數(shù)橫向?qū)Ρ燃热灰诰唧w的數(shù)據(jù)庫(kù)里寫(xiě)代碼就得把手頭的“武器”摸清楚。我用得最多的是SQL ServerMySQL和Oracle也常年接觸這里把三家的常用函數(shù)擺在一起對(duì)比方便遇到什么環(huán)境都能快速上手。本質(zhì)區(qū)別在于SQL Server把函數(shù)拆得很細(xì)MySQL習(xí)慣一個(gè)函數(shù)解決格式化Oracle則走TO_CHAR和TRUNC的路子。2.1 SQL Server函數(shù)最全也最容易“迷路”SQL Server的時(shí)間處理函數(shù)多到我剛接觸時(shí)也有點(diǎn)暈。核心就記這幾類取分量YEAR()、MONTH()、DAY()對(duì)應(yīng)DATEPART(yyyy, ts)、DATEPART(mm, ts)、DATEPART(dd, ts)。取名稱DATENAME(month, ts)返回“May”或“5月”這種本地化文本適合做報(bào)表表頭。格式化輸出CONVERT(varchar, ts, 120)得到2024-05-18 14:30:00FORMAT(ts, yyyy-MM-dd)更直觀但性能稍慢。截?cái)嗟教旖?jīng)典的DATEADD(day, DATEDIFF(day, 0, ts), 0)這招能把時(shí)間直接歸零到當(dāng)天零點(diǎn)。寫(xiě)SQL Server查詢時(shí)我有個(gè)習(xí)慣凡是涉及“某天之內(nèi)”的查詢統(tǒng)一用ts 2024-05-18 AND ts 2024-05-19而不是CONVERT(varchar, ts, 23) 2024-05-18。前者能走索引后者不能這是鐵律。2.2 MySQL函數(shù)名好記但格式串要小心MySQL的時(shí)間函數(shù)命名非常接地氣YEAR()、MONTH()、DAY()、HOUR()、MINUTE()、SECOND()看一眼就知道干嘛的。真正需要花心思的是DATE_FORMAT()的格式串用的是%Y、%m、%d這種寫(xiě)法跟SQL Server的yyyy、MM、dd完全兩樣兩邊切換時(shí)最容易寫(xiě)錯(cuò)。-- MySQL 按天分組統(tǒng)計(jì) SELECT DATE_FORMAT(create_time, %Y-%m-%d) AS day, COUNT(*) AS cnt FROM orders GROUP BY day;MySQL還有一個(gè)特別方便的函數(shù)叫DATE()作用是把2024-05-18 14:30:00直接變成2024-05-18。我經(jīng)常拿它跟BETWEEN配合做日粒度統(tǒng)計(jì)但要注意DATE(create_time)包了函數(shù)同樣有索引失效的問(wèn)題。MySQL 5.7以上支持虛擬列和函數(shù)索引能緩解但最省事的寫(xiě)法依然是范圍查詢。2.3 Oracle 和 PostgreSQLEXTRACT 與 TRUNC 的取舍Oracle里取時(shí)間分量首選EXTRACT(YEAR FROM ts)語(yǔ)法接近標(biāo)準(zhǔn)SQL但EXTRACT不能取季度和星期幾。處理“截?cái)嗟饺铡⒃隆⒛辍边@個(gè)需求時(shí)Oracle的王牌是TRUNC(ts)默認(rèn)截?cái)嗟疆?dāng)天零點(diǎn)還能指定粒度-- Oracle 截?cái)嗟皆?SELECT TRUNC(SYSDATE, MM) FROM dual;PostgreSQL跟Oracle思路類似同樣有EXTRACT和DATE_TRUNC。DATE_TRUNC(month, ts)返回當(dāng)月第一天的零點(diǎn)配合GROUP BY做統(tǒng)計(jì)特別順手。這三家對(duì)比下來(lái)你會(huì)發(fā)現(xiàn)沒(méi)有一個(gè)函數(shù)能包打天下關(guān)鍵是先想清楚需求是“取分量”還是“截?cái)嗟侥硞€(gè)粒度”再選對(duì)應(yīng)的函數(shù)思路比背函數(shù)名重要得多。3. 實(shí)操演練把時(shí)間段和日期從數(shù)據(jù)里準(zhǔn)確“挖”出來(lái)理論說(shuō)再多不如直接上代碼。我挑幾個(gè)實(shí)際項(xiàng)目里反復(fù)用到的場(chǎng)景從最簡(jiǎn)單的截取日期到按小時(shí)分組一步一步給你拆解。所有示例都給出SQL Server和MySQL雙版本方便對(duì)照遷移。3.1 截取到日查“某一天”的完整記錄這是最最常見(jiàn)的需求。你要統(tǒng)計(jì)某一天的訂單、某一天的用戶登錄記錄很多人的第一版SQL是這么寫(xiě)的-- 錯(cuò)誤示范函數(shù)包住索引列效率低下 SELECT * FROM orders WHERE CONVERT(varchar, create_time, 23) 2024-05-18;如果orders表有幾十萬(wàn)行以上這條SQL會(huì)全表掃描。正確姿勢(shì)是把“2024年5月18日”轉(zhuǎn)換為一個(gè)時(shí)間區(qū)間-- SQL Server / MySQL 通用正確寫(xiě)法 SELECT * FROM orders WHERE create_time 2024-05-18 00:00:00 AND create_time 2024-05-19 00:00:00;這里有個(gè)細(xì)節(jié)為什么不寫(xiě) 2024-05-18 23:59:59因?yàn)槿绻鹀reate_time帶毫秒或微秒23:59:59會(huì)漏掉23:59:59.500這種記錄。用 2024-05-19表達(dá)“5月18日整天”前閉后開(kāi)才是嚴(yán)謹(jǐn)?shù)膮^(qū)間。如果你只想取日期部分用于展示那再單獨(dú)用CONVERT(varchar, create_time, 23)或DATE_FORMAT(create_time, %Y-%m-%d)輸出不影響查詢條件走索引。3.2 截取到月、季度、年統(tǒng)計(jì)粒度自由切換按月統(tǒng)計(jì)是報(bào)表的標(biāo)配。正確邏輯是先把時(shí)間戳“壓平”到月份粒度再分組。SQL Server我習(xí)慣這么寫(xiě)-- SQL Server 按月統(tǒng)計(jì)銷(xiāo)售額 SELECT YEAR(create_time) AS 年份, MONTH(create_time) AS 月份, SUM(amount) AS 銷(xiāo)售額 FROM orders GROUP BY YEAR(create_time), MONTH(create_time) ORDER BY 年份, 月份;MySQL則更推薦用DATE_FORMAT一步到位-- MySQL 按月統(tǒng)計(jì) SELECT DATE_FORMAT(create_time, %Y-%m) AS 月份, SUM(amount) AS 銷(xiāo)售額 FROM orders GROUP BY DATE_FORMAT(create_time, %Y-%m) ORDER BY 月份;季度統(tǒng)計(jì)稍微繞一點(diǎn)SQL Server可以直接DATEPART(QUARTER, create_time)MySQL沒(méi)有專門(mén)的季度函數(shù)自己用MONTH整除算一下就行CONCAT(YEAR(create_time), -Q, CEIL(MONTH(create_time) / 3))。年統(tǒng)計(jì)最簡(jiǎn)單GROUP BY YEAR(create_time)搞定。這幾個(gè)例子的核心還是那句話分組維度是什么就用函數(shù)把時(shí)間“掰”到什么粒度。不要在GROUP BY里用那種返回字符串的長(zhǎng)格式函數(shù)比如DATE_FORMAT(create_time, %Y-%m-%d %H:%i:%s)它會(huì)讓聚合粒度精確到秒根本不是你要的月維度。3.3 截取時(shí)間區(qū)間按小時(shí)、半小時(shí)分組統(tǒng)計(jì)當(dāng)我需要看一天內(nèi)各時(shí)段的訪問(wèn)量就要把時(shí)間截取到小時(shí)。SQL Server這么寫(xiě)-- SQL Server 按小時(shí)統(tǒng)計(jì) SELECT DATEPART(HOUR, create_time) AS 小時(shí), COUNT(*) AS 訪問(wèn)量 FROM access_log WHERE create_time 2024-05-18 AND create_time 2024-05-19 GROUP BY DATEPART(HOUR, create_time) ORDER BY 小時(shí);MySQL的寫(xiě)法幾乎一樣只是把DATEPART(HOUR, ...)換成HOUR(...)。如果要按半小時(shí)分組就沒(méi)那么直接了。一個(gè)實(shí)用技巧是先把時(shí)間轉(zhuǎn)為當(dāng)天零點(diǎn)起的分鐘數(shù)或秒數(shù)再除以區(qū)間長(zhǎng)度取整-- MySQL 按半小時(shí)分組 SELECT DATE_FORMAT(create_time, %Y-%m-%d) AS 日期, FLOOR(HOUR(create_time) * 60 MINUTE(create_time)) DIV 30 AS 半小時(shí)序號(hào), COUNT(*) AS 訪問(wèn)量 FROM access_log GROUP BY 日期, 半小時(shí)序號(hào);這個(gè)寫(xiě)法的好處是不用拼接一堆字符串直接對(duì)數(shù)值分組性能也更好。拿半小時(shí)序號(hào)換算回具體時(shí)間區(qū)間也不難序號(hào)乘30就是當(dāng)天從零點(diǎn)起的分鐘偏移。3.4 格式化輸出把日期時(shí)間轉(zhuǎn)成業(yè)務(wù)友好的字符串很多時(shí)候我們不是要拿時(shí)間做條件而是要顯示給人看。這時(shí)候就涉及“日期轉(zhuǎn)字符”。各數(shù)據(jù)庫(kù)都有對(duì)應(yīng)的格式化函數(shù)這里做個(gè)小表方便查閱需求SQL ServerMySQLOracle轉(zhuǎn)成2024-05-18CONVERT(varchar(10), ts, 120)DATE_FORMAT(ts, %Y-%m-%d)TO_CHAR(ts, yyyy-mm-dd)轉(zhuǎn)成2024/05/18CONVERT(varchar(10), ts, 111)DATE_FORMAT(ts, %Y/%m/%d)TO_CHAR(ts, yyyy/mm/dd)轉(zhuǎn)成14:30時(shí)刻CONVERT(varchar(5), ts, 108)DATE_FORMAT(ts, %H:%i)TO_CHAR(ts, hh24:mi)轉(zhuǎn)成2024-05-18 14:30:00CONVERT(varchar(19), ts, 120)DATE_FORMAT(ts, %Y-%m-%d %H:%i:%s)TO_CHAR(ts, yyyy-mm-dd hh24:mi:ss)SQL Server的FORMAT(ts, yyyy-MM-dd)雖然可讀性好但它走的是.NET格式化性能比CONVERT慢不少在大量數(shù)據(jù)的報(bào)表里慎用。MySQL新版也提供了DATE_FORMAT之外的一些函數(shù)但DATE_FORMAT已經(jīng)足夠覆蓋絕大多數(shù)情況。4. 時(shí)間段截取查詢的性能與索引使用寫(xiě)完功能還得看性能。一段截取日期的SQL在十萬(wàn)行的小表上跑得飛快換到千萬(wàn)級(jí)大表就可能直接拖垮庫(kù)。這里分享我在性能優(yōu)化方面踩過(guò)的坑和總結(jié)的規(guī)律。4.1 不要在索引列上套函數(shù)等式會(huì)失效這是最經(jīng)典也最容易犯的問(wèn)題。WHERE YEAR(create_time) 2024聽(tīng)著很合理但如果create_time上建了索引這個(gè)查詢基本就用不上了。因?yàn)閿?shù)據(jù)庫(kù)要先把每一行的create_time都算一遍YEAR()才能跟2024比較索引里的有序結(jié)構(gòu)完全發(fā)揮不了作用。這跟查字典時(shí)非得把每個(gè)詞都先倒過(guò)來(lái)再找一樣效率不可能高。解決辦法就是前面反復(fù)在強(qiáng)調(diào)的把條件改寫(xiě)成范圍查詢。create_time 2024-01-01 AND create_time 2025-01-01。這樣優(yōu)化器能夠直接在索引上做區(qū)間掃描性能天差地別。別嫌啰嗦我見(jiàn)過(guò)太多慢SQL根源就是這一條。4.2 用半開(kāi)區(qū)間替代BETWEEN避免邊界遺漏還有一個(gè)很隱蔽的邊界問(wèn)題。寫(xiě)B(tài)ETWEEN 2024-05-18 00:00:00 AND 2024-05-18 23:59:59看似覆蓋了一整天但只要有記錄的毫秒數(shù)是23:59:59.500就會(huì)被漏掉。更可靠的方式是半開(kāi)區(qū)間-- 推薦寫(xiě)法 WHERE create_time 2024-05-18 00:00:00 AND create_time 2024-05-19 00:00:00;如果時(shí)間列本身是date類型不帶時(shí)分秒那BETWEEN 2024-05-18 AND 2024-05-18沒(méi)問(wèn)題但一旦換成datetime、timestamp就一定要小心。養(yǎng)成寫(xiě)半開(kāi)區(qū)間的習(xí)慣相當(dāng)于給自己買(mǎi)了一份“數(shù)據(jù)類型變更保險(xiǎn)”。我遇到過(guò)上線后數(shù)據(jù)對(duì)不上的事故排查到最后就是新舊表字段類型不一致導(dǎo)致的前車(chē)之鑒。4.3 隱式類型轉(zhuǎn)換字符串和日期的隱形坑再一個(gè)容易忽略的問(wèn)題是隱式轉(zhuǎn)換。比如代碼里傳進(jìn)來(lái)一個(gè)字符串2024-05-18直接拿來(lái)跟datetime列比較數(shù)據(jù)庫(kù)通常能自動(dòng)轉(zhuǎn)但轉(zhuǎn)換規(guī)則在不同數(shù)據(jù)庫(kù)里有細(xì)微差異。SQL Server遇到create_time 2024-05-18會(huì)自動(dòng)把字符串轉(zhuǎn)成2024-05-18 00:00:00看起來(lái)能用但如果你本意是查一整天這個(gè)寫(xiě)法只會(huì)查到一個(gè)瞬間數(shù)據(jù)一定不對(duì)。寫(xiě)法上要注意的是參數(shù)類型盡量跟列類型保持一致。查詢條件里傳日期就傳日期傳字符串就顯式轉(zhuǎn)換別指望數(shù)據(jù)庫(kù)“智能”處理。另外字符編碼、區(qū)域設(shè)置也會(huì)影響日期字符串的解析統(tǒng)一用YYYYMMDD這種無(wú)分隔符格式最安全比如20240518任何數(shù)據(jù)庫(kù)都不會(huì)認(rèn)錯(cuò)。5. 常見(jiàn)問(wèn)題排查與避坑心得最后集中回答一些新手甚至老手經(jīng)常栽跟頭的問(wèn)題。我整理了一個(gè)速查表把典型的錯(cuò)誤寫(xiě)法和正確寫(xiě)法放在一起方便日常查閱。5.1 分組結(jié)果里多出“時(shí)間尾巴”數(shù)據(jù)直接串組這個(gè)問(wèn)題常見(jiàn)的表現(xiàn)是按天分組統(tǒng)計(jì)發(fā)現(xiàn)某一天的數(shù)據(jù)被分到了相鄰兩天或者一天內(nèi)的數(shù)據(jù)被拆成了多條。原因通常是時(shí)間字段帶時(shí)分秒而你在GROUP BY里只用了YEAR()和MONTH()忘了處理“日”這一層或者用了DATE_FORMAT卻把格式串寫(xiě)錯(cuò)比如%Y-%m寫(xiě)成了%Y-%M在MySQL里%M返回英文月份名肉眼看著差不多分組結(jié)果卻是亂的。排查建議先單獨(dú)跑一條SELECT create_time, 截取后的結(jié)果 FROM 表把每一條的截取結(jié)果打出來(lái)跟分組結(jié)果對(duì)一下問(wèn)題一眼就能看出來(lái)。5.2 月份邊界與閏年2月29日去哪了涉及月份計(jì)算時(shí)還有個(gè)經(jīng)典問(wèn)題計(jì)算“上個(gè)月”或“下個(gè)月”的起止點(diǎn)。很多人拿當(dāng)前月份減1結(jié)果1月份減1變成0就出錯(cuò)了。正確做法是用數(shù)據(jù)庫(kù)的日期加減函數(shù)讓引擎自己處理跨年和大小月-- SQL Server 上個(gè)月第一天 SELECT DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()) - 1, 0); -- MySQL 上個(gè)月第一天 SELECT DATE_FORMAT(DATE_SUB(CURDATE(), INTERVAL 1 MONTH), %Y-%m-01);閏年也是一樣2月29日只在閏年存在如果代碼里寫(xiě)死“每月29號(hào)”非閏年就會(huì)報(bào)錯(cuò)或者返回空。我的習(xí)慣是永遠(yuǎn)用函數(shù)算日期不要自己拼。自己拼字符串搞日期運(yùn)算十有八九會(huì)栽在跨月和閏年上。5.3 服務(wù)器時(shí)區(qū)問(wèn)題明明同一天查詢差8小時(shí)還有一個(gè)經(jīng)常被忽略的“隱形殺手”時(shí)區(qū)。數(shù)據(jù)庫(kù)服務(wù)器和業(yè)務(wù)服務(wù)器如果不在一個(gè)時(shí)區(qū)或者數(shù)據(jù)庫(kù)連接的time_zone設(shè)置不一致你查出來(lái)的“今天”就不是業(yè)務(wù)眼里的“今天”。尤其MySQL的timestamp類型會(huì)做時(shí)區(qū)轉(zhuǎn)換datetime不會(huì)兩者混用就可能出現(xiàn)數(shù)據(jù)錯(cuò)亂。排查思路是先確認(rèn)數(shù)據(jù)庫(kù)全局時(shí)區(qū)、會(huì)話時(shí)區(qū)、連接字符串里的時(shí)區(qū)參數(shù)三者必須一致。生產(chǎn)環(huán)境我強(qiáng)烈建議統(tǒng)一用UTC存儲(chǔ)展示層再轉(zhuǎn)本地時(shí)間。這樣至少保證數(shù)據(jù)本身不帶歧義。常見(jiàn)問(wèn)題典型原因解決思路查詢結(jié)果慢索引列被函數(shù)包裹改為范圍查詢當(dāng)天的數(shù)據(jù)查不全BETWEEN邊界沒(méi)覆蓋毫秒用半開(kāi)區(qū)間按天分組串組截取粒度不對(duì)或格式串寫(xiě)錯(cuò)打印截取結(jié)果核對(duì)月份計(jì)算錯(cuò)誤自己拼日期的月用DATEADD/INTERVAL時(shí)間差8小時(shí)時(shí)區(qū)不一致統(tǒng)一UTC或統(tǒng)一時(shí)區(qū)字符轉(zhuǎn)日期報(bào)錯(cuò)格式與數(shù)據(jù)庫(kù)設(shè)置不符用無(wú)分隔符YYYYMMDD踩過(guò)這些坑之后再回過(guò)頭看SQL里截取時(shí)間段和日期核心就兩句話一是用函數(shù)把時(shí)間“掰”到想要的粒度二是查詢條件永遠(yuǎn)寫(xiě)成對(duì)索引友好的范圍判斷。前一句管功能正確后一句管性能不崩。我在實(shí)際項(xiàng)目中處理過(guò)很多報(bào)表查詢優(yōu)化發(fā)現(xiàn)絕大多數(shù)慢SQL都是栽在第二句上。建議你寫(xiě)完SQL之后回過(guò)頭檢查一遍看到WHERE條件里日期列被函數(shù)包著就條件反射地改寫(xiě)成范圍區(qū)間這習(xí)慣能幫你省掉不少深夜救火的麻煩。