2016年1月11日 星期一

SQL計算日期間隔

日期和時間類型
https://docs.microsoft.com/zh-tw/sql/t-sql/functions/date-and-time-data-types-and-functions-transact-sql?view=sql-server-2017

https://msdn.microsoft.com/zh-tw/library/ms189794(v=sql.120).aspx

DATEDIFF ( datepart , startdate , enddate )

datepart
這是指定所跨越之界限類型的 startdate 和 enddate 部分。 下表列出所有有效的 datepart 引數。 使用者自訂變數對等項目無效。
datepart
縮寫
year
yy, yyyy
quarter
qq, q
month
mm, m
dayofyear
dy, y
day
dd, d
week
wk, ww
hour
hh
minute
mi, n
second
ss, s
millisecond
ms
microsecond
mcs
nanosecond
ns
startdate
這是可解析成 timedatesmalldatetimedatetimedatetime2 或 datetimeoffset 值的運算式。 date 可以是運算式、資料行運算式、使用者自訂變數或字串常值。 從 enddate 中扣除 startdate
若要避免模糊不清,請使用四位數年份。 如需兩位數年份的詳細資訊,請參閱<設定 two digit year cutoff 伺服器組態選項>。
enddate
請參閱<startdate>。


  • 每個 datepart 及其縮寫都會傳回相同的值。

善用DATEADD和DATEDIFF

DATEDIFF 函数造成溢位

DATEDIFF 函數造成溢位。分隔兩個日期/時間執行個體的日期部分數目太大。請嘗試使用日期部分較不精確的 DATEDIFF。

摘要:SQL Server - DATEDIFF 函數造成溢位。分隔兩個日期/時間執行個體的日期部分數目太大。請嘗試使用日期部分較不精確的 DATEDIFF。
因為 兩個日期之間差距了100年。
而導致用秒去算的時後發生了錯誤。
select  DATEDIFF (second, StartDate, SysDateTime()) from TableName
為了解決這個問題從網路上找到了解答,如下
create function fn_diffsecond
(
    @date1 datetime,
    @date2 datetime
)
returns bigint
as
begin
    return         (convert(bigint, datediff(day, @date1, @date2)) * 24 * 60 * 60)
        -     (datediff(second, dateadd(day, datediff(day, 0, @date1), 0), @date1))
        +     (datediff(second, dateadd(day, datediff(day, 0, @date2), 0), @date2))
end

go

select    dbo.fn_diffsecond('1900-01-02 03:45:56', '9999-12-31 23:59:59')
select 
(convert(bigint, datediff(day, [StartDate] , SysDateTime())) * 24 * 60 * 60) 
- (datediff(second, dateadd(day, datediff(day, 0, [StartDate]), 0), [StartDate]))
+ (datediff(second, dateadd(day, datediff(day, 0, SysDateTime()), 0), SysDateTime()))
from TableName
在 Excel 上的 EOMONTH 日期函數,可以用來傳回在 start_date 之前或之後指定月數的這一個月最後一天的序列值。 使用 EOMONTH 來計算剛好落在這一個月最後一天的到期日。 如今,在 SQL Server 2012 版本也提供此函數。

EOMONTH (Transact-SQL) 以選擇性位移,傳回包含指定日期的當月最後一天。 SQL Server 2012 版本新增加 語法:
?
1
EOMONTH ( start_date [, month_to_add ] )
引數: (1) start_date 日期運算式為當月最後一天該傳回者指定日期。 (2) month_to_add 指定要加入 start_date 之月數的選擇性整數運算式。 如果這個引數是指定的,則 EOMONTH 新增當月指定數字到 start_date,還有傳回當月最後一天做為結果日期。 如果這個加法溢位有效日期範圍,則會引起錯誤。 傳回類型:date -- 備註 此函數支援啟動遠端功能到 SQL Server 2012 伺服器和上方。 其無法從遠端處理到低於 SQL Server 2012 的伺服器版本。

請參考以下的範例程式碼: EX1. 日期時間類型與 EOMONTH 函數
?
1
2
3
4
5
6
7
-- EX1. 日期時間類型與 EOMONTH 函數
-- 傳回包含指定日期的當月最後一天
DECLARE @date DATETIME2
SET @date = '20130201'
SELECT EOMONTH ( @date ) AS N'指定日期的當月最後一天'  -- 2013-02-28
GO
-- 01_日期時間類型與 EOMONTH 函數

EX2. 字串參數及明確轉換 與 EOMONTH 函數
?
1
2
3
4
5
6
7
-- EX2. 字串參數及明確轉換 與 EOMONTH 函數
-- 傳回包含指定日期的當月最後一天
DECLARE @date VARCHAR(255)
SET @date = '20140201'
SELECT EOMONTH ( @date ) AS N'指定日期的當月最後一天'  -- 2014-02-28
GO
-- 02_字串參數及明確轉換 與 EOMONTH 函數

EX3. 具有和不具有 month_to_add 參數的 EOMONTH 函數
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- EX3. 具有和不具有 month_to_add 參數的 EOMONTH 函數
-- 計算:以目前日期為基礎,其相差特定月數的最後一天
DECLARE @date DATETIME2
SET @date = SYSDATETIME()
SELECT EOMONTH ( @date ) AS N'本月最後一天' , @date N'今天'
SELECT EOMONTH ( @date, 1 ) AS N'下一個月最後一天' , @date N'今天'
SELECT EOMONTH ( @date, -1 ) AS N'前一個月最後一天' , @date N'今天'
SELECT EOMONTH ( @date, 2 ) AS N'下兩個月最後一天' , @date N'今天'
SELECT EOMONTH ( @date, -2 ) AS N'前兩個月最後一天' , @date N'今天'
GO
-- 03_計算:以目前日期為基礎,其相差特定月數的最後一天

在 SQL Server 2012 之前版本的作法 DATEADD (Transact-SQL) 傳回指定的 date,並將指定的 number 間隔 (帶正負號的整數) 加入至該 date 的指定 datepart。 語法:
?
1
DATEADD (datepart , number , date )
引數: (1) datepart 這是 integernumber 要加入其中的 date 部分。 (2) number 這是可解析成 int (要加入至 date 的 datepart) 的運算式。 使用者自訂的變數有效。 如果您指定了含有十進位小數的值,該小數就會被截斷而且不會四捨五入。 (3) date 這是可解析成 time、date、smalldatetime、datetime、datetime2 或 datetimeoffset 值的運算式。 date 可以是運算式、資料行運算式、使用者自訂變數或字串常值。 如果此運算式為字串常值,它必須解析為 datetime。 若要避免模糊不清,請使用四位數年份。 -- 傳回資料類型: 是 date 引數的資料類型,但字串常值除外。 字串常值的傳回資料類型是 datetime。 如果字串常值的秒數小數位數超過三個位置 (. nnn),或者包含時區時差部分,就會引發錯誤。 -- 關於 datepart 引數 dayofyear 、day 和 weekday 都會傳回相同的值。 每個 datepart 及其縮寫都會傳回相同的值。 如果 datepart 是 month、date 月份的天數比傳回月份的天數多,而且 date 日期不存在傳回月份中,就會傳回傳回月份的最後一天。 例如,九月有 30 天。因此,下列陳述式會傳回 2006-09-30 00:00:00.000:
?
1
2
SELECT DATEADD(month, 1, '2006-08-30')
SELECT DATEADD(month, 1, '2006-08-31')
-- 在 SQL Server 2012 之前版本的作法,例如:2000、2005、2008、2008 R2 需求: 計算與目前日期為基礎,其相差特定月數的最後一天 公式:
?
1
SELECT DATEADD(MM, DATEDIFF(MM, -1, GETDATE()) + <相差特定月數>, 0) - 1 AS N'xxx月最後一天'
請參考以下的範例程式碼:
?
1
2
3
4
5
6
7
8
9
10
11
DECLARE @date DATETIME
SET @date = GETDATE()
SELECT DATEADD(MM, DATEDIFF(MM, -1, @date), 0) - 1 AS N'本月最後一天' , @date N'今天'
SELECT DATEADD(MM, DATEDIFF(MM, -1, @date) - 1, 0) - 1 AS N'前一個月最後一天' , @date N'今天'
SELECT DATEADD(MM, DATEDIFF(MM, -1, @date) + 1, 0) - 1 AS  N'下一個月最後一天' , @date N'今天'
SELECT DATEADD(MM, DATEDIFF(MM, -1, @date) - 2, 0) - 1 AS N'前兩個月最後一天' , @date N'今天'
SELECT DATEADD(MM, DATEDIFF(MM, -1, @date) + 2, 0) - 1 AS  N'下兩個月最後一天' , @date N'今天'
GO
-- 04_舊版_計算與目前日期為基礎,其相差特定月數的最後一天

參考資料: EOMONTH http://office.microsoft.com/zh-tw/excel-help/HP005209076.aspx EOMONTH (Transact-SQL) http://technet.microsoft.com/zh-tw/library/hh213020.aspx

沒有留言: