在日常資料庫開發與維護中,依據日期區間統計業務數據(如每日訂單量、每月銷售額)是極為常見的需求。本文將通過具體範例,示範如何在 SQL Server 中高效編寫日報與月報查詢語句,並對比 Oracle 的寫法差異,幫助開發者快速掌握核心技巧。
日報的關鍵在於「精確截取某一天的時間範圍」。許多初學者習慣使用 BETWEEN,但這在包含時間成分的 DATETIME 欄位上容易產生邊界問題(例如會漏掉 '2009-01-01 23:59:59' 之後的數據)。最佳實踐是採用 「大於等於起始日期,且小於隔天日期」 的半開半閉區間寫法。
-- 查詢 2009-01-01 當天,客戶 A1、A2、A3 的訂單總數量
SELECT
Cust_Name,
CONVERT(CHAR(10), Order_Date, 120) AS Order_Date, -- 格式化為 YYYY-MM-DD
SUM(Qty) AS Qty
FROM Orders_Big
WHERE 1=1
AND Cust_Name IN ('A1', 'A2', 'A3')
AND Order_Date >= '2009-01-01' -- 包含當天 00:00:00
AND Order_Date < '2009-01-02' -- 排除隔天 00:00:00
GROUP BY
Cust_Name,
CONVERT(CHAR(10), Order_Date, 120)
ORDER BY 2, 1;
核心解析:
CONVERT(CHAR(10), Order_Date, 120):使用樣式 120 (ODBC 標準 yyyy-mm-dd) 截取日期部分,去除時間,便於分組。Order_Date >= '2009-01-01' AND Order_Date < '2009-01-02':這是處理日期範圍最安全、效能最好(可用索引)的方式。SELECT
Cust_Name,
TO_CHAR(Order_Date, 'YYYY-MM-DD') AS Order_Date, -- 方法一:轉字串
-- TRUNC(Order_Date) AS Order_Date, -- 方法二:截斷時間 (推薦,可用索引)
SUM(Qty) AS Qty
FROM Orders_Big
WHERE 1=1
AND Cust_Name IN ('A1', 'A2', 'A3')
AND Order_Date >= DATE '2009-01-01'
AND Order_Date < DATE '2009-01-02'
GROUP BY
Cust_Name,
TO_CHAR(Order_Date, 'YYYY-MM-DD') -- 或 TRUNC(Order_Date)
ORDER BY 2, 1;
Oracle 中 TRUNC(Order_Date) 直接截斷時間部分,語意更清晰,且若在 Order_Date 上有函數索引,效能極佳。
月報邏輯同理,只需將日期截取到「年-月」粒度,篩選條件鎖定整個月份區間。
-- 查詢 2009 年 1 月份報表
SELECT
Cust_Name,
CONVERT(CHAR(7), Order_Date, 120) AS Order_YrMs, -- 取得 '2009-01'
CAST(CONVERT(CHAR(7), Order_Date, 120) + '-01' AS DATETIME) AS Order_Date, -- 補齊為月首日期
SUM(Qty) AS Qty
FROM Orders_Big
WHERE 1=1
AND Cust_Name IN ('A1', 'A2', 'A3')
AND Order_Date >= '2009-01-01'
AND Order_Date < '2009-02-01' -- 關鍵:小於 2 月 1 號,涵蓋 1 月所有天數
GROUP BY
Cust_Name,
CONVERT(CHAR(7), Order_Date, 120)
ORDER BY 2, 1;
SELECT
Cust_Name,
TO_CHAR(Order_Date, 'YYYY-MM') AS Order_YrMs, -- 方法一
-- TRUNC(Order_Date, 'MM') AS Order_Date, -- 方法二:截斷到月首 (推薦)
SUM(Qty) AS Qty
FROM Orders_Big
WHERE 1=1
AND Cust_Name IN ('A1', 'A2', 'A3')
AND Order_Date >= DATE '2009-01-01'
AND Order_Date < DATE '2009-02-01'
GROUP BY
Cust_Name,
TO_CHAR(Order_Date, 'YYYY-MM') -- 或 TRUNC(Order_Date, 'MM')
ORDER BY 2, 1;
Order_Date 與 Cust_Name 上建立複合索引 (Cust_Name, Order_Date),可顯著加速篩選與分組。WHERE CONVERT(CHAR(10), Order_Date, 120) = '2009-01-01',這會導致索引失效,改用區間查詢。@StartDate, @EndDate)而非硬編碼日期,利於計畫快取重用,防止 SQL 注入。OFFSET ... FETCH)分批返回前端。掌握「半開半閉區間」查詢日期範圍,搭配 CONVERT 或 FORMAT (SQL Server 2012+) 進行日期格式化分組,即可輕鬆實現高效、準確的日報與月報功能。這套邏輯通用於各主流關聯式資料庫,只需調整日期函數語法即可無縫遷移。