从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代码如下:
 IF OBJECT_ID(N'dbo.ufn_Days', 'FN') IS NOT NULL
BEGIN
DROP FUNCTION dbo.ufn_Days;
END
GO --==================================
-- 功能: 获得指定日期时间基于基准日期的总天数(一个整数值)
-- 说明: 如果指定的日期时间为NULL或者小于基准日期“--”时,则其值默认基准日期
-- 结果值为非负整数,从0开始计数。
-- 作者: XXX
-- 创建: yyyy-MM-dd
-- 修改: yyyy-MM-dd XXX 修改内容描述
-- 调用: SET @intDays = dbo.ufn_Days('2008-01-14')
--==================================
CREATE FUNCTION dbo.ufn_Days
(
@dtmDate DATETIME -- 指定的日期时间
) RETURNS INT
--$Encode$--
AS
BEGIN
IF @dtmDate IS NULL OR @dtmDate < '1900-01-01'
BEGIN
SET @dtmDate = '1900-01-01';
END -- datepart参数也可以为dd或d
RETURN DATEDIFF(DAY, '1900-01-01', @dtmDate)
END
GO IF OBJECT_ID(N'dbo.ufn_Days2Date', 'FN') IS NOT NULL
BEGIN
DROP FUNCTION dbo.ufn_Days2Date;
END
GO --==================================
-- 功能: 获得一个整数值基于基准日期对应的日期
-- 说明: 如果指定的整数值为NULL或为负整数时,则其值默认为0;
-- 如果指定的整数值大于“--”对应的整数值时,则其值默认设置为“--”对应的整数值
-- 结果值为从基准日期开始计数的日期
-- 作者: XXX
-- 创建: yyyy-MM-dd
-- 修改: yyyy-MM-dd XXX 修改内容描述
-- 调用: SET @dtmDate = dbo.ufn_Days2Date() --'2008-01-14'
--==================================
CREATE FUNCTION dbo.ufn_Days2Date
(
@intDays INT -- 指定的整数值
) RETURNS DATETIME
--$Encode$--
AS
BEGIN
IF @intDays IS NULL OR @intDays <
BEGIN
SET @intDays = ;
END DECLARE @intMaxDays AS INT;
SET @intMaxDays = dbo.ufn_Days('9999-12-31'); IF @intDays >= @intMaxDays
BEGIN
SET @intDays = @intMaxDays;
END -- datepart参数也可以为dd或d
RETURN DATEADD(DAY, @intDays, '1900-01-01');
END
GO
以上功能函数对的测试T-SQL代码如下:
 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对应的整数值';
SELECT dbo.ufn_Days2Date(NULL) AS 'NULL值对应的日期', dbo.ufn_Days2Date(-) AS '小于0对应的日期', dbo.ufn_Days2Date() AS '0对应的日期', dbo.ufn_Days2Date() AS '42374对应的日期', dbo.ufn_Days2Date() AS '2958463对应的整数值', dbo.ufn_Days2Date() AS '大于2958463对应的整数值';
GO

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

 
注意:以上功能函数对使用到了日期和时间函数datediffdateadd 
 
SQL Server周有关时间粒度
    
    SQL Server周有关时间粒度可以表述为以下问题:一个日期属于一年的第几周和一个日期属于当前所在周的周几。一个日期是否是工作日这个要根据是否上班来确定的,不然简单的根据周一到周五是工作日,周六和周日是休息日来判断的。第一个问题很用以通过datepart(weekday,  @dtmDateTime)来解决的,第二个问题则通过datename(weekday, @dtmDateTime)来得到结果的,不过结果值的展现形式要依赖默认语言的。
    
   将以上两个问题的解决封装在SQL Server标量函数中,其T-SQL代码如下:
 IF OBJECT_ID(N'dbo.ufn_WeekOfYear', 'FN') IS NOT NULL
BEGIN
DROP FUNCTION dbo.ufn_WeekOfYear;
END
GO --==================================
-- 功能: 获取指定日期属于当前第几周
-- 说明: 一年最多1-53周
-- 作者: XXX
-- 创建: yyyy-MM-dd
-- 修改: yyyy-MM-dd XXX 修改内容描述
-- 调用: SELECT dbo.ufn_WeekOfYear('2016-01-07');
--==================================
CREATE FUNCTION dbo.ufn_WeekOfYear
(
@dtmDate DATETIME
) RETURNS TINYINT
--$Encode$--
AS
BEGIN
-- datepart参数也可以为wk, ww
RETURN CAST(DATEPART(WEEK, @dtmDate) AS TINYINT)
END
GO IF OBJECT_ID(N'dbo.ufn_WeekdayNameOfWeek', 'FN') IS NOT NULL
BEGIN
DROP FUNCTION dbo.ufn_WeekdayNameOfWeek;
END
GO --==================================
-- 功能: 获取指定日期属于当前周周几的名称
-- 说明: 结果值的展示形式会以来默认设置语言
-- 作者: XXX
-- 创建: yyyy-MM-dd
-- 修改: yyyy-MM-dd XXX 修改内容描述
-- 调用: SELECT dbo.ufn_WeekOfYear('2016-01-07');
--==================================
CREATE FUNCTION dbo.ufn_WeekdayNameOfWeek
(
@dtmDate DATETIME
) RETURNS NVARCHAR()
--$Encode$--
AS
BEGIN
-- datepart参数也可以为dw
RETURN DATENAME(WEEKDAY, @dtmDate);
END
GO

测试以上函数效果的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代码如下:
 IF OBJECT_ID(N'dbo.ufn_WeekdayOfWeek', 'FN') IS NOT NULL
BEGIN
DROP FUNCTION dbo.ufn_WeekdayOfWeek;
END
GO --==================================
-- 功能: 获取指定日期隶属当前周周几
-- 说明: 结果值从1到7,分别对应从周一到周日,该值与@@DATEFISRT配置函数值保持一致
-- 作者: XXX
-- 创建: yyyy-MM-dd
-- 修改: yyyy-MM-dd XXX 修改内容描述
-- 调用: SELECT dbo.ufn_WeekdayOfWeek('2017-01-07') -- (表示星期四)
--==================================
CREATE FUNCTION dbo.ufn_WeekdayOfWeek
(
@dtmDate DATETIME -- 指定的日期时间
) RETURNS TINYINT
--$Encode$--
BEGIN
DECLARE
@tintDateFirst AS TINYINT,
@tintWeekDayIndexID AS TINYINT,
@tintSum AS TINYINT; SELECT
@tintDateFirst = @@DATEFIRST,
@tintWeekDayIndexID = DATEPART(WEEKDAY, @dtmDate),
@tintSum = @tintDateFirst + @tintWeekDayIndexID; RETURN (CASE WHEN @tintSum >= THEN @tintSum - WHEN @tintSum = THEN ELSE @tintSum - END);
END
GO
测试其功能的T-SQL代码如下:
 DECLARE @tintLoopID AS TINYINT;
SET @tintLoopID = ; DECLARE @dtmDate AS DATETIME;
SET @dtmDate = '2016-01-07'; SELECT @dtmDate AS 'date', DATENAME(WEEKDAY, @dtmDate) AS 'WeekdayName';
WHILE @tintLoopID <=
BEGIN
SET DATEFIRST @tintLoopID; SELECT @@DATEFIRST AS 'Start Day Of Week[1=星期一、2=星期二、3=星期三,……,7=星期日]', dbo.ufn_WeekdayOfWeek(@dtmDate) AS 'Nth Of Week1=星期一、2=星期二、3=星期三,……,7=星期日]' SET @tintLoopID = @tintLoopID + ;
END
GO
执行后的查询结果下图:

 

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

SQL Server时间粒度系列----第2节日期、周时间粒度详解的更多相关文章

  1. SQL Server时间粒度系列----第4节季、年时间粒度详解

    本文目录列表: 1.SQL Server季时间粒度2.SQL Server年时间粒度 3.总结语 4.参考清单列表   SQL Serve季时间粒度       季时间粒度也即是季度时间粒度.一年每3 ...

  2. SQL Server时间粒度系列----第1节时间粒度概述

    本文目录列表: 1.什么是时间粒度?2.SQL Server提供的时间粒度3.SQL Server时间粒度代码演示   4.SQL Server基准日期 5.总结语6.参考清单列表   什么是时间粒度 ...

  3. 【目录】sql server 进阶篇系列

    随笔分类 - sql server 进阶篇系列 sql server 下载安装标记 摘要: SQL Server 2017 的各版本和支持的功能 https://docs.microsoft.com/ ...

  4. SQL Server调优系列基础篇

    前言 关于SQL Server调优系列是一个庞大的内容体系,非一言两语能够分析清楚,本篇先就在SQL 调优中所最常用的查询计划进行解析,力图做好基础的掌握,夯实基本功!而后再谈谈整体的语句调优. 通过 ...

  5. SQL Server调优系列基础篇(常用运算符总结——三种物理连接方式剖析)

    前言 上一篇我们介绍了如何查看查询计划,本篇将介绍在我们查看的查询计划时的分析技巧,以及几种我们常用的运算符优化技巧,同样侧重基础知识的掌握. 通过本篇可以了解我们平常所写的T-SQL语句,在SQL ...

  6. SQL Server调优系列基础篇(并行运算总结篇二)

    前言 上一篇文章我们介绍了查看查询计划的并行运行方式. 本篇我们接着分析SQL Server的并行运算. 闲言少叙,直接进入本篇的正题. 技术准备 同前几篇一样,基于SQL Server2008R2版 ...

  7. SQL Server调优系列基础篇(索引运算总结)

    前言 上几篇文章我们介绍了如何查看查询计划.常用运算符的介绍.并行运算的方式,有兴趣的可以点击查看. 本篇将分析在SQL Server中,如何利用先有索引项进行查询性能优化,通过了解这些索引项的应用方 ...

  8. SQL Server调优系列进阶篇(查询语句运行几个指标值监测)

    前言 上一篇我们分析了查询优化器的工作方式,其中包括:查询优化器的详细运行步骤.筛选条件分析.索引项优化等信息. 本篇我们分析在我们运行的过程中几个关键指标值的检测. 通过这些指标值来分析语句的运行问 ...

  9. SQL Server调优系列进阶篇(深入剖析统计信息)

    前言 经过前几篇的分析,其实大体已经初窥到SQL Server统计信息的重要性了,所以本篇就要祭出这个神器了. 该篇内容会很长,坐好板凳,瓜子零食之类... 不废话,进正题 技术准备 数据库版本为SQ ...

  10. SQL Server调优系列进阶篇(如何维护数据库索引)

    前言 上一篇我们研究了如何利用索引在数据库里面调优,简要的介绍了索引的原理,更重要的分析了如何选择索引以及索引的利弊项,有兴趣的可以点击查看. 本篇延续上一篇的内容,继续分析索引这块,侧重索引项的日常 ...

随机推荐

  1. Metrics-Java版的指标度量工具之一

    Metrics是一个给JAVA服务的各项指标提供度量工具的包,在JAVA代码中嵌入Metrics代码,可以方便的对业务代码的各个指标进行监控,同时,Metrics能够很好的跟Ganlia.Graphi ...

  2. Async Console Programs 异步控制台程序

    如果你正在写一个控制台程序,你可能最终想要一个异步的main方法,像这样: class Program { static async void Main(string[] args) { ... } ...

  3. 图解js中常用的判断浏览器窗体、用户屏幕可视区域大小位置的方法

    有时我们需要获得浏览器窗口或屏幕的大小.窗口下拉框下拉的距离等数据,对应这些需求,js中提供了不少解决方法,只是数量稍多容易混淆它们各自的意义,下面咱们用图例来解释下12个常见对象属性的作用. 其中有 ...

  4. 《你必须知道的.NET》读书笔记:从Hello World认识IL

    通用的语言基础是.NET运行的基础,当我们对程序运行的结果有异议的时候,如何透过本质看表面,需要我们从底层来入手探索,这时候,IL便是我们必须知道的基础. 一.IL基础概念 1.1 什么是IL? IL ...

  5. [.net 面向对象编程基础] (10) 类的成员(字段、属性、方法)

    [.net 面向对象编程基础] (10) 类的成员(字段.属性.方法) 前面定义的Person的类,里面的成员包括:字段.属性.方法.事件等,此外,前面说的嵌套类也是类的成员. a.类的成员为分:静态 ...

  6. Linux 比较判断运算(if test)

    200 ? "200px" : this.width)!important;} --> 介绍 本篇文章主要是列举在shell命令中常出现的一些用来做比较的运算符,这些运算符是 ...

  7. 字符串查找String.IndexOf

    String.indexOf的模拟实现,没想象中有多么高深的查找算法,就是最普通的遍历查找 思路:先找到第一个相同的字符,然后依次比较后面的字符,若都相等则表示查找成功 /** * 查找字符串patt ...

  8. XMPie部署与创建过程 - 快速指南

    XMPie部署与创建过程 1PhotoShop.Indesign.VS2013关系.作用.使用 .1.1目的与过程 1. Photoshop负责导出cpkg文件. 1.1 动态性 如果你想要生成动态的 ...

  9. WebApi系列~dynamic让你的省了很多临时类

    回到目录 dynamic这个动态类型早在.net3.5时就已经出现了,当时是伴随的Linq一起让我们认识的,但在使用时总觉得有点别扭,因为它是internal的,所以不能跨程序集使用,这对于分层开发的 ...

  10. Atitit.java expression fsm 表达式词法分析引擎 v2 qaa.docx

    Atitit.java expression fsm 表达式词法分析引擎 v2 qaa.docx C:\0workspace\AtiPlatf_cms\src\com\attilax\fsm\Java ...