本文目录列表:
 
位运算
 
SQL Server支持的按位运算符有三个,分别为:按位与(&)按位或(|)按位异或(^)。位运算符用于 int、smallint 或 tinyint 数据,目前SQL Server能支持的按位运算的最大整数类型为Int(4字节整数)。有关以上三个按位运算符的详细使用,请参考微软的SQL Server的联机帮助。
 
设置日期数据表节假日标志
 
上篇博文(日历数据表详解)中,总结出来一个日历数据表,该表的字段列(WorkDayFlag bit)表示是否工作日。默认设置周一到周五为工作日,周六和周日为非工作日的。但是国家法定节假日有时候会占用默认工作日(周一到周五中一天或若干天)的,这就要涉及设置工作日的功能。这个大家很显然能想到一条日历数据记录的进行设置,也是基于迭代或游标的方法。
 
基于迭代或游标的方法仔细想想确实存在性能问题的,一个月至少有28天,最多的有31天,如果设置一个月中每天的工作日标志字段列值,需要很多次的数据库连接资源的,即便共享一次数据库连接,多次执行的也是存在性能问题的。那如何解决常规方式存在的性能问题呢?
 
我们先不说解决方案,我们先从每月包含的最大天数31来说起。我们再来看SQL Server 提供的数据类型int,这是个有符号的4字节整数,共计32位,第32位为符号位,对于非负数该符号位为0,对于负数该符号位为1。非负整数的int只有31位来存储数据的;每个月最大天数是31天,这两者确实很巧合吧。如果我们将一个月的每一天分别对应一个int的每一位,从第一天到最后一天分别对应int的第一位到第31位,如以下表格:
月内日索引(从1开始计数) int位索引(从0开始计数)
1
0
2
1
3
2
……
……
28
28
29
28
30
29
31
30

月内日索引和int索引相差1,这个很容易发现的。

 
既然知道了月内日索引( 我们设位变量DayOfMonth)和int位索引(我们设置变量为BitOfInt)的关系,两者之间的关系使用变量表示为:DayOfMonth - 1 = BitOfInt,那么我们就是用一个int值来保存一个月所有的工作日标志值(每个工作日标志值:1*power(2,DayOfMonth-1), 整数的位值:1*power(2, BitOfInt),这两者是相等的)之和(我们设置变量为WorkDayValueOfMonth),。这样就可以基于集合的方法来设置一个月的工作日标志。有了工作日标志值之和的int数值,那么如何分别设置每一天的工作日标志值呢?这个就要使用SQL Server提供的按位与运算符,如果(1*power(2,DayOfMonth-1)) & WorkDayValueOfMonth 的按位与的结果值为1,那么DayOfMonth所对应的的天则设置为了工作日;如果其结果为0,则设置为了非工作日。
 
基于集合的方法来设置一个月的每天的工作日标志,也不是每一天都可以设置的,我们设置工作日标志有关前提,所设置的工作日必须大于当前日(今天)的,小于当前日的我们不做设置,只能按照默认工作日设置(周一到周五为工作日,周六和周日为非工作日)。
 
既然有了设置工作日标志的方法,当然也有获取指定月的工作日值和和当前月的工作日的数量。根据以上我们提供两个存储过程,分别对应设置工作日标志或获取工作日标志的功能。
 
设置工作日标志的存储过程,T-SQL代码如下:
 IF OBJECT_ID(N'dbo.usp_Calendar_WorkDaySet', 'P') IS NOT NULL
BEGIN
DROP PROCEDURE dbo.usp_Calendar_WorkDaySet;
END
GO --==================================
-- 功能: 设置指定月份的工作日标志
-- 说明: 具体实现阐述
-- 作者: XXX
-- 创建: yyyy-MM-dd
-- 修改: yyyy-MM-dd XXX 修改内容描述
--================================== CREATE PROCEDURE usp_Calendar_WorkDaySet
(
@intMonths INT, -- 指定的日期月数
@intWorkDayValueSum INT, -- 指定的日期月数的所有工作日标志值之和
@bitIsUseDefault BIT = 0, -- 是否使用默认设置,1:使用默认设置(周一到周五为工作日,周六和周日非工作日),0:基于指定的日期月数的所有工作日标志值之和来设置 -- 方便记录用户操作日志
--@chvnUser NVARCHAR(20), -- 指定的用户
--@intUserID INT, -- 指定的用户ID
--@chvUserIP VARCHAR(40), -- 指定的用户IP
--@chvnUserFrom NVARCHAR(30), -- 指定的用户位置 @chvnErrMsg NVARCHAR(100) OUTPUT -- 错误异常消息字符串
)
--$Encode$--
AS
BEGIN
SET NOCOUNT ON; SET @intMonths = dbo.ufn_GetValidDateNum(@intMonths); IF @intWorkDayValueSum IS NULL OR @intWorkDayValueSum < 0
BEGIN
SET @intWorkDayValueSum = 0;
END SET @chvnErrMsg = N''; DECLARE @tintResultValue AS TINYINT;
SET @tintResultValue = 1; -- 默认存在错误 DECLARE
@dtmNow AS DATETIME,
@intDays AS INT;
SELECT
@dtmNow = GETDATE(),
@intDays = dbo.ufn_Days(@dtmNow); IF @intMonths < dbo.ufn_Months(@dtmNow)
BEGIN
SET @chvnErrMsg = N'不能设置小于当前月份的工作日标志。'; RETURN @tintResultValue;
END DECLARE
@WorkDayValueSum AS INT,
@DayCount AS INT;
SELECT
@WorkDayValueSum = 0,
@DayCount = 0; SELECT
@WorkDayValueSum = SUM(POWER(2, [DayOfMonth] - 1))
,@DayCount = COUNT(1)
FROM dbo.Calendar
WHERE Months = @intMonths
AND [Days] >= @intDays + 1; IF @DayCount = 0 OR @WorkDayValueSum = 0
BEGIN
SET @chvnErrMsg = N'日历数据表不存在满足条件的数据。'; RETURN @tintResultValue;
END IF @intWorkDayValueSum = @WorkDayValueSum
BEGIN
SET @tintResultValue = 0; RETURN @tintResultValue;
END DECLARE @intRowCount AS INT;
SELECT @intRowCount = 0; BEGIN TRY
IF @bitIsUseDefault = 0
BEGIN
UPDATE Calendar
SET WorkdayFlag = POWER(2, [DayOfMonth] - 1) & @intWorkDayValueSum
WHERE Months = @intMonths
AND [Days] >= @intDays + 1;
END
ELSE
BEGIN
UPDATE Calendar
SET WorkdayFlag = CASE WHEN dbo.ufn_DayOfWeek(CalendarDate) <= 5 THEN 1 ELSE 0 END
WHERE Months = @intMonths
AND [Days] >= @intDays + 1;
END SET @intRowCount = @@ROWCOUNT; SET @tintResultValue = 0;
END TRY
BEGIN CATCH
SET @chvnErrMsg = N'设置指定月的工作日标志发生错误。'; RETURN @tintResultValue;
END CATCH RETURN @tintResultValue;
END
GO -- Test Code
DECLARE
@intMonths AS INT,
@intWorkDayValueSum AS INT,
@bitIsUseDefault AS BIT,
@chvnErrMsg AS NVARCHAR(100),
@tintResultVaule AS TINYINT;
SELECT
@intMonths = 0, -- int
@intWorkDayValueSum = 0, -- int
@bitIsUseDefault = NULL, -- bit
@chvnErrMsg = N'', -- nvarchar(100)
@tintResultVaule = 1; -- tinyint EXEC @tintResultVaule = dbo.usp_Calendar_WorkDaySet
@intMonths = @intMonths, -- int
@intWorkDayValueSum = @intWorkDayValueSum, -- int
@bitIsUseDefault = @bitIsUseDefault, -- bit
@chvnErrMsg = @chvnErrMsg OUTPUT -- nvarchar(100) SELECT @chvnErrMsg AS 'Error Message'
,@tintResultVaule AS 'Return Value';
GO -- Test Code
-- 2016-02月份
-- 根据国家节假日获取的工作日标志值和以及工作日总数
SELECT
WorkDayValueSum = SUM(T.WorkDayFlag2 * POWER(2, T.[DayOfMonth] - 1))
,WorkDayCount = SUM(T.WorkDayFlag2 * 1)
FROM (
SELECT
Months
,[DayOfMonth]
,WorkDayFlag
,WorkDayFlag2 = CASE
WHEN [DayOfMonth] = 6 THEN 1
WHEN [DayOfMonth] BETWEEN 7 AND 12 THEN 0
WHEN [DayOfMonth] = 14 THEN 1
ELSE WorkDayFlag END
FROM dbo.Calendar
WHERE Months = dbo.ufn_Months('2016-02-01')
) AS T
GO DECLARE
@intMonths AS INT,
@intWorkDayValueSum AS INT,
@bitIsUseDefault AS BIT,
@chvnErrMsg AS NVARCHAR(100),
@tintResultVaule AS TINYINT;
SELECT
@intMonths = dbo.ufn_Months('2016-02-01'), -- int
@intWorkDayValueSum = 333963327, -- int
@bitIsUseDefault = 0, -- bit
@chvnErrMsg = N'', -- nvarchar(100)
@tintResultVaule = 1; -- tinyint EXEC @tintResultVaule = dbo.usp_Calendar_WorkDaySet
@intMonths = @intMonths, -- int
@intWorkDayValueSum = @intWorkDayValueSum, -- int
@bitIsUseDefault = @bitIsUseDefault, -- bit
@chvnErrMsg = @chvnErrMsg OUTPUT -- nvarchar(100) SELECT @chvnErrMsg AS 'Error Message'
,@tintResultVaule AS 'Return Value';
GO
测试以上存储的功能效果,如下图:
 
查询201602月份的日历数据,如下图:
 
获取工作日标志的存储过程,T-SQL代码如下:
 IF OBJECT_ID(N'dbo.usp_Calendar_WeekDayGet', 'P') IS NOT NULL
BEGIN
DROP PROCEDURE dbo.usp_Calendar_WeekDayGet
END
GO --==================================
-- 功能: 获取满足条件的日期月数的工作日值和工作日总数
-- 说明: 具体实现阐述
-- 作者: XXX
-- 创建: yyyy-MM-dd
-- 修改: yyyy-MM-dd XXX 修改内容描述
--==================================
CREATE PROCEDURE dbo.usp_Calendar_WeekDayGet
(
@intStartMonths INT, -- 指定的开始日期月数
@intEndMonths INT -- 指定的结束日期月数
)
--$Encode$--
AS
BEGIN
SET NOCOUNT ON; SET @intStartMonths = dbo.ufn_GetValidDateNum(@intStartMonths);
SET @intEndMonths = dbo.ufn_GetValidDateNum(@intEndMonths); IF @intStartMonths > @intEndMonths
BEGIN
DECLARE @intTemp AS INT;
SET @intTemp = @intStartMonths;
SET @intStartMonths = @intEndMonths;
SET @intEndMonths = @intTemp;
END SELECT
Months
,WorkDayValueSum = ISNULL(SUM(WorkDayFlag * POWER(2, [DayOfMonth] - 1)), 0)
,WorkDayCount = ISNULL(SUM(WorkDayFlag * 1), 0)
FROM dbo.Calendar
WHERE Months BETWEEN @intStartMonths AND @intEndMonths
GROUP BY Months;
END
GO -- Test Code
DECLARE
@intStartMonths AS INT,
@intEndMonths AS INT,
@tintResultValue AS TINYINT; SELECT
@intStartMonths = dbo.ufn_Months('2015-06-01'),
@intEndMonths = dbo.ufn_Months('2016-03-02'),
@tintResultValue = 1; -- 默认范围值 EXEC @tintResultValue = dbo.usp_Calendar_WeekDayGet
@intStartMonths = @intStartMonths, -- int
@intEndMonths = @intEndMonths; -- int SELECT @tintResultValue AS 'Return Value (1:Have Error,0:No Error)'
GO
 
测试以上存储的功能效果,如下图:
 
总结语
 
    本文提起了SQL Server的按位运算符,重点讲解了日历数据表中的工作日标志的设置的处理方法,基于集合的处理方法,结合按位与运算符来处理的方法。
 
参考清单列表

SQL Server时间粒度系列----第8节位运算以及设置日历数据表节假日标志详解的更多相关文章

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

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

  2. SQL Server时间粒度系列----第9节时间粒度示例演示

    本文目录列表: 1.准备测试数据 2.向测试数据表添加相关时间粒度字段列 3.基于日月季年统计汇总的演示 4.总结语 5.参考清单列表   准备测试数据   为了提供不同时间粒度示例的演示,就需要测试 ...

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

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

  4. SQL Server时间粒度系列----第3节旬、月时间粒度详解

    本文目录列表: 1.SQL Server旬时间粒度2.SQL Server月有关时间粒度 3.SQL Server函数重构 4.总结语 5.参考清单列表   SQL Server旬时间粒度       ...

  5. SQL Server时间粒度系列----第5节小时、分钟时间粒度详解

    本文目录列表: 1.SQL Server小时时间粒度2.SQL Server分钟时间粒度 3.总结语 4.参考清单列表   SQL Server小时时间粒度          这里说的时间粒度是指带有 ...

  6. SQL Server时间粒度系列----第6节基于当前日的小时数和分钟数与mysql unix_timestamp和from_unixtime的mssql实现

    本文目录列表: 1.基于当前日的小时数和分钟数2.mysql unix_timestamp和from_unixtime的mssql实现 3.总结语 4.参考清单列表   基于当前日的小时数和分钟数   ...

  7. SQL Server时间粒度系列

        工作中经常遇到针对业务部门提出不同时间粒度(年.季度.月.周.日等等日期时间粒度,以下简称时间粒度)的数据统计汇总任务,也看到不少博友针对这方便的博文,结合SQL Server的日期时间函数和 ...

  8. SQL Server时间粒度系列----第7节日历数据表详解

    本文目录列表: 1.时间粒度有关描述 2.时间维度有关功能函数3.日历数据表 4.日历数据表数据填充 5.总结语 6.参考清单列表   时间粒度有关描述   将该系列涉及到的时间粒度以及分钟以下的粒度 ...

  9. SQL Server时间粒度系列----第2节日期、周时间粒度详解

    本文目录列表: 1.从MySQL提供的TO_DAYS和FROM_DAYS这对函数说起2.SQL Server日期时间粒度3.SQL Server周有关时间粒度 4.总结语 5.参考清单列表   从My ...

随机推荐

  1. HDFS DataNode 设计实现解析

    前文分析了 NameNode,本文进一步解析 DataNode 的设计和实现要点. 文件存储 DataNode 正如其名是负责存储文件数据的节点.HDFS 中文件的存储方式是将文件按块(block)切 ...

  2. 初识Opserver,StackExchange的监控解决方案

    Opserver是闻名遐迩的网站Stack Overflow的开源监控解决方案,由Stack Exchange发布.它基于.NET框架构建,这在监控工具领域有些与众不同. 旨在为每个受监控系统的健康状 ...

  3. Expert 诊断优化系列------------------锁是个大角色

    前面几篇已经陆续从服务器的几个大块讲述了SQL SERVER数据库的诊断和调优方式.加上本篇可以说已经可以完成常规的问题诊断及优化,本篇就是SQL SERVER中的锁.为了方便阅读给出系列文章的导读链 ...

  4. CSS3魔法堂:CSS3滤镜及Canvas、SVG和IE滤镜替代方案详解

    一.前言    IE特有的滤镜常常作为CSS3各种新特性的降级处理补充,而Adobe转向HTML5后与Chrome合作推出CSS3的Filter特性,因此当前仅Webkit内核的浏览器支持CSS3 F ...

  5. Web高级征程:《大型网站技术架构》读书笔记系列

    一.此书到底何方神圣? <大型网站技术架构:核心原理与案例分析>通过梳理大型网站技术发展历程,剖析大型网站技术架构模式,深入讲述大型互联网架构设计的核心原理,并通过一组典型网站技术架构设计 ...

  6. CSS3 特效分解一

    先声明下,下面的特效不是我发明的,对CSS3的创造力还不够,只是看了别人demo的源码,一点一点分析出来的.整理出的笔记,分享给大家.因为源码是好,但是一头扎进去半天出不来. 首先看个登陆框,如下,相 ...

  7. Linux学习之Exam系统发布

    配置时间:2015年11月27日 配置人:撰写人:微冷的雨   Happy 01.Linux安装图 欢迎页面 桌面 02.Linux命令之文件目录操作 给北大青鸟五道口校区创建三个机房(L4,L5,L ...

  8. Java 8函数编程轻松入门

    函数接口介绍 在Java1.8中,新增了Lambda表达式.在.net3.5(C# 3.0)在原先的匿名方法基础上演变出了Lambda表达式.学过C# Lambda表达式的同学,对于Java的Lamb ...

  9. PHP钩子机制

    什么是钩子 大家想必听过插件,wordpress插件特别多,这个就是用钩子机制实现的. 当代码在运行的过程中,我们预先在运行的几个特殊点里执行一些特殊方法:例如在运行方法(例如Blog::add的ad ...

  10. Android笔记——eclipse快捷键

    查看快捷键定义的地方 Window->Preferences->General->Keys. 编辑相关快捷键 1. [ALT+/] 自动提示方法 此快捷键为用户编辑的好帮手,能为用户 ...