最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SQL?中日期的特殊性總結(jié)(格式符嚴格要求全大寫)

 更新時間:2026年04月24日 09:42:37   作者:穆金秋  
文章總結(jié)了SQL中日期數(shù)據(jù)類型的特殊性,并詳細介紹了日期與字符串的轉(zhuǎn)換、日期比較和加減運算的注意事項,強調(diào)了索引使用與避免函數(shù)修飾篩選列的重要性,本文結(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星期幾'星期三'
HH2424小時制14
MI分鐘30
SS秒鐘45

在SQL中,日期格式符是區(qū)分大小寫的,這是一個非常重要的細節(jié),寫錯了會導(dǎo)致轉(zhuǎn)換失敗或結(jié)果錯誤。

核心規(guī)則:格式符嚴格區(qū)分大小寫

格式符含義正確示例錯誤示例(大小寫錯誤)
MM月份(01-12)TO_CHAR(date, 'MM') → 04mm → ? 報錯或無效
MI分鐘(00-59)TO_CHAR(date, 'MI') → 30Mi / mi → ?
HH2424小時制(00-23)TO_CHAR(date, 'HH24') → 14hh24 → ?
HH12 / HH12小時制(01-12)TO_CHAR(date, 'HH12') → 02hh12 → ?
YYYY四位年份TO_CHAR(date, 'YYYY') → 2026yyyy → ?
YY兩位年份TO_CHAR(date, 'YY') → 26yy → ?
MON月份縮寫(如'4月')TO_CHAR(date, 'MON') → 4月Mon / mon → ?
MONTH月份全稱(如'4月')TO_CHAR(date, 'MONTH') → 4月Month → ?
DD日期(01-31)TO_CHAR(date, 'DD') → 23dd → ?
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 范圍寫法
年份 = 1981TO_CHAR(HIREDATE,'YYYY') = 1981HIREDATE >= TO_DATE('1981-01-01','YYYY-MM-DD') AND HIREDATE < TO_DATE('1982-01-01','YYYY-MM-DD')
年份 < 1982TO_CHAR(HIREDATE,'YYYY') < 1982HIREDATE < TO_DATE('1982-01-01','YYYY-MM-DD')
年份 <= 1982TO_CHAR(HIREDATE,'YYYY') <= 1982HIREDATE < TO_DATE('1983-01-01','YYYY-MM-DD')
年份 > 1981TO_CHAR(HIREDATE,'YYYY') > 1981HIREDATE >= TO_DATE('1982-01-01','YYYY-MM-DD')
年份 >= 1982TO_CHAR(HIREDATE,'YYYY') >= 1982HIREDATE >= 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)

功能OracleMySQL
當前日期時間SYSDATENOW() / CURDATE()
提取年份TO_CHAR(date, 'YYYY')YEAR(date)
提取月份TO_CHAR(date, 'MM')MONTH(date)
日期加減天數(shù)date + 10DATE_ADD(date, INTERVAL 10 DAY)
日期差(天數(shù))date1 - date2DATEDIFF(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:282026-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:282026-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 > 50000WHERE 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)文章

最新評論

闵行区| 青阳县| 东光县| 朝阳市| 龙泉市| 太仓市| 安徽省| 多伦县| 礼泉县| 财经| 昌吉市| 陈巴尔虎旗| 许昌县| 普陀区| 措勤县| 咸丰县| 喀喇| 宾川县| 益阳市| 焦作市| 米泉市| 菏泽市| 尖扎县| 方正县| 南宁市| 长白| 义马市| 尼木县| 辽阳县| 芦溪县| 郴州市| 曲靖市| 休宁县| 孟津县| 常德市| 兴山县| 合川市| 八宿县| 中山市| 南漳县| 永修县|