从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. 图解集合2:LinkedList

    初识LinkedList 上一篇中讲解了ArrayList,本篇文章讲解一下LinkedList的实现. LinkedList是基于链表实现的,所以先讲解一下什么是链表.链表原先是C/C++的概念,是 ...

  2. angular controller as syntax vs scope

    今天要和大家分享的是angular从1.2版本开始带来了新语法Controller as.再次之前我们对于angular在view上的绑定都必须使用直接的scope对象,对于controller来说我 ...

  3. Nim编码风格

    介绍 Nim语言不限制开发人员使用哪种具体的编码风格, 但为了社区的发展,在编写一些标准库的时候还是应该遵从统一的编码风格 这篇文章会列出一系列的编码风格准则,供大家参考. 但值得注意的是,有很多例外 ...

  4. 1、CC2541蓝牙4.0芯片中级教程——基于OSAL操作系统的运行流程了解+定时器和串口例程了解

    本文根据一周CC2541笔记汇总得来—— 适合概览和知识快速索引—— 全部链接: 中级教程-OSAL操作系统\OSAL操作系统-实验01 OSAL初探 [插入]SourceInsight-工程建立方法 ...

  5. Thrift架构~windows下安装和Hello World及编码引起的错误

    最近开始正式接触Thrift架构,很牛B的技术,它被apache收纳了,属于开源中的一员,呵呵. 概念: Thrift源于大名鼎鼎的facebook之手,在2007年facebook提交Apache基 ...

  6. VM~Linux联不上网

    使用桥接的链接方式,centos6.4配置静态ip,能ping通网关,但ping 外网时出现 "network is unreachable" 如:ping www.baidu.c ...

  7. Atitit 编程语言常用算法attilax总结

    Atitit 编程语言常用算法attilax总结 1. 编译算法分类and   数据操作算法.1 1.1. Tab driver stat  状态转换表格算法1 1.2. Nest case 词法分析 ...

  8. Atitit mtp ptp rndis midi协议的不同区别

    Atitit mtp ptp rndis midi协议的不同区别 1. PTP:1 2. MTP:1 3. Mtp 与usb区别2 4. 不过和UMS相比,MTP也有不足之处:3 5.  MTP协议介 ...

  9. Spring Cloud 配置服务

    Spring Cloud 配置服务 1. 配置服务简介 产生背景: 传统开发中,我们通常是将系统的业务无关配置(数据库,缓存服务器)在properties中配置,在这个文件中不会经常改变,但随着系统规 ...

  10. java中string内存的相关知识点

    (一):区别java内存中堆和栈: 1.栈:数据可以共享,存放基本数据类型和对象的引用,其中对象存放在堆中,对象的引用存放在栈中: 当在一段代码块定义一个变量时,就在栈中 为这个变量分配内存空间,当该 ...