本文目录列表:
 
1、为什么要模拟实现datepart(week,date)的功能
 
SQL Server时间粒度系列----第2节日期、周时间粒度详解这篇博文中,就有个函数ufn_WeekOfYear----就是用了datepart(week,date)来实现获取,不过该函数是依赖@@datefirst这个全局变量值的,SQL Server 默认这个值时7(美国的习惯,周日作为一周的第一天),而我们中国则是周一作为一周的第一天的。后来在SQL Server时间粒度系列----第7节日历数据表详解这篇博文中,依然重新实现了ufn_WeekOfYear,当时没有充分的测试,现在发现也是存在bug的。后来又看到BIWork的这篇博文----SQL Server - 把星期一(周一)当作每个星期的开始在一年中求取周数,这才思考许久才重新实现类似datepart(week,date)的功能函数。
 
SQL Server提供的datepart(week,date)这个函数是依赖@@datefirst这个全局变量值的,由于不同的区域每周的第一天有所不同的,但是@@datefirst不能再函数中重新修改值(其实通过set datefirst num来修改的),这个是很大的不方面的,为了灵活地获取指定日期所在当前年的星期索引数值(索引数值从1开始计数,依次为1、2、……、51、52、53、54,下同),请继续往下看。
 
 
2、具体实现思路
 
由于知道了datepart(week,date)和@@datefirst的依赖关系以及其存在的不方便,将@@datefirst的值以一个参数的形式出现,这样就可以动态的设定一周的第一天啦。再加上指定的日期以及一周第一天索引数值(索引数值从1开始计数,依次为1、2、……、7,分别对应周一、周二、……、周日,例如:一周第一天索引值为1,即周一是一周的第一天)这两个参数来实现模拟datepart(week,date)函数的功能,具体思路大致分为如下步骤:
1)、获取指定日期所在当前年的第一天。
2)、获取指定日期所在当前周的日索引数值(索引数值从1开始计数,依次为1、2、……、7,分别对应周一、周二、……、周日)。
3)、获取指定日期和一周第一天索引值来获得当前年的第一周的第一天。
4)、指定日期、当前年的第一天和当前年的第一周的第一天这三个日期进行逻辑判断如下:
4.1)、指定日期大于等于当前年的第一天且小于当前年的第一周的第一天时,当前年的星期索引数值为1。
4.2)、4.1的否定为逻辑真时,先通过当前年的星期索引数值默认值为1在加上当前年的第一天与当前的第一周的第一天的日期天差除以7的值,然后通过当前年的第一天小于当前年的第一周的第一天时,将上边的结果值在加上1,否则上边的结果值就是当前年的星期索引数值。
 
可能以上文字表述具体的思路有些不太清楚,那就请继续看下面的T-SQL代码实现。
 
3、T-SQL代码实现逻辑以及测试效果
 
根据以上具体的思路,根据T-SQL语言来进行一种实现,T-SQL代码实现路基如下:
 IF OBJECT_ID(N'dbo.ufn_FirstDayOfYear', 'FN') IS NOT NULL
BEGIN
DROP FUNCTION dbo.ufn_FirstDayOfYear;
END
GO --==================================
-- 功能:获取指定日期所在当前年的第一天
-- 说明:具体功能说明
-- 兼容:运行SQL Server 2005+
-- 创建:2016-07-06 09:00-09:05 剑走江湖 创建实现
-- 修改:yyyy-MM-dd hh:mm-hh:mm XXX 修改内容描述
--==================================
CREATE FUNCTION dbo.ufn_FirstDayOfYear
(
@dtmDate AS DATETIME --指定日期
) RETURNS DATETIME
AS
BEGIN
RETURN DATEADD(YEAR, DATEDIFF(YEAR, 0, @dtmDate), 0);
END
GO IF OBJECT_ID(N'dbo.ufn_FirstWeekFirstDayOfYear', 'FN') IS NOT NULL
BEGIN
DROP FUNCTION dbo.ufn_FirstWeekFirstDayOfYear;
END
GO --==================================
-- 功能:获取指定日期所在当前年的第一周的第一天
-- 说明:可以通过参数@tntDateFirst动态指定一周开始的第一天,类似全局变量@@DATEFIRST的值且保持一致
-- 兼容:运行SQL Server 2005+
-- 创建:2016-07-06 09:05-09:15 剑走江湖 创建实现
-- 修改:yyyy-MM-dd hh:mm-hh:mm XXX 修改内容描述
--==================================
CREATE FUNCTION dbo.ufn_FirstWeekFirstDayOfYear
(
@dtmDate AS DATETIME --指定日期
,@tntDateFirst AS TINYINT = 1 --第一天日期(从1、2、3、……、7分别对应周一、周二、周三、……、周日)
) RETURNS DATETIME
AS
BEGIN
IF (@tntDateFirst IS NULL OR @tntDateFirst = 0 OR @tntDateFirst NOT BETWEEN 1 AND 7)
BEGIN
SET @tntDateFirst = 1;
END DECLARE @dtmFirstWeekFirstDayOfYear AS DATETIME;
SET @dtmFirstWeekFirstDayOfYear = 0; DECLARE
@dtmFirstDayOfYear AS DATETIME
,@dtmStartDate AS DATETIME
,@dtmEndDate AS DATETIME;
SELECT
@dtmFirstDayOfYear = [dbo].[ufn_FirstDayOfYear](@dtmDate)
,@dtmStartDate = @dtmFirstDayOfYear
,@dtmEndDate = DATEADD(DAY, 7, @dtmStartDate); WHILE (@dtmStartDate <= @dtmEndDate)
BEGIN
IF ([dbo].[ufn_DayOfWeek](@dtmStartDate) = @tntDateFirst)
BEGIN
SET @dtmFirstWeekFirstDayOfYear = @dtmStartDate; BREAK;
END SET @dtmStartDate = DATEADD(DAY, 1, @dtmStartDate);
END RETURN @dtmFirstWeekFirstDayOfYear;
END
GO IF OBJECT_ID(N'dbo.ufn_DayOfWeek', 'FN') IS NOT NULL
BEGIN
DROP FUNCTION dbo.ufn_DayOfWeek;
END
GO --==================================
-- 功能: 获取指定日期时间的所在当前周的日索引值(索引值从1开始计数,依次为1、2、……、7)
-- 说明: 运行在SQL Server 2005+。
-- 结果值从1到7,分别对应从周一到周日,该值与@@DATEFISRT配置函数值保持一致。
-- 使用(@@datefirst + datepart(weekday, @dtmDate))%7的结果值从2、3、4、5、6、0、1
-- 分别对应周一、周二、周三、周四、周五、周六、周日。
-- 兼容:运行SQL Server 2005+
-- 创建:2016-01-02 hh:mm-hh:mm 剑走江湖 创建实现
-- 修改:yyyy-MM-dd hh:mm-hh:mm XXX 修改内容描述
-- 调用: SELECT dbo.ufn_DayOfWeek('2017-01-07') -- 4(表示星期四)
--==================================
CREATE FUNCTION dbo.ufn_DayOfWeek
(
@dtmDate AS DATETIME -- 指定的日期时间
) RETURNS TINYINT
--$Encode$--
BEGIN
DECLARE @tntRemainder AS TINYINT;
SET @tntRemainder = (@@DATEFIRST + DATEPART(WEEKDAY, @dtmDate)) % 7; RETURN (CASE WHEN @tntRemainder <= 1 THEN @tntRemainder + 6 ELSE @tntRemainder - 1 END);
END
GO IF OBJECT_ID(N'dbo.ufn_WeekOfYear', 'FN') IS NOT NULL
BEGIN
DROP FUNCTION dbo.ufn_WeekOfYear;
END
GO --==================================
-- 功能:获取指定日期所在当前年的星期索引值(索引值从1开始计数,依次为1、2、……、51、52、53、54)
-- 说明:具体功能说明
-- 兼容:运行SQL Server 2005+
-- 创建:2016-07-06 09:15-09:35 剑走江湖 创建实现
-- 修改:yyyy-MM-dd hh:mm-hh:mm XXX 修改内容描述
--==================================
CREATE FUNCTION dbo.ufn_WeekOfYear
(
@dtmDate AS DATETIME --指定日期
,@tntDateFirst AS TINYINT = 1 --第一天日期(从1、2、3、……、7分别对应周一、周二、周三、……、周日)
) RETURNS TINYINT
AS
BEGIN
DECLARE @tntWeekOfYear AS TINYINT;
SET @tntWeekOfYear = 1; DECLARE
@dtmFirstDayOfYear AS DATETIME
,@dtmFirstWeekFirstDayOfYear AS DATETIME;
SELECT
@dtmFirstDayOfYear = dbo.[ufn_FirstDayOfYear](@dtmDate)
,@dtmFirstWeekFirstDayOfYear = dbo.[ufn_FirstWeekFirstDayOfYear](@dtmDate, @tntDateFirst); IF NOT(@dtmDate >= @dtmFirstDayOfYear AND @dtmDate < @dtmFirstWeekFirstDayOfYear)
BEGIN
SET @tntWeekOfYear = @tntWeekOfYear + DATEDIFF(DAY, @dtmFirstWeekFirstDayOfYear, @dtmDate) / 7; IF @dtmFirstDayOfYear < @dtmFirstWeekFirstDayOfYear
BEGIN
SET @tntWeekOfYear = @tntWeekOfYear + 1;
END
END RETURN @tntWeekOfYear;
END
GO
测试代码如下:
 DECLARE
@tntDateFirst AS TINYINT
,@tntMaxDateFirst AS TINYINT
,@dtmStartDate AS DATETIME
,@dtmEndDate AS DATETIME;
SELECT
@tntDateFirst = 1
,@tntMaxDateFirst = 7
,@dtmStartDate = '2000-01-01'
,@dtmEndDate = '2000-01-07';
WHILE (@tntDateFirst <= @tntMaxDateFirst)
BEGIN
SELECT
[T2].[FullDate]
,[T2].[DayOfWeek]
,[T2].[FirstWeekFirstDayOfYear]
,@tntDateFirst AS [FirstDateOfWeek]
,[T2].[WeekOfYear]
,COUNT(T2.[FullDate]) OVER (PARTITION BY YEAR(T2.[FullDate]), T2.[WeekOfYear]) AS DayCountOfWeek
,T2.[DefaultDayOfWeek]
,@@DATEFIRST AS [DefaultFirstDateOfWeek]
,[T2].[DefaultWeekOfYear]
,COUNT(T2.[FullDate]) OVER (PARTITION BY YEAR(T2.[FullDate]), T2.[DefaultWeekOfYear]) AS DefaultDayCountOfWeek
FROM (
SELECT
T.[FullDate]
,[dbo].[ufn_DayOfWeek](T.[FullDate]) AS [DayOfWeek]
,[dbo].[ufn_FirstWeekFirstDayOfYear](T.[FullDate], @tntDateFirst) AS [FirstWeekFirstDayOfYear]
,[dbo].[ufn_WeekOfYear](T.[FullDate], @tntDateFirst) AS [WeekOfYear]
,DATEPART(WEEKDAY, T.[FullDate]) AS [DefaultDayOfWeek]
,DATEPART(WEEK, T.[FullDate]) AS [DefaultWeekOfYear]
FROM (
SELECT DATEADD(DAY, [Num], @dtmStartDate) AS FullDate
FROM [dbo].[ufn_GetNums](0, DATEDIFF(DAY, @dtmStartDate, @dtmEndDate))
) AS T
) AS T2; SET @tntDateFirst = @tntDateFirst + 1;
END
GO
以上测试效果如下图:
 
4、总结语
 
由于我之前写过几个版本的ufn_WeekOfYear实现,但是通过博文发出去的有两个版本的,直到看到BIWork的SQL Server - 把星期一(周一)当作每个星期的开始在一年中求取周数这篇博文才发现自己之前发布的两个博文中的ufn_WeekOfYear是存在问题的,这才花费了不少时间再次梳理和思考这个功能的实现,都说变成代码需要测试,T-SQL代码一样要严格的单元测试才行,不然真是迷惑了自己,也误导了别人的。
  
5、参考清单列表

模拟实现SQL Server中的datepart(week,date)的功能的更多相关文章

  1. SQL Server中的DATEPART函数的使用

    下面文章来自:http://blog.csdn.net/hello_world_wusu/article/details/4632049 定义和用法 DATEPART() 函数用于返回日期/时间的单独 ...

  2. 在sql server中利用with as实现递归功能

    在sqlserver2005之前,要实现递归功能比较麻烦,比如可能会要用到临时表与while语句来循环.自sqlserver2005之后,新增了with as功能语法,即 公用表达式(CTE),让递归 ...

  3. SQL Server中SELECT会真的阻塞SELECT吗?

    在SQL Server中,我们知道一个SELECT语句执行过程中只会申请一些意向共享锁(IS) 与共享锁(S), 例如我使用SQL Profile跟踪会话86执行SELECT * FROM dbo.T ...

  4. Microsoft SQL Server中的事务与并发详解

    本篇索引: 1.事务 2.锁定和阻塞 3.隔离级别 4.死锁 一.事务 1.1 事务的概念 事务是作为单个工作单元而执行的一系列操作,比如查询和修改数据等. 事务是数据库并发控制的基本单位,一条或者一 ...

  5. Sql Server中查询今天、昨天、本周、上周、本月、上月数据

    Sql Server中查询今天.昨天.本周.上周.本月.上月数据 在做Sql Server开发的时候有时需要获取表中今天.昨天.本周.上周.本月.上月等数据,这时候就需要使用DATEDIFF()函数及 ...

  6. SQL Server 中的事务与事务隔离级别以及如何理解脏读, 未提交读,不可重复读和幻读产生的过程和原因

    原本打算写有关 SSIS Package 中的事务控制过程的,但是发现很多基本的概念还是需要有 SQL Server 事务和事务的隔离级别做基础铺垫.所以花了点时间,把 SQL Server 数据库中 ...

  7. 模拟实现SQL Server字段列显示的数据类型

    本文目录列表: 1.SQL Server表设计视图中的数据类型列展示效果 2.模拟实现类似的数据类型显示效果 3.测试效果 4.总结语 5.参考清单列表   1.SQL Server表设计视图中的数据 ...

  8. 【转】T-SQL查询进阶—理解SQL Server中的锁

      简介 在SQL Server中,每一个查询都会找到最短路径实现自己的目标.如果数据库只接受一个连接一次只执行一个查询.那么查询当然是要多快好省的完成工作.但对于大多数数据库来说是需要同时处理多个查 ...

  9. SQL Server中关于跟踪(Trace)那点事

    前言 一提到跟踪俩字,很多人想到警匪片中的场景,同样在我们的SQL Server数据库中“跟踪”也是无处不在的,如果我们利用好了跟踪技巧,就可以针对某些特定的场景做定向分析,找出充足的证据来破案. 简 ...

随机推荐

  1. PHP 文件处理

    $handler = fopen('./abc.html', 'w'); if(!feof($handler)){ // 读取文件末尾,也可以用file_exists mkdir('./abc.htm ...

  2. Java工作环境笔记

    环境 1. Jvm最简生存指南: http://www.importnew.com/10127.html 2. 所有路径中,不要出现中文,即使开始的时候,调试Tomcat时,路径有中文也可以,你真不知 ...

  3. Unity3D游戏开发初探—4.开发一个“疯狂击箱子”游戏

    一.预备知识—对象的”生“与”死“ (1)如何在游戏脚本程序中创建对象而不是一开始就创建好对象?->使用GameObject的静态方法:CreatePrimitive() 以上一篇的博文中的“指 ...

  4. MySQL mysqlslap压测

    200 ? "200px" : this.width)!important;} --> 介绍 mysqlslap是mysql自带的一个性能压测工具:mysqlslap用于和其 ...

  5. 谈初学Java历程

    学习Java一个月左右,本来很早就想好好静下心来写一点东西了.但由于不想手写,文档写了不知道放在哪好,所以一直拖着.最近注册了博客园,还是挺方便的. 即将大学毕业了,则面临了所以大学生所面临的问题,就 ...

  6. SpringMVC常用的注解

    1. @Controller @Controller用于标识控制层主键,负责注册一个bean到spring上下文,bean的ID默认为首字母小写的类名称,用户也可以自定义. 例如: 方式一:  @Co ...

  7. 谈谈关键字strictfp

     Java语言中的其中一个设计目标是可移植性.无论在哪个虚拟机上运行,同一个计算应该得到同样的结果.对于浮点数的算术运算,实现这样的可移植性是相当困难的.double 类型使用 64 位存储一个 do ...

  8. 【Win10应用开发】自定义桌面壁纸

    调用通用的API来设置桌面壁纸,是一件既简单又有趣的事情,结合XAML可以生成图像的特性,你甚至可以做一个应用,让用户用他所拍的照片做成一张自定义壁纸,然后作为桌面壁纸. 这个API是通用的,应用运行 ...

  9. sizzle分析记录:词法分析器(tokenize)

    词法分析器(tokenize)? 词法分析器又称扫描器.词法分析是指将我们编写的文本代码流解析为一个一个的记号,分析得到的记号以供后续语法分析使用. sizzle引入了tokenize这个概念,意义? ...

  10. CSS list-style属性控制ul标签样式

    试一试 <style type="text/css" media="all"> ul { list-style-type: disc; } ul#c ...