SQL Server時間粒度系列----第2節日期、周時間粒度詳解

来源:http://www.cnblogs.com/dzy863/archive/2016/01/07/5110106.html
-Advertisement-
Play Games

本文目錄列表:1、從MySQL提供的TO_DAYS和FROM_DAYS這對函數說起2、SQL Server日期時間粒度3、SQL Server周有關時間粒度4、總結語5、參考清單列表從MySQL提供的TO_DAYS和FROM_DAYS針對函數說起 學習和使用過MySQL的博友,大都知道MySQL提供...


本文目錄列表: 1、從MySQL提供的TO_DAYS和FROM_DAYS這對函數說起
2、SQL Server日期時間粒度
3、SQL Server周有關時間粒度 4、總結語 5、參考清單列表   從MySQL提供的TO_DAYS和FROM_DAYS針對函數說起      學習和使用過MySQL的博友,大都知道MySQL提供了很多針對日期和時間的函數,提供了獲取不同時間粒度上的功能。相對而然SQL Server提供的有關日期和時間函數不太多的,但是其提供的功能也是蠻強大的。還是讓我們繼續話說MySQL的日期和時間函數,發現了TO_DAYS和FROM_DAYS這一對函數:TO_DAYS將一個日期轉換為一個從1開始的整數(註意(摘自mysql官網文檔):Given a date date, returns a day number (the number of days since year 0).),FROM_DAYS將一個從1開始的整數(更嚴格的說從366開始,因為1-365得到的結果值為NULL)轉換為一個日期(摘自mysql官網文檔):Given a day number N, returns a DATE value.)。       數字和日期很顯然讓我想起序列這個概念。那什麼是序列呢?序列就是一個連續的任意相鄰兩個間隔單位值相等的集合(很想數學上的等差數列的定義),比如自然數集合(非負整數集合)(0,1,2,3,……正無窮)、整數集合(……,-3,-2,-1,0,1,2,3,……)、有符號(負)整數集合(……,-3,-2,-1)、無符號(非負)整數集合(自然數集合)、日期區間從"0001-01-01"‘到“9999-12-31“間隔單位值相差1天的日期集合、當然也可以是時間序列相鄰間隔單位小時等等。間隔單位值也可以是2、5等等任意的一個整數值(對於數字序列來說的),也可以1天,12小時,480分鐘等等(對於日期和時間序列來說的)。       從序列的定義來分析,數字序列和日期序列都具有共同的特性:間隔單位值相等。MySQL提供的這一對將整數和日期相互轉換的函數,也是基於一個基準日期的實現。MySQL的函數TO_DAYS的基準日期是”0000-01-01“的,該函數返回值還是從整數1開始計數的。       提起MySQL提供的基準日期,SQL Server引擎也提供一個預設的基準日期,那就是”1900-01-01“,下麵我們來看看如何使用這個基準日期。         SQL Server日期時間粒度          SQL Server很顯然沒有提供向MySQL中那樣的將整數和日期相互轉換的一對函數,但是我們瞭解了日期序列的特性,再結合SQL Server提供的基準日期,我們很容易實現類似的一對功能函數。在提供SQL Server版本的整數和日期相互轉換的一對函數實現前,我們還是講解日期這個時間粒度。          日期這個時間粒度就是一個表示年月日的值。SQL Server 提供的日期和時間的數據類型包括:date(3位元組)、smalldatetime(4位元組),time(5位元組)、datetime(8位元組),datetime2(8位元組)和datetimeoffset(10位元組),其中除smalldatetime和datetime以外的其他日期和時間類型是從SQL Server 2008提供的。為了保證我們的實現方案可以在SQL Server 2005+環境運行,我們只能針對性地選擇smalldatetime和datetime這兩個日期和時間數據類型。從這兩個日期和時間數據類的日期部分範圍(不包括時間部分)來看,smalldatetime的日期範圍區間是[1900-01-01,2079-06-06],datetime的日期部分範圍區間是[0001-01-01,9999-12-31]。目前我們處在21世界的,新開發的應用或項目中使用到的歷史數據也很難是19世紀以前的。基於這因素和基準日期”1900-01-01“的考慮,我們要滿足的日期部分範圍區間是[1900-01-01,9999-12-31],這個範圍區間也是我們要確定的日期時間粒度的範圍區間。       日期時間粒度的範圍區間找到了,只需要將該區間中的每一個日期和一個整數值進行一映射對應,也就是日期和整數間的相互轉換,基於2664600(該範圍區間獲得的總天數:((9999-1900)  + 1)*366),使用4位元組整數(以下簡稱int)完全滿足存儲要求的。我們也看到了smalldatetime和其對應的int都是4位元組,就存儲空間而然將欄位列的數據類型設置為smalldatetime和int任意一個都是沒有問題的,但是存儲了smalldatetime的日期部分的範圍顯然不能存儲2079-06-07以後的日期時間值的,另外SQl Server引擎內部使用了兩個2位元組的整數存儲,第一個 2 位元組存儲 1900 年 1 月 1 日後的天數。另外一個 2 位元組存儲午夜後經過的分鐘數,從其存儲原理我們知道該數據類型的值從文件頁(數據頁、索引頁等等)中提取到得到smalldatetime值的過程是要經過一系列轉換(比如分別將兩個2位元組的整數轉換為日期和時間兩部分,再將兩部分串聯等等),相比較一個int數據類型的值,從文件頁提取到得到其值就沒有向smalldate那樣複雜的轉換操作。基於smallldatetime和int在存儲範圍區間和轉換複雜度這兩個方面的比較,我們在設計表欄位列時如果遇到只存儲日期時間粒度的時間值(只包含年月日的值,即日期部分值)時,可以設計為int數據類型。       SQL Server實現的日期和整數相互轉換的功能函數對,T-SQL代碼如下:
 1 IF OBJECT_ID(N'dbo.ufn_Days', 'FN') IS NOT NULL
 2 BEGIN
 3     DROP FUNCTION dbo.ufn_Days;
 4 END
 5 GO
 6  
 7 --==================================
 8 -- 功能: 獲得指定日期時間基於基準日期的總天數(一個整數值)
 9 -- 說明: 如果指定的日期時間為NULL或者小於基準日期“1900-01-01”時,則其值預設基準日期
10 --       結果值為非負整數,從0開始計數。
11 -- 作者: XXX
12 -- 創建: yyyy-MM-dd
13 -- 修改: yyyy-MM-dd XXX 修改內容描述
14 -- 調用: SET @intDays = dbo.ufn_Days('2008-01-14')
15 --==================================
16 CREATE FUNCTION dbo.ufn_Days
17 (
18     @dtmDate DATETIME                    -- 指定的日期時間
19 ) RETURNS INT
20     --$Encode$--
21 AS
22 BEGIN
23     IF @dtmDate IS NULL OR @dtmDate < '1900-01-01'
24     BEGIN
25         SET @dtmDate = '1900-01-01';
26     END
27  
28     -- datepart參數也可以為dd或d
29     RETURN DATEDIFF(DAY, '1900-01-01', @dtmDate)
30 END
31 GO
32  
33 IF OBJECT_ID(N'dbo.ufn_Days2Date', 'FN') IS NOT NULL
34 BEGIN
35     DROP FUNCTION dbo.ufn_Days2Date;
36 END
37 GO
38  
39 --==================================
40 -- 功能: 獲得一個整數值基於基準日期對應的日期
41 -- 說明: 如果指定的整數值為NULL或為負整數時,則其值預設為0;
42 --       如果指定的整數值大於“9999-12-31”對應的整數值時,則其值預設設置為“9999-12-31”對應的整數值
43 --       結果值為從基準日期開始計數的日期
44 -- 作者: XXX
45 -- 創建: yyyy-MM-dd
46 -- 修改: yyyy-MM-dd XXX 修改內容描述
47 -- 調用: SET @dtmDate = dbo.ufn_Days2Date(39459) --'2008-01-14'
48 --==================================
49 CREATE FUNCTION dbo.ufn_Days2Date 
50 (
51     @intDays INT                -- 指定的整數值
52 ) RETURNS DATETIME
53     --$Encode$--
54 AS
55 BEGIN
56     IF @intDays IS NULL OR @intDays < 0
57     BEGIN
58         SET @intDays = 0;
59     END
60  
61     DECLARE @intMaxDays AS INT;
62     SET @intMaxDays = dbo.ufn_Days('9999-12-31');
63  
64     IF @intDays >= @intMaxDays
65     BEGIN
66         SET @intDays = @intMaxDays;
67     END
68  
69     -- datepart參數也可以為dd或d
70     RETURN DATEADD(DAY, @intDays, '1900-01-01');
71 END
72 GO

 

以上功能函數對的測試T-SQL代碼如下:
1 SELECT dbo.ufn_Days(NULL) AS 'NULL值對應的整數值', dbo.ufn_Days('1899-12-31') AS '小於1900-01-01對應的整數值', dbo.ufn_Days('1900-01-01') AS '1900-01-01對應的整數值', dbo.ufn_Days('2016-01-07') AS '2016-01-07對應的整數值', dbo.ufn_Days('9999-12-31') AS '9999-12-31對應的整數值';
2 SELECT dbo.ufn_Days2Date(NULL) AS 'NULL值對應的日期', dbo.ufn_Days2Date(-1) AS '小於0對應的日期', dbo.ufn_Days2Date(0) AS '0對應的日期', dbo.ufn_Days2Date(42374) AS '42374對應的日期', dbo.ufn_Days2Date(2958463) AS '2958463對應的整數值', dbo.ufn_Days2Date(2958464) AS '大於2958463對應的整數值';
3 GO

 

 

執行後的查詢結果如下圖:

  註意:以上功能函數對使用到了日期和時間函數datediffdateadd    SQL Server周有關時間粒度          SQL Server周有關時間粒度可以表述為以下問題:一個日期屬於一年的第幾周和一個日期屬於當前所在周的周幾。一個日期是否是工作日這個要根據是否上班來確定的,不然簡單的根據周一到周五是工作日,周六和周日是休息日來判斷的。第一個問題很用以通過datepart(weekday,  @dtmDateTime)來解決的,第二個問題則通過datename(weekday, @dtmDateTime)來得到結果的,不過結果值的展現形式要依賴預設語言的。         將以上兩個問題的解決封裝在SQL Server標量函數中,其T-SQL代碼如下:
 1 IF OBJECT_ID(N'dbo.ufn_WeekOfYear', 'FN') IS NOT NULL
 2 BEGIN
 3     DROP FUNCTION dbo.ufn_WeekOfYear;
 4 END
 5 GO
 6  
 7 --==================================
 8 -- 功能: 獲取指定日期屬於當前第幾周
 9 -- 說明: 一年最多1-53周
10 -- 作者: XXX
11 -- 創建: yyyy-MM-dd
12 -- 修改: yyyy-MM-dd XXX 修改內容描述
13 -- 調用: SELECT dbo.ufn_WeekOfYear('2016-01-07');
14 --==================================
15 CREATE FUNCTION dbo.ufn_WeekOfYear
16 (
17     @dtmDate DATETIME
18 ) RETURNS TINYINT
19     --$Encode$--
20 AS
21 BEGIN
22     -- datepart參數也可以為wk, ww
23     RETURN CAST(DATEPART(WEEK, @dtmDate) AS TINYINT)
24 END
25 GO
26  
27 IF OBJECT_ID(N'dbo.ufn_WeekdayNameOfWeek', 'FN') IS NOT NULL
28 BEGIN
29     DROP FUNCTION dbo.ufn_WeekdayNameOfWeek;
30 END
31 GO
32  
33 --==================================
34 -- 功能: 獲取指定日期屬於當前周周幾的名稱
35 -- 說明: 結果值的展示形式會以來預設設置語言
36 -- 作者: XXX
37 -- 創建: yyyy-MM-dd
38 -- 修改: yyyy-MM-dd XXX 修改內容描述
39 -- 調用: SELECT dbo.ufn_WeekOfYear('2016-01-07');
40 --==================================
41 CREATE FUNCTION dbo.ufn_WeekdayNameOfWeek
42 (
43     @dtmDate DATETIME
44 ) RETURNS NVARCHAR(20)
45     --$Encode$--
46 AS
47 BEGIN
48     -- datepart參數也可以為dw
49     RETURN DATENAME(WEEKDAY, @dtmDate);
50 END
51 GO
52  

 

測試以上函數效果的T-SQL代碼如下:

SET LANGUAGE N'us_english';
SELECT @@LANGUAGE, dbo.ufn_WeekOfYear('2017-02-01'), dbo.ufn_WeekdayNameOfWeek('2017-02-01')
GO
 
SET LANGUAGE N'簡體中文';
SELECT @@LANGUAGE, dbo.ufn_WeekOfYear('2017-02-01'), dbo.ufn_WeekdayNameOfWeek('2017-02-01')
GO

 

執行後的查詢結果如下圖: 註意:以上使用了SET LANGUAGE和@@LANGUAGE,更多閱讀配置函數   繼續補充增加不論@@DATEFISRT以任何一天作為一周的開始,都要準確獲得指定日期隸屬當前周的周幾的功能,其實現的T-SQL代碼如下:
 1 IF OBJECT_ID(N'dbo.ufn_WeekdayOfWeek', 'FN') IS NOT NULL
 2 BEGIN
 3     DROP FUNCTION dbo.ufn_WeekdayOfWeek;
 4 END
 5 GO
 6  
 7 --==================================
 8 -- 功能: 獲取指定日期隸屬當前周周幾
 9 -- 說明: 結果值從1到7,分別對應從周一到周日,該值與@@DATEFISRT配置函數值保持一致
10 -- 作者: XXX
11 -- 創建: yyyy-MM-dd
12 -- 修改: yyyy-MM-dd XXX 修改內容描述
13 -- 調用: SELECT dbo.ufn_WeekdayOfWeek('2017-01-07') -- 4(表示星期四)
14 --==================================
15 CREATE FUNCTION dbo.ufn_WeekdayOfWeek
16 (
17     @dtmDate DATETIME                -- 指定的日期時間
18 ) RETURNS TINYINT
19     --$Encode$--
20 BEGIN
21     DECLARE
22         @tintDateFirst AS TINYINT,
23         @tintWeekDayIndexID AS TINYINT,
24         @tintSum AS TINYINT;
25  
26     SELECT
27         @tintDateFirst = @@DATEFIRST,
28         @tintWeekDayIndexID = DATEPART(WEEKDAY, @dtmDate),
29         @tintSum = @tintDateFirst + @tintWeekDayIndexID;
30  
31     RETURN (CASE WHEN @tintSum >= 9 THEN @tintSum - 8 WHEN @tintSum = 8 THEN 7 ELSE @tintSum - 1 END);    
32 END
33 GO

 

測試其功能的T-SQL代碼如下:
 1 DECLARE @tintLoopID AS TINYINT;
 2 SET @tintLoopID = 1;
 3  
 4 DECLARE @dtmDate AS DATETIME;
 5 SET @dtmDate = '2016-01-07';
 6  
 7 SELECT @dtmDate AS 'date', DATENAME(WEEKDAY, @dtmDate) AS 'WeekdayName';
 8 WHILE @tintLoopID <= 7
 9 BEGIN
10     SET DATEFIRST @tintLoopID;
11  
12     SELECT @@DATEFIRST AS 'Start Day Of Week[1=星期一、2=星期二、3=星期三,……,7=星期日]', dbo.ufn_WeekdayOfWeek(@dtmDate) AS 'Nth Of Week1=星期一、2=星期二、3=星期三,……,7=星期日]'
13  
14     SET @tintLoopID = @tintLoopID + 1; 
15 END
16 GO
17  

 

執行後的查詢結果下圖:     總結語       本文我們瞭解到MySQL提供的將日期和整數相互轉換的功能函數對,還瞭解了序列的概念,又提供了SQL Server將日期和整數相互轉換的實現,最後也實現了一個日期隸屬當年的第幾周以及其隸屬當前周周幾名稱的標量函數。(我中間一直在嘗試通過datepart(weekday, @dtmDateTime)過去指定日期所在本周的索引值從1開始計數到7,不過這個要根據@@datefirst配置函數來設置一周的第一天為周幾,周一到周日分別對應1到7,前者的值與後者的值相關,但是不會時刻保持相同,例如美國預設周日是一周的第一天,也是@@datefirst為7(通過SET DATEFIRST N來設置),如果通過datepart(weekday, @dtmDateTime)獲得的結果值為2,改日期在其隸屬周圍星期一。目前還沒有實現,中間花費了不少的時間,博文針對這個問題的解決提供一個實現。博友如果更好的思路,請提出寶貴的建議。)      參考清單列表 1、http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_to-days 2、http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_from-days 3、https://msdn.microsoft.com/zh-cn/library/ms189794(v=sql.90).aspx 4、https://msdn.microsoft.com/zh-cn/library/ms186819(v=sql.90).aspx 5、https://msdn.microsoft.com/zh-cn/library/ms174395(v=sql.90).aspx 6、https://msdn.microsoft.com/zh-cn/library/ms174398(v=sql.90).aspx
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在Windows Server 2012 上遇到了“The Recycle Bin On E:\ is corrupted。 Do you want to empty the Recycle Bin for this drive?" . 一般對應的中文提示為”E:\上的回收站已經損壞。是否清空該.....
  • 一、cmd命令行---進行Windows服務操作 1、安裝服務 sc create 服務名 binPath= "C:\Users\Administrator\Desktop\win32srvDemo\win32srvdemo\Debug\win32srvDemo.exe" 註:服務名:指創建的Win...
  • 1、使用expect前,需要先安裝兩個rpm包,下載:http://download.csdn.net/detail/wang7dao/4416172# rpm -ihv expect-5.43.0-8.el5.i386.rpm# rpm -ihv expect-devel-5.43.0-8.el5...
  • 對nginx的一些小優化的記錄,例如日誌的切割,日誌的分析,以及參數的配置等等,會持續的進行更新。
  • SELECT hp_patient.name, hp_ptorders.DrugName, hp_ptorders.Dosage,hp_ptorders.DosageUnitFROM hp_patientLEFT JOIN hp_ptordersON hp_patient.pid=hp_ptorde...
  • 一、 ASM(自動存儲管理)的來由:ASM是Oracle 10g R2中為了簡化Oracle資料庫的管理而推出來的一項新功能,這是Oracle自己提供的捲管理器,主要用於替代操作系統所提供的LVM,它不僅支持單實例,同時對RAC的支持也是非常好。ASM可以自動管理磁碟組並提供有效的數據冗餘功能。.....
  • 表:T_USERS,T_USER_EXCEPT_WORK,其中T_USERS中的主鍵ID和T_USER_EXCEPT_WORK中的外鍵都為數列SEQUENCE_USERS.Currval的自增長數列。更通俗的講就是:在T_USERS表中插入一條數據,通過觸發器在T_USER_EXCEPT_WORK插...
  • SQLServer資料庫的基礎知識的回顧 1)主數據文件:*.mdf 2)次要數據文件:*.ndf 3)日誌文件:*.ldf 每個資料庫至少要包含兩個文件:一個數據文件和一個日誌文件 如何查看SQL Server的幫助==================快捷鍵F1一、創建資料庫1.語法1 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...