从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的日期部分范围区间是[1753-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 NULL2 BEGIN3 DROP FUNCTION dbo.ufn_Days;4 END5 GO6

7 --==================================

8 --功能: 获得指定日期时间基于基准日期的总天数(一个整数值)9 -- 说明: 如果指定的日期时间为NULL或者小于基准日期“1900-01-01”时,则其值默认基准日期10 --结果值为非负整数,从0开始计数。11 --作者: XXX12 -- 创建: yyyy-MM-dd13 -- 修改: yyyy-MM-dd XXX 修改内容描述14 -- 调用: SET @intDays = dbo.ufn_Days('2008-01-14')15 --==================================

16 CREATE FUNCTION dbo.ufn_Days17 (18 @dtmDate DATETIME --指定的日期时间19 ) RETURNS INT20 --$Encode$--

21 AS22 BEGIN23 IF @dtmDate IS NULL OR @dtmDate < '1900-01-01'

24 BEGIN25 SET @dtmDate = '1900-01-01';26 END27

28 --datepart参数也可以为dd或d29 RETURN DATEDIFF(DAY, '1900-01-01', @dtmDate)30 END31 GO32

33 IF OBJECT_ID(N'dbo.ufn_Days2Date', 'FN') IS NOT NULL34 BEGIN35 DROP FUNCTION dbo.ufn_Days2Date;36 END37 GO38

39 --==================================

40 --功能: 获得一个整数值基于基准日期对应的日期41 --说明: 如果指定的整数值为NULL或为负整数时,则其值默认为0;42 -- 如果指定的整数值大于“9999-12-31”对应的整数值时,则其值默认设置为“9999-12-31”对应的整数值43 --结果值为从基准日期开始计数的日期44 --作者: XXX45 -- 创建: yyyy-MM-dd46 -- 修改: yyyy-MM-dd XXX 修改内容描述47 -- 调用: SET @dtmDate = dbo.ufn_Days2Date(39459) --'2008-01-14'

48 --==================================

49 CREATE FUNCTION dbo.ufn_Days2Date50 (51 @intDays INT --指定的整数值52 ) RETURNS DATETIME53 --$Encode$--

54 AS55 BEGIN56 IF @intDays IS NULL OR @intDays < 0

57 BEGIN58 SET @intDays = 0;59 END60

61 DECLARE @intMaxDays AS INT;62 SET @intMaxDays = dbo.ufn_Days('9999-12-31');63

64 IF @intDays >=@intMaxDays65 BEGIN66 SET @intDays =@intMaxDays;67 END68

69 --datepart参数也可以为dd或d70 RETURN DATEADD(DAY, @intDays, '1900-01-01');71 END72 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

执行后的查询结果如下图:

注意:以上功能函数对使用到了日期和时间函数datediff和dateadd。

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 NULL2 BEGIN3 DROP FUNCTION dbo.ufn_WeekOfYear;4 END5 GO6

7 --==================================

8 --功能: 获取指定日期属于当前第几周9 -- 说明: 一年最多1-53周10 --作者: XXX11 -- 创建: yyyy-MM-dd12 -- 修改: yyyy-MM-dd XXX 修改内容描述13 -- 调用: SELECT dbo.ufn_WeekOfYear('2016-01-07');14 --==================================

15 CREATE FUNCTION dbo.ufn_WeekOfYear16 (17 @dtmDate DATETIME18 ) RETURNS TINYINT19 --$Encode$--

20 AS21 BEGIN22 --datepart参数也可以为wk, ww23 RETURN CAST(DATEPART(WEEK, @dtmDate) AS TINYINT)24 END25 GO26

27 IF OBJECT_ID(N'dbo.ufn_WeekdayNameOfWeek', 'FN') IS NOT NULL28 BEGIN29 DROP FUNCTION dbo.ufn_WeekdayNameOfWeek;30 END31 GO32

33 --==================================

34 --功能: 获取指定日期属于当前周周几的名称35 --说明: 结果值的展示形式会以来默认设置语言36 --作者: XXX37 -- 创建: yyyy-MM-dd38 -- 修改: yyyy-MM-dd XXX 修改内容描述39 -- 调用: SELECT dbo.ufn_WeekOfYear('2016-01-07');40 --==================================

41 CREATE FUNCTION dbo.ufn_WeekdayNameOfWeek42 (43 @dtmDate DATETIME44 ) RETURNS NVARCHAR(20)45 --$Encode$--

46 AS47 BEGIN48 --datepart参数也可以为dw49 RETURN DATENAME(WEEKDAY, @dtmDate);50 END51 GO52

测试以上函数效果的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 NULL2 BEGIN3 DROP FUNCTION dbo.ufn_WeekdayOfWeek;4 END5 GO6

7 --==================================

8 --功能: 获取指定日期隶属当前周周几9 --说明: 结果值从1到7,分别对应从周一到周日,该值与@@DATEFISRT配置函数值保持一致10 --作者: XXX11 -- 创建: yyyy-MM-dd12 -- 修改: yyyy-MM-dd XXX 修改内容描述13 -- 调用: SELECT dbo.ufn_WeekdayOfWeek('2017-01-07') -- 4(表示星期四)14 --==================================

15 CREATE FUNCTION dbo.ufn_WeekdayOfWeek16 (17 @dtmDate DATETIME --指定的日期时间18 ) RETURNS TINYINT19 --$Encode$--

20 BEGIN21 DECLARE22 @tintDateFirst AS TINYINT,23 @tintWeekDayIndexID AS TINYINT,24 @tintSum AS TINYINT;25

26 SELECT27 @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 - 1END);32 END33 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 BEGIN10 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 END16 GO17

执行后的查询结果下图:

总结语

本文我们了解到MySQL提供的将日期和整数相互转换的功能函数对,还了解了序列的概念,又提供了SQL Server将日期和整数相互转换的实现,最后也实现了一个日期隶属当年的第几周以及其隶属当前周周几名称的标量函数。(我中间一直在尝试通过datepart(weekday, @dtmDateTime)过去指定日期所在本周的索引值从1开始计数到7,不过这个要根据@@datefirst配置函数来设置一周的第一天为周几,周一到周日分别对应1到7,前者的值与后者的值相关,但是不会时刻保持相同,例如美国默认周日是一周的第一天,也是@@datefirst为7(通过SET DATEFIRST N来设置),如果通过datepart(weekday, @dtmDateTime)获得的结果值为2,改日期在其隶属周围星期一。目前还没有实现,中间花费了不少的时间,博文针对这个问题的解决提供一个实现。博友如果更好的思路,请提出宝贵的建议。)

参考清单列表

mysql 时间粒度_SQL Server时间粒度系列----第2节日期、周时间粒度详解相关推荐

  1. [系统安全] 四十四.APT系列(9)Metasploit技术之基础用法万字详解及防御机理

    您可能之前看到过我写的类似文章,为什么还要重复撰写呢?只是想更好地帮助初学者了解病毒逆向分析和系统安全,更加成体系且不破坏之前的系列.因此,我重新开设了这个专栏,准备系统整理和深入学习系统安全.逆向分 ...

  2. Git学习系列之Git基本操作提交项目(图文详解)

    前面博客 Git学习系列之Git基本操作克隆项目(图文详解) 然后可以 cd 切换到 LispGentleIntro 目录, 新增或者修改某些文件.这里只是模拟一下操作, 实际情况可能是 使用 Ecl ...

  3. Linux Shell脚本入门教程系列之(八)Shell printf命令详解

    本文是Linux Shell脚本系列教程的第(八)篇,更多shell教程请看:Linux Shell脚本系列教程 在上一篇:Linux Shell系列教程之(七)Shell输出这篇文章中,已经对She ...

  4. 离线强化学习(Offline RL)系列3: (算法篇) IQL(Implicit Q-learning)算法详解与实现

    [更新记录] 论文信息:Ilya Kostrikov, Ashvin Nair, Sergey Levine: "Offline Reinforcement Learning with Im ...

  5. MySQL隔离级别--未提交读,提交读,可重复读,序列化--详解(有示例)

    原文网址:MySQL隔离级别--未提交读,提交读,可重复读,序列化--详解(有示例)_IT利刃出鞘的博客-CSDN博客 简介          本文介绍MySQL的事务隔离级别的含义,并用示例说明各个 ...

  6. 上手Redis系列(六):超全Geospatial特殊类型详解(地理位置)

    学习Redis之前,我还是觉得我务必跟你说一声,也是在我文章之中说的很多的一句话,我想也会适用于学习Redis,那就是在接触文章里的Reids命令时,不用试图去记这些命令 ,用到时去看API帮助文档即 ...

  7. 【Unity3D-UGUI系列】(一)Canvas 画布组件详解

    推荐阅读 CSDN主页 GitHub开源地址 Unity3D插件分享 简书地址 我的个人博客 QQ群:1040082875   大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉 ...

  8. SpringSecurity权限管理框架系列(六)-Spring Security框架自定义配置类详解(二)之authorizeRequests配置详解

    1.预置演示环境 这个演示环境继续沿用 SpringSecurit权限管理框架系列(五)-Spring Security框架自定义配置类详解(一)之formLogin配置详解的环境. 2.自定义配置类 ...

  9. MySQL DTAETIME、TIMESTAMP、DATE、TIME、YEAR(日期和时间类型)

    MySQL 中有多处表示日期的数据类型:YEAR.TIME.DATE.DTAETIME.TIMESTAMP.当只记录年信息的时候,可以只使用 YEAR 类型. 每一个类型都有合法的取值范围,当指定确定 ...

最新文章

  1. 20、21_链式法则、MLP(多层感知器模型)反向传播
  2. Nginx 笔记与总结(11)Nginx + php-fpm + MySQL 安装 ecshop
  3. java pdfbox2 中文乱码,如何从PDFBox 2.0.2中的PDDocument提取字体
  4. RPC简介及框架选择
  5. C# 使用 HelpProvider 控件调用帮助文件
  6. 生产者消费者模型 java
  7. java 内部类 加载_Java 内部类基础介绍
  8. android文字立体,3d立体字转换器
  9. OLAP 系统选型,选 Apache Kylin 还是 ClickHouse?
  10. 第二章 Dubbo框架
  11. 关于文件关联的图标不能正常显示
  12. 【算法学习】前缀树Trie
  13. 人工智能 AI 绘画发展史
  14. 关于程序员发展的一些遐想-个人笔记
  15. 回归预测 | MATLAB实现贝叶斯优化门控循环单元(BO-GRU)多输入单输出
  16. VM虚拟机双网卡配置
  17. [4G5G专题-97]:MAC层- 调度 - 上行调度的原理、过程与算法
  18. 【论文】文本相似度计算方法综述
  19. java ogv格式_COMSOL Multiphysics
  20. 以红酒数据集分类为例做决策树的可视化

热门文章

  1. Samba实现文件共享
  2. 2018河北计算机对口高考学校6,[2018年最新整理]2014河北省对口升学计算机高考试题A.doc...
  3. 《淘宝店铺设计装修一册通》一1.3 店铺装修必须注意的问题
  4. 抖音怎么显示服务器开小差,抖音服务器不能看直播是怎么办 用户打不开不能点赞干着急...
  5. 新手玩抖音快手上热门推荐必掉的七大坑?-阿甘电商
  6. vue实现第三方登录——qq
  7. ns3学习(一)——first.cc
  8. qq飞车鸿蒙车队,QQ飞车手游车队系统玩法详解 车队系统怎么玩
  9. 使用npm的一些问题
  10. 保护你的数字资产:网络安全的重要性和应对策略