首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> SQL Server 資料庫 >> 關於SqlServer

MS SQL DATEPART 與 DATENAME 深度解析:從語法差異到交叉表實戰

     編輯:關於SqlServer

DATEPART 與 DATENAME 是 SQL Server 提取日期組件的雙子星函數:前者返回整數,適合計算、分組、排序;後者返回字串,適合顯示、報表標籤。本文將從語法、參數、返回類型、week/weekday 特殊行為、時區陷阱,到動態交叉表 (按星期統計城市數據) 實戰全維度解析,助你精準選型並避開常見坑點。

語法與參數對比

項目DATEPARTDATENAME
語法DATEPART ( datepart , date )DATENAME ( datepart , date )
返回類型intnvarchar
適用場景計算、分組、排序、WHERE 篩選報表顯示、標籤生成、動態列名
week (wk/ww) 行為受 SET DATEFIRST 影響,1月1日所在週 = 1同 DATEPART
weekday (dw) 行為返回數字:1=週日...7=週六 (受 DATEFIRST 影響)返回名稱:'星期日'...'星期六' (受語言環境影響)

datepart 參數完整列表

datepart縮寫說明
Yearyy, yyyy年份
Quarterqq, q季度 (1-4)
Monthmm, m月份 (1-12)
DayOfYeardy, y年內第幾天 (1-366)
Daydd, d月內第幾天 (1-31)
Weekwk, ww年內第幾週 (受 DATEFIRST)
Weekdaydw星期幾 (受 DATEFIRST)
Hourhh小時 (0-23)
Minutemi, n分鐘 (0-59)
Secondss, s秒 (0-59)
Millisecondms毫秒 (0-999)

date 參數規範

  • 支援 datetime、smalldatetime、datetime2、date、datetimeoffset 類型列/變量/字面量。
  • 字面量必須加單引號:'2023-01-01'。
  • smalldatetime 精度僅到分鐘,秒/毫秒部分始終為 0。
  • 兩位數年份解析規則:two digit year cutoff (默認 2049),49→2049, 50→1950。強制使用四位數年份。

實戰案例:動態生成「城市 × 星期」交叉統計表

源數據:

IDCityDate
1a2005-01-24 (週一)
2b2005-01-23 (週日)
3b2005-01-23 (週日)
4a2005-01-21 (週五)
5d2005-01-20 (週四)
6c2005-01-19 (週三)
7c2005-01-19 (週三)
8a2005-01-18 (週二)

目標報表:

City\DayMonTueWedThuFriSatSunTotal
a11001003
b00000022
c00200002
d00010001
Total11211028

方案 A:DATEPART (dw) + 條件聚合 (性能最優,推薦)

-- 建表
CREATE TABLE #t (id int, city varchar(2), dt datetime);
INSERT INTO #t VALUES
(1,'a','20050124'),(2,'b','20050123'),(3,'b','20050123'),
(4,'a','20050121'),(5,'d','20050120'),(6,'c','20050119'),
(7,'c','20050119'),(8,'a','20050118');

-- 核心查詢 (假設 DATEFIRST=7, 週日=1)
SELECT
    city AS [City\Day],
    SUM(CASE WHEN DATEPART(dw, dt) = 2 THEN 1 ELSE 0 END) AS Mon,  -- 週一=2
    SUM(CASE WHEN DATEPART(dw, dt) = 3 THEN 1 ELSE 0 END) AS Tue,
    SUM(CASE WHEN DATEPART(dw, dt) = 4 THEN 1 ELSE 0 END) AS Wed,
    SUM(CASE WHEN DATEPART(dw, dt) = 5 THEN 1 ELSE 0 END) AS Thu,
    SUM(CASE WHEN DATEPART(dw, dt) = 6 THEN 1 ELSE 0 END) AS Fri,
    SUM(CASE WHEN DATEPART(dw, dt) = 7 THEN 1 ELSE 0 END) AS Sat,
    SUM(CASE WHEN DATEPART(dw, dt) = 1 THEN 1 ELSE 0 END) AS Sun,
    COUNT(*) AS Total
FROM #t
GROUP BY city
UNION ALL
SELECT
    'Total',
    SUM(CASE WHEN DATEPART(dw, dt) = 2 THEN 1 ELSE 0 END),
    SUM(CASE WHEN DATEPART(dw, dt) = 3 THEN 1 ELSE 0 END),
    SUM(CASE WHEN DATEPART(dw, dt) = 4 THEN 1 ELSE 0 END),
    SUM(CASE WHEN DATEPART(dw, dt) = 5 THEN 1 ELSE 0 END),
    SUM(CASE WHEN DATEPART(dw, dt) = 6 THEN 1 ELSE 0 END),
    SUM(CASE WHEN DATEPART(dw, dt) = 7 THEN 1 ELSE 0 END),
    SUM(CASE WHEN DATEPART(dw, dt) = 1 THEN 1 ELSE 0 END),
    COUNT(*)
FROM #t;

方案 B:DATENAME (dw) + 條件聚合 (可讀性強,受語言環境影響)

SELECT
    city AS [City\Day],
    SUM(CASE WHEN DATENAME(dw, dt) = N'星期一' THEN 1 ELSE 0 END) AS Mon,
    SUM(CASE WHEN DATENAME(dw, dt) = N'星期二' THEN 1 ELSE 0 END) AS Tue,
    SUM(CASE WHEN DATENAME(dw, dt) = N'星期三' THEN 1 ELSE 0 END) AS Wed,
    SUM(CASE WHEN DATENAME(dw, dt) = N'星期四' THEN 1 ELSE 0 END) AS Thu,
    SUM(CASE WHEN DATENAME(dw, dt) = N'星期五' THEN 1 ELSE 0 END) AS Fri,
    SUM(CASE WHEN DATENAME(dw, dt) = N'星期六' THEN 1 ELSE 0 END) AS Sat,
    SUM(CASE WHEN DATENAME(dw, dt) = N'星期日' THEN 1 ELSE 0 END) AS Sun,
    COUNT(*) AS Total
FROM #t
GROUP BY city
UNION ALL
SELECT 'Total', ... ; -- 同理

關鍵警示: DATENAME 返回值取決於 SET LANGUAGE / 登錄語言。中文環境返回 N'星期一',英文環境返回 'Monday'。跨環境部署必須用 DATEPART 或顯式 SET LANGUAGE。

現代替代方案:PIVOT 操作符 (SQL 2005+)

SELECT City\Day, ISNULL([1],0) AS Sun, ISNULL([2],0) AS Mon, ISNULL([3],0) AS Tue,
       ISNULL([4],0) AS Wed, ISNULL([5],0) AS Thu, ISNULL([6],0) AS Fri, ISNULL([7],0) AS Sat,
       ([1]+[2]+[3]+[4]+[5]+[6]+[7]) AS Total
FROM (
    SELECT city, DATEPART(dw, dt) AS dw
    FROM #t
) src
PIVOT (COUNT(dw) FOR dw IN ([1],[2],[3],[4],[5],[6],[7])) pvt;

總結與選型指南

需求推薦函數理由
分組、排序、WHERE 篩選、計算DATEPART返回 int,索引友好、運算快、跨語言穩定
報表列名、標籤顯示、用戶可讀輸出DATENAME返回本地化字串,直接可用
動態交叉表/透視表DATEPART + 條件聚合 / PIVOT性能最優,邏輯清晰
週/星期相關邏輯DATEPART(dw, ...) + 明確 SET DATEFIRST消除歧義,建議在會話/存儲過程開頭 SET DATEFIRST 7 (週日=1)

掌握 DATEPART (整數計算) 與 DATENAME (字串顯示) 的分工本質,並始終顯式設置 SET DATEFIRST 7 統一週起始日,是編寫健壯、可移植、高性能日期邏輯的基石。現代 T-SQL 開發請優先考慮 PIVOT 替代手工條件聚合,讓優化器為你做更好的執行計劃選擇。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved