SQL?中日期的特殊性總結(jié)(格式符嚴格要求全大寫)
SQL中日期的特殊性總結(jié)
在SQL中,日期是一種特殊的數(shù)據(jù)類型,既有數(shù)值的特性,又有字符串的表現(xiàn)形式,使用時有諸多需要注意的地方。
一、日期數(shù)據(jù)類型的特點
| 特性 | 說明 | 示例 |
|---|---|---|
| 存儲格式 | 內(nèi)部存儲為數(shù)字(從某個基準日期開始的天數(shù)/秒數(shù)) | Oracle: 4712-01-01 起的天數(shù) |
| 顯示格式 | 由數(shù)據(jù)庫參數(shù)控制,不一定是輸入時的格式 | Oracle: 17-12月-80 |
| 運算能力 | 支持加減運算(天數(shù)/月數(shù)/年數(shù)) | HIREDATE + 30(30天后) |
| 比較能力 | 支持 <, >, =, BETWEEN 等比較操作 | HIREDATE > TO_DATE('1981-01-01') |
二、日期與字符串的轉(zhuǎn)換(最重要)
核心函數(shù)
| 函數(shù) | 方向 | 用途 |
|---|---|---|
TO_DATE(字符串, 格式) | 字符串 → 日期 | 將字符串按指定格式解析為日期類型 |
TO_CHAR(日期, 格式) | 日期 → 字符串 | 將日期按指定格式轉(zhuǎn)換為字符串 |
常用日期格式元素
| 格式符 | 含義 | 示例 |
|---|---|---|
| YYYY | 四位年份 | 1981 |
| YY | 兩位年份 | 81 |
| MM | 兩位月份 | 05 |
| MON | 月份縮寫(中文環(huán)境為'5月') | '5月' |
| MONTH | 月份全稱 | '5月' |
| DD | 兩位日期 | 01 |
| DAY | 星期幾 | '星期三' |
| HH24 | 24小時制 | 14 |
| MI | 分鐘 | 30 |
| SS | 秒鐘 | 45 |
在SQL中,日期格式符是區(qū)分大小寫的,這是一個非常重要的細節(jié),寫錯了會導(dǎo)致轉(zhuǎn)換失敗或結(jié)果錯誤。
核心規(guī)則:格式符嚴格區(qū)分大小寫
| 格式符 | 含義 | 正確示例 | 錯誤示例(大小寫錯誤) |
|---|---|---|---|
MM | 月份(01-12) | TO_CHAR(date, 'MM') → 04 | mm → ? 報錯或無效 |
MI | 分鐘(00-59) | TO_CHAR(date, 'MI') → 30 | Mi / mi → ? |
HH24 | 24小時制(00-23) | TO_CHAR(date, 'HH24') → 14 | hh24 → ? |
HH12 / HH | 12小時制(01-12) | TO_CHAR(date, 'HH12') → 02 | hh12 → ? |
YYYY | 四位年份 | TO_CHAR(date, 'YYYY') → 2026 | yyyy → ? |
YY | 兩位年份 | TO_CHAR(date, 'YY') → 26 | yy → ? |
MON | 月份縮寫(如'4月') | TO_CHAR(date, 'MON') → 4月 | Mon / mon → ? |
MONTH | 月份全稱(如'4月') | TO_CHAR(date, 'MONTH') → 4月 | Month → ? |
DD | 日期(01-31) | TO_CHAR(date, 'DD') → 23 | dd → ? |
DY | 星期縮寫(如'周三') | TO_CHAR(date, 'DY') → 周三 | dy → ? |
DAY | 星期全稱(如'星期三') | TO_CHAR(date, 'DAY') → 星期三 | Day → ? |
示例代碼
-- TO_DATE:字符串轉(zhuǎn)日期
TO_DATE('1981-05-01', 'YYYY-MM-DD') -- 返回日期:1981年5月1日
TO_DATE('19810501', 'YYYYMMDD') -- 返回日期:1981年5月1日
TO_DATE('1981-05', 'YYYY-MM') -- 返回日期:1981年5月1日(默認當月1號)
-- TO_CHAR:日期轉(zhuǎn)字符串
TO_CHAR(HIREDATE, 'YYYY-MM-DD') -- '1981-05-01'
TO_CHAR(HIREDATE, 'YYYYMM') -- '198105'
TO_CHAR(HIREDATE, 'MON DD, YYYY') -- '5月 01, 1981'三、日期比較的特殊性
1. 不能直接用字符串比較日期
-- ? 錯誤:字符串 '1981' 和日期類型不能直接比較
SELECT * FROM EMP WHERE HIREDATE = '1981';
-- ? 正確方式1:轉(zhuǎn)換日期為字符串比較
SELECT * FROM EMP WHERE TO_CHAR(HIREDATE, 'YYYY') = '1981';
-- ? 正確方式2:字符串轉(zhuǎn)日期比較
SELECT * FROM EMP WHERE HIREDATE >= TO_DATE('1981-01-01', 'YYYY-MM-DD')
AND HIREDATE < TO_DATE('1982-01-01', 'YYYY-MM-DD');2. 日期比較的邊界問題(重要??)
-- 查詢1981年入職的員工(錯誤寫法)
SELECT * FROM EMP
WHERE TO_CHAR(HIREDATE, 'YYYY') = 1981; -- ? 可行,但效率低
-- 查詢1981年入職的員工(正確寫法 - 使用范圍)
SELECT * FROM EMP
WHERE HIREDATE >= TO_DATE('1981-01-01', 'YYYY-MM-DD')
AND HIREDATE < TO_DATE('1982-01-01', 'YYYY-MM-DD');
-- 查詢1981年5月入職(錯誤寫法)
WHERE HIREDATE BETWEEN TO_DATE('1981-05-01', 'YYYY-MM-DD')
AND TO_DATE('1981-05-31', 'YYYY-MM-DD'); -- ?? 漏掉了5月31日23:59:59之后的數(shù)據(jù)
-- 查詢1981年5月入職(正確寫法)
WHERE HIREDATE >= TO_DATE('1981-05-01', 'YYYY-MM-DD')
AND HIREDATE < TO_DATE('1981-06-01', 'YYYY-MM-DD');邊界寫法參考
| 需求 | TO_CHAR 寫法 | TO_DATE 范圍寫法 |
|---|---|---|
| 年份 = 1981 | TO_CHAR(HIREDATE,'YYYY') = 1981 | HIREDATE >= TO_DATE('1981-01-01','YYYY-MM-DD') AND HIREDATE < TO_DATE('1982-01-01','YYYY-MM-DD') |
| 年份 < 1982 | TO_CHAR(HIREDATE,'YYYY') < 1982 | HIREDATE < TO_DATE('1982-01-01','YYYY-MM-DD') |
| 年份 <= 1982 | TO_CHAR(HIREDATE,'YYYY') <= 1982 | HIREDATE < TO_DATE('1983-01-01','YYYY-MM-DD') |
| 年份 > 1981 | TO_CHAR(HIREDATE,'YYYY') > 1981 | HIREDATE >= TO_DATE('1982-01-01','YYYY-MM-DD') |
| 年份 >= 1982 | TO_CHAR(HIREDATE,'YYYY') >= 1982 | HIREDATE >= TO_DATE('1982-01-01','YYYY-MM-DD') |
四、日期的加減運算
| 運算 | 含義 | 示例 |
|---|---|---|
日期 + 數(shù)字 | 增加天數(shù) | HIREDATE + 30(30天后) |
日期 - 數(shù)字 | 減少天數(shù) | HIREDATE - 7(7天前) |
日期1 - 日期2 | 相差天數(shù) | SYSDATE - HIREDATE(入職天數(shù)) |
ADD_MONTHS(日期, 數(shù)字) | 增加月份 | ADD_MONTHS(HIREDATE, 6)(6個月后) |
MONTHS_BETWEEN(日期1, 日期2) | 相差月數(shù) | MONTHS_BETWEEN(SYSDATE, HIREDATE) |
示例代碼
-- 計算員工入職天數(shù) SELECT ENAME, SYSDATE - HIREDATE AS 工作天數(shù) FROM EMP; -- 計算員工入職月數(shù) SELECT ENAME, MONTHS_BETWEEN(SYSDATE, HIREDATE) AS 工作月數(shù) FROM EMP; -- 查詢?nèi)肼毘^30年的員工 SELECT * FROM EMP WHERE ADD_MONTHS(HIREDATE, 30*12) < SYSDATE;
五、日期函數(shù)對比(Oracle vs MySQL)
| 功能 | Oracle | MySQL |
|---|---|---|
| 當前日期時間 | SYSDATE | NOW() / CURDATE() |
| 提取年份 | TO_CHAR(date, 'YYYY') | YEAR(date) |
| 提取月份 | TO_CHAR(date, 'MM') | MONTH(date) |
| 日期加減天數(shù) | date + 10 | DATE_ADD(date, INTERVAL 10 DAY) |
| 日期差(天數(shù)) | date1 - date2 | DATEDIFF(date1, date2) |
| 增加月份 | ADD_MONTHS(date, 6) | DATE_ADD(date, INTERVAL 6 MONTH) |
六、常見陷阱與最佳實踐
? 常見錯誤
-- 1. 直接比較字符串和日期 WHERE HIREDATE = '1981-05-01' -- 隱式轉(zhuǎn)換可能失敗 -- 2. 使用 BETWEEN 包含結(jié)束日期(會丟失當天23:59:59后的數(shù)據(jù)) WHERE HIREDATE BETWEEN '1981-05-01' AND '1981-05-31' -- 3. TO_CHAR 寫在 WHERE 條件的左邊(無法使用索引) WHERE TO_CHAR(HIREDATE, 'YYYY') = '1981' -- 4. 忽略時區(qū)問題 WHERE CREATE_TIME = '2026-04-23' -- 可能漏掉帶時分秒的記錄
? 最佳實踐
-- 1. 始終使用顯式轉(zhuǎn)換
WHERE HIREDATE >= TO_DATE('1981-05-01', 'YYYY-MM-DD')
AND HIREDATE < TO_DATE('1981-06-01', 'YYYY-MM-DD')
-- 2. 范圍查詢使用左閉右開區(qū)間
WHERE HIREDATE >= TRUNC(SYSDATE - 30) -- 30天前零點
AND HIREDATE < TRUNC(SYSDATE) -- 今天零點
-- 3. 讓函數(shù)作用在常量上,保持索引有效
WHERE HIREDATE >= TO_DATE('1981-01-01', 'YYYY-MM-DD') -- ? 索引有效
WHERE TO_CHAR(HIREDATE, 'YYYY') = '1981' -- ? 索引失效
-- 4. 使用 TRUNC 去掉時間部分
WHERE TRUNC(HIREDATE) = TO_DATE('1981-05-01', 'YYYY-MM-DD')使用 TRUNC 去掉時間部分是什么意思
TRUNC 是一個用于截斷日期或數(shù)字的函數(shù)。在日期處理中,"去掉時間部分"是指將日期中的時、分、秒清零,只保留年、月、日。
為什么需要"去掉時間部分"?問題場景:日期比較的陷阱
-- 假設(shè)表中有一條記錄,HIREDATE = 1981-05-01 14:30:00
-- ? 錯誤:這條記錄會被漏掉!
SELECT * FROM EMP
WHERE HIREDATE = TO_DATE('1981-05-01', 'YYYY-MM-DD');
-- 因為左邊有 14:30:00,右邊是 00:00:00,不相等
-- ? 解法1:使用 TRUNC 去掉時間部分
SELECT * FROM EMP
WHERE TRUNC(HIREDATE) = TO_DATE('1981-05-01', 'YYYY-MM-DD');
-- ? 解法2:使用范圍查詢(更推薦,索引友好)
SELECT * FROM EMP
WHERE HIREDATE >= TO_DATE('1981-05-01', 'YYYY-MM-DD')
AND HIREDATE < TO_DATE('1981-05-02', 'YYYY-MM-DD');TRUNC 的常用格式
| 用法 | 結(jié)果 | 說明 |
|---|---|---|
TRUNC(SYSDATE) | 2026-04-24 00:00:00 | 截斷到當天開始(默認) |
TRUNC(SYSDATE, 'DD') | 2026-04-24 00:00:00 | 同上,DD表示天 |
TRUNC(SYSDATE, 'MM') | 2026-04-01 00:00:00 | 截斷到當月第一天 |
TRUNC(SYSDATE, 'Q') | 2026-04-01 00:00:00 | 截斷到當季第一天 |
TRUNC(SYSDATE, 'YYYY') | 2026-01-01 00:00:00 | 截斷到當年第一天 |
TRUNC(SYSDATE, 'HH24') | 2026-04-24 14:00:00 | 截斷到當前小時開始 |
對比總結(jié)
| 函數(shù) | 作用 | 示例輸入 | 示例輸出 |
|---|---|---|---|
TRUNC(date) | 去掉時間部分(歸零) | 2026-04-24 14:35:28 | 2026-04-24 00:00:00 |
TO_CHAR(date, 'YYYY-MM-DD') | 轉(zhuǎn)為字符串(丟失時間) | 2026-04-24 14:35:28 | '2026-04-24' |
ROUND(date) | 四舍五入到天 | 2026-04-24 14:35:28 | 2026-04-25 00:00:00 |
TRUNC 會讓索引失效(類似 TO_CHAR)
-- ? 索引失效
WHERE TRUNC(HIREDATE) = TO_DATE('1981-05-01', 'YYYY-MM-DD')
-- ? 推薦:范圍查詢(索引有效)
WHERE HIREDATE >= TO_DATE('1981-05-01', 'YYYY-MM-DD')
AND HIREDATE < TO_DATE('1981-05-02', 'YYYY-MM-DD')原則:能不用 TRUNC 在 WHERE 條件中就不用,除非數(shù)據(jù)量很小或沒有時間精度要求。
TRUNC 常用于 GROUP BY 分組
-- 按天統(tǒng)計(即使數(shù)據(jù)庫存了時分秒) SELECT TRUNC(HIREDATE) AS 入職日期, COUNT(*) AS 人數(shù) FROM EMP GROUP BY TRUNC(HIREDATE);
一句話總結(jié)
TRUNC去掉時間部分 = 把14:35:28變成00:00:00
用于忽略時分秒的干擾,讓日期比較只看年月日。
但在WHERE中要小心使用,因為它和TO_CHAR一樣會讓索引失效,大數(shù)據(jù)量時建議用范圍查詢替代。
七、快速參考卡片
| 需求 | SQL寫法 |
|---|---|
| 當前系統(tǒng)日期 | SYSDATE(Oracle)/ CURDATE()(MySQL) |
| 年月日格式 | 'YYYY-MM-DD' |
| 字符串→日期 | TO_DATE('1981-05-01', 'YYYY-MM-DD') |
| 日期→字符串 | TO_CHAR(HIREDATE, 'YYYY-MM-DD') |
| 提取年份 | TO_CHAR(HIREDATE, 'YYYY') |
| 提取年月 | TO_CHAR(HIREDATE, 'YYYYMM') |
| 某月第一天 | TRUNC(HIREDATE, 'MM') |
| 某年第一天 | TRUNC(HIREDATE, 'YYYY') |
| 月底最后一天 | LAST_DAY(HIREDATE) |
| 下個月同一天 | ADD_MONTHS(HIREDATE, 1) |
八、你在作業(yè)中的日期問題總結(jié)
-- 第3題 ? 正確 WHERE TO_CHAR(HIREDATE, 'YYYYMM') < 198210 -- 第5題 ?? 缺少括號(結(jié)果正確但不規(guī)范) WHERE DEPTNO=20 AND TO_CHAR(HIREDATE,'YYYY')<1982 OR DEPTNO=30 AND TO_CHAR(HIREDATE,'YYYY')<1985 -- 第11題 ? 完全遺漏WHERE條件 -- 應(yīng)該加:WHERE TO_CHAR(HIREDATE, 'YYYY') > 1981
核心要點:
- 日期比較時,優(yōu)先使用范圍查詢(左閉右開)
- TO_CHAR 會讓索引失效,大數(shù)據(jù)量時慎用
- 始終用 顯式轉(zhuǎn)型,不要依賴隱式轉(zhuǎn)換
- 注意邊界值,BETWEEN 可能丟失最后一天的末尾時間
TO_CHAR 會讓索引失效,大數(shù)據(jù)量時慎用。是什么意思?
索引就像一本書的"目錄",它能幫你快速翻到需要的頁碼。但如果對"目錄"里的文字做了修改(比如加了格式),那原來的目錄就失效了,你只能一頁一頁地翻完整本書來找內(nèi)容。
1. 為什么TO_CHAR會讓索引失效?
SQL的執(zhí)行順序決定了索引的生效機制。
當你執(zhí)行 WHERE TO_CHAR(HIREDATE, 'YYYY') = '1981' 時,數(shù)據(jù)庫的處理過程是這樣的:
- 讀取一條數(shù)據(jù):數(shù)據(jù)庫從硬盤或內(nèi)存中取出第一行員工的數(shù)據(jù)。
- 執(zhí)行函數(shù):對這行數(shù)據(jù)的
HIREDATE列執(zhí)行TO_CHAR函數(shù),把日期類型(如1981-05-01的內(nèi)部存儲值)轉(zhuǎn)換成字符串類型(如'1981')。 - 條件比對:判斷這個轉(zhuǎn)換后的字符串
'1981'是否等于你指定的'1981'。 - 重復(fù):對表中的每一行重復(fù)第1到第3步。
索引為什么沒起作用?
因為索引里存的是原始的、未經(jīng)過任何處理的 HIREDATE 值,而你在查詢時使用的是 TO_CHAR(HIREDATE) 這個函數(shù)的返回值。數(shù)據(jù)庫沒法用原始的日期值去匹配一個函數(shù)的返回值,所以只能放棄索引,從頭到尾把整張表的數(shù)據(jù)都處理一遍。
這正是你在筆記中看到的高級查詢優(yōu)化問題。
2. 另一種寫法:為什么索引能生效?
如果你換一種寫法,比如 WHERE HIREDATE >= TO_DATE('1981-01-01', 'YYYY-MM-DD'),過程完全不同:
- 函數(shù)執(zhí)行一次:數(shù)據(jù)庫首先執(zhí)行
TO_DATE('1981-01-01', 'YYYY-MM-DD'),把字符串'1981-01-01'轉(zhuǎn)換成一個日期值(比如1981-01-01的內(nèi)部存儲數(shù)字)。 - 索引快速定位:數(shù)據(jù)庫拿著這個日期值,直接去索引(書的目錄)里查找。
- 直接找到數(shù)據(jù):通過索引快速定位到滿足條件的數(shù)據(jù)在磁盤上的物理位置,然后直接讀取。
索引生效的關(guān)鍵在于:
列本身(HIREDATE)沒有被任何函數(shù)、計算所改變,數(shù)據(jù)庫可以直接用你在 WHERE 里給的值去跟索引里的值做比對。
3. “大數(shù)據(jù)量時慎用”是什么意思?
| 數(shù)據(jù)量 | 影響程度 | 說明 |
|---|---|---|
| 小數(shù)據(jù)量 (幾十、幾百條) | 影響極微 | 即便沒有索引,逐行掃描也快如閃電,用戶完全感受不到差異。 |
| 中等數(shù)據(jù)量 (幾萬、幾十萬條) | 影響顯著 | 逐行掃描開始變慢,可能需要幾秒甚至更久,用戶能明顯感覺到"卡"。 |
| 大數(shù)據(jù)量 (百萬、千萬條以上) | 災(zāi)難性影響 | 逐行掃描會讓查詢耗時從毫秒級(有索引)變成分鐘甚至小時級(無索引,全表掃描) |
舉個生活化的例子:
- 小數(shù)據(jù)量:在一個五六個座位的家庭餐桌上找一個人,掃一眼就行,不需要名片索引。
- 大數(shù)據(jù)量:在鳥巢(容納9萬人)里找一個座位號是 '1981' 的人。如果不用座位索引,每個座位都去核對,會累到崩潰。
4. 優(yōu)化建議與最佳實踐
核心原則:永遠不要讓函數(shù)去修飾被篩選的列本身。
| 要避免的寫法 (? 索引失效) | 推薦寫法 (? 索引生效) | 說明 |
|---|---|---|
WHERE TO_CHAR(HIREDATE, 'YYYY') = '1981' | WHERE HIREDATE >= TO_DATE('1981-01-01', 'YYYY-MM-DD') AND HIREDATE < TO_DATE('1982-01-01', 'YYYY-MM-DD') | 用范圍查詢代替函數(shù) |
WHERE TO_CHAR(HIREDATE, 'YYYYMM') = '198105' | WHERE HIREDATE >= TO_DATE('1981-05-01', 'YYYY-MM-DD') AND HIREDATE < TO_DATE('1981-06-01', 'YYYY-MM-DD') | 同上 |
WHERE SAL * 12 > 50000 | WHERE SAL > 50000 / 12 | 讓函數(shù)作用在常量上 |
WHERE SUBSTR(ENAME, 1, 1) = 'S' | WHERE ENAME LIKE 'S%' | 使用 LIKE 前綴匹配(也能利用索引) |
總結(jié)一句話:
寫 WHERE 條件時,讓“列”自己待著,別碰它。如果需要處理,去處理等號右邊的“值”。 這樣你的查詢才能在大數(shù)據(jù)量下保持高效。
你之前寫的 WHERE TO_CHAR(HIREDATE, 'YYYY') = 1981 在小數(shù)據(jù)量練習(xí)時完全沒問題,但在真正的企業(yè)生產(chǎn)環(huán)境中(數(shù)據(jù)量可能上千萬),這種寫法幾乎是被明令禁止的。
初學(xué)階段掌握規(guī)則即可,但養(yǎng)成好習(xí)慣會很有幫助。
到此這篇關(guān)于SQL 中日期的特殊性總結(jié)(格式符嚴格要求全大寫)的文章就介紹到這了,更多相關(guān)sql日期特殊性內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SQL?Server數(shù)據(jù)庫備份與還原完整操作案例
在開發(fā)與運維的過程中,數(shù)據(jù)的備份與還原是經(jīng)常用到的,下面這篇文章主要給大家介紹了關(guān)于SQL?Server數(shù)據(jù)庫備份與還原的相關(guān)資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下2024-07-07
自動備份mssql server數(shù)據(jù)庫并壓縮的批處理腳本
windows下,使用mssql命令行工具sqlcmd備份數(shù)據(jù)庫,并調(diào)用rar壓縮;不借助mssql"維護計劃"功能,拜托權(quán)限問題。2011-07-07
SQL Server將數(shù)據(jù)導(dǎo)入導(dǎo)出到Excel表格的全過程
這篇文章主要介紹了SQL Server將數(shù)據(jù)導(dǎo)入導(dǎo)出到Excel表格的全過程,文中通過圖文結(jié)合的形式給大家介紹的非常詳細,具有一定的參考價值,需要的朋友可以參考下2024-06-06
SQL Server中Check約束的學(xué)習(xí)教程
這篇文章主要介紹了SQL Server中Check約束的學(xué)習(xí)教程,包括對啟用Check約束來提升性能的介紹,需要的朋友可以參考下2015-12-12
SQL?SERVER數(shù)據(jù)庫中日期格式化詳解
這篇文章主要給大家介紹了關(guān)于SQL?SERVER數(shù)據(jù)庫中日期格式化的相關(guān)資料,在SQL?Server中可以使用CONVERT函數(shù)來格式化日期,文中通過代碼介紹的非常詳細,需要的朋友可以參考下2023-09-09
存儲過程解密(破解函數(shù),過程,觸發(fā)器,視圖.僅限于SQLSERVER2000)
解密指定存儲過程 exec sp_decrypt '存儲過程名'2009-05-05

