本文目錄列表:1、位運算2、設置日曆數據表節假日標誌3、總結語4、參考清單列表位運算SQL Server支持的按位運算符有三個,分別為:按位與(&)、按位或(|)、按位異或(^)。位運算符用於int、smallint或tinyint數據,目前SQL Server能支持的按位運算的最大整數類型為Int...
本文目錄列表: 1、位運算 2、設置日曆數據表節假日標誌 3、總結語 4、參考清單列表 位運算 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 |
1 IF OBJECT_ID(N'dbo.usp_Calendar_WorkDaySet', 'P') IS NOT NULL 2 BEGIN 3 DROP PROCEDURE dbo.usp_Calendar_WorkDaySet; 4 END 5 GO 6 7 --================================== 8 -- 功能: 設置指定月份的工作日標誌 9 -- 說明: 具體實現闡述 10 -- 作者: XXX 11 -- 創建: yyyy-MM-dd 12 -- 修改: yyyy-MM-dd XXX 修改內容描述 13 --================================== 14 15 CREATE PROCEDURE usp_Calendar_WorkDaySet 16 ( 17 @intMonths INT, -- 指定的日期月數 18 @intWorkDayValueSum INT, -- 指定的日期月數的所有工作日標誌值之和 19 @bitIsUseDefault BIT = 0, -- 是否使用預設設置,1:使用預設設置(周一到周五為工作日,周六和周日非工作日),0:基於指定的日期月數的所有工作日標誌值之和來設置 20 21 -- 方便記錄用戶操作日誌 22 --@chvnUser NVARCHAR(20), -- 指定的用戶 23 --@intUserID INT, -- 指定的用戶ID 24 --@chvUserIP VARCHAR(40), -- 指定的用戶IP 25 --@chvnUserFrom NVARCHAR(30), -- 指定的用戶位置 26 27 @chvnErrMsg NVARCHAR(100) OUTPUT -- 錯誤異常消息字元串 28 ) 29 --$Encode$-- 30 AS 31 BEGIN 32 SET NOCOUNT ON; 33 34 SET @intMonths = dbo.ufn_GetValidDateNum(@intMonths); 35 36 IF @intWorkDayValueSum IS NULL OR @intWorkDayValueSum < 0 37 BEGIN 38 SET @intWorkDayValueSum = 0; 39 END 40 41 SET @chvnErrMsg = N''; 42 43 DECLARE @tintResultValue AS TINYINT; 44 SET @tintResultValue = 1; -- 預設存在錯誤 45 46 DECLARE 47 @dtmNow AS DATETIME, 48 @intDays AS INT; 49 SELECT 50 @dtmNow = GETDATE(), 51 @intDays = dbo.ufn_Days(@dtmNow); 52 53 IF @intMonths < dbo.ufn_Months(@dtmNow) 54 BEGIN 55 SET @chvnErrMsg = N'不能設置小於當前月份的工作日標誌。'; 56 57 RETURN @tintResultValue; 58 END 59 60 DECLARE 61 @WorkDayValueSum AS INT, 62 @DayCount AS INT; 63 SELECT 64 @WorkDayValueSum = 0, 65 @DayCount = 0; 66 67 SELECT 68 @WorkDayValueSum = SUM(POWER(2, [DayOfMonth] - 1)) 69 ,@DayCount = COUNT(1) 70 FROM dbo.Calendar 71 WHERE Months = @intMonths 72 AND [Days] >= @intDays + 1; 73 74 IF @DayCount = 0 OR @WorkDayValueSum = 0 75 BEGIN 76 SET @chvnErrMsg = N'日曆數據表不存在滿足條件的數據。'; 77 78 RETURN @tintResultValue; 79 END 80 81 IF @intWorkDayValueSum = @WorkDayValueSum 82 BEGIN 83 SET @tintResultValue = 0; 84 85 RETURN @tintResultValue; 86 END 87 88 DECLARE @intRowCount AS INT; 89 SELECT @intRowCount = 0; 90 91 BEGIN TRY 92 IF @bitIsUseDefault = 0 93 BEGIN 94 UPDATE Calendar 95 SET WorkdayFlag = POWER(2, [DayOfMonth] - 1) & @intWorkDayValueSum 96 WHERE Months = @intMonths 97 AND [Days] >= @intDays + 1; 98 END 99 ELSE 100 BEGIN 101 UPDATE Calendar 102 SET WorkdayFlag = CASE WHEN dbo.ufn_DayOfWeek(CalendarDate) <= 5 THEN 1 ELSE 0 END 103 WHERE Months = @intMonths 104 AND [Days] >= @intDays + 1; 105 END 106 107 SET @intRowCount = @@ROWCOUNT; 108 109 SET @tintResultValue = 0; 110 END TRY 111 BEGIN CATCH 112 SET @chvnErrMsg = N'設置指定月的工作日標誌發生錯誤。'; 113 114 RETURN @tintResultValue; 115 END CATCH 116 117 RETURN @tintResultValue; 118 END 119 GO 120 121 -- Test Code 122 DECLARE 123 @intMonths AS INT, 124 @intWorkDayValueSum AS INT, 125 @bitIsUseDefault AS BIT, 126 @chvnErrMsg AS NVARCHAR(100), 127 @tintResultVaule AS TINYINT; 128 SELECT 129 @intMonths = 0, -- int 130 @intWorkDayValueSum = 0, -- int 131 @bitIsUseDefault = NULL, -- bit 132 @chvnErrMsg = N'', -- nvarchar(100) 133 @tintResultVaule = 1; -- tinyint 134 135 EXEC @tintResultVaule = dbo.usp_Calendar_WorkDaySet 136 @intMonths = @intMonths, -- int 137 @intWorkDayValueSum = @intWorkDayValueSum, -- int 138 @bitIsUseDefault = @bitIsUseDefault, -- bit 139 @chvnErrMsg = @chvnErrMsg OUTPUT -- nvarchar(100) 140 141 SELECT @chvnErrMsg AS 'Error Message' 142 ,@tintResultVaule AS 'Return Value'; 143 GO 144 145 -- Test Code 146 -- 2016-02月份 147 -- 根據國家節假日獲取的工作日標誌值和以及工作日總數 148 SELECT 149 WorkDayValueSum = SUM(T.WorkDayFlag2 * POWER(2, T.[DayOfMonth] - 1)) 150 ,WorkDayCount = SUM(T.WorkDayFlag2 * 1) 151 FROM ( 152 SELECT 153 Months 154 ,[DayOfMonth] 155 ,WorkDayFlag 156 ,WorkDayFlag2 = CASE 157 WHEN [DayOfMonth] = 6 THEN 1 158 WHEN [DayOfMonth] BETWEEN 7 AND 12 THEN 0 159 WHEN [DayOfMonth] = 14 THEN 1 160 ELSE WorkDayFlag END 161 FROM dbo.Calendar 162 WHERE Months = dbo.ufn_Months('2016-02-01') 163 ) AS T 164 GO 165 166 DECLARE 167 @intMonths AS INT, 168 @intWorkDayValueSum AS INT, 169 @bitIsUseDefault AS BIT, 170 @chvnErrMsg AS NVARCHAR(100), 171 @tintResultVaule AS TINYINT; 172 SELECT 173 @intMonths = dbo.ufn_Months('2016-02-01'), -- int 174 @intWorkDayValueSum = 333963327, -- int 175 @bitIsUseDefault = 0, -- bit 176 @chvnErrMsg = N'', -- nvarchar(100) 177 @tintResultVaule = 1; -- tinyint 178 179 EXEC @tintResultVaule = dbo.usp_Calendar_WorkDaySet 180 @intMonths = @intMonths, -- int 181 @intWorkDayValueSum = @intWorkDayValueSum, -- int 182 @bitIsUseDefault = @bitIsUseDefault, -- bit 183 @chvnErrMsg = @chvnErrMsg OUTPUT -- nvarchar(100) 184 185 SELECT @chvnErrMsg AS 'Error Message' 186 ,@tintResultVaule AS 'Return Value'; 187 GO 188 189
測試以上存儲的功能效果,如下圖: 查詢201602月份的日曆數據,如下圖: 獲取工作日標誌的存儲過程,T-SQL代碼如下:
1 IF OBJECT_ID(N'dbo.usp_Calendar_WeekDayGet', 'P') IS NOT NULL 2 BEGIN 3 DROP PROCEDURE dbo.usp_Calendar_WeekDayGet 4 END 5 GO 6 7 --================================== 8 -- 功能: 獲取滿足條件的日期月數的工作日值和工作日總數 9 -- 說明: 具體實現闡述 10 -- 作者: XXX 11 -- 創建: yyyy-MM-dd 12 -- 修改: yyyy-MM-dd XXX 修改內容描述 13 --================================== 14 CREATE PROCEDURE dbo.usp_Calendar_WeekDayGet 15 ( 16 @intStartMonths INT, -- 指定的開始日期月數 17 @intEndMonths INT -- 指定的結束日期月數 18 ) 19 --$Encode$-- 20 AS 21 BEGIN 22 SET NOCOUNT ON; 23 24 SET @intStartMonths = dbo.ufn_GetValidDateNum(@intStartMonths); 25 SET @intEndMonths = dbo.ufn_GetValidDateNum(@intEndMonths); 26 27 IF @intStartMonths > @intEndMonths 28 BEGIN 29 DECLARE @intTemp AS INT; 30 SET @intTemp = @intStartMonths; 31 SET @intStartMonths = @intEndMonths; 32 SET @intEndMonths = @intTemp; 33 END 34 35 SELECT 36 Months 37 ,WorkDayValueSum = ISNULL(SUM(WorkDayFlag * POWER(2, [DayOfMonth] - 1)), 0) 38 ,WorkDayCount = ISNULL(SUM(WorkDayFlag * 1), 0) 39 FROM dbo.Calendar 40 WHERE Months BETWEEN @intStartMonths AND @intEndMonths 41 GROUP BY Months; 42 END 43 GO 44 45 -- Test Code 46 DECLARE 47 @intStartMonths AS INT, 48 @intEndMonths AS INT, 49 @tintResultValue AS TINYINT; 50 51 SELECT 52 @intStartMonths = dbo.ufn_Months('2015-06-01'), 53 @intEndMonths = dbo.ufn_Months('2016-03-02'), 54 @tintResultValue = 1; -- 預設範圍值 55 56 EXEC @tintResultValue = dbo.usp_Calendar_WeekDayGet 57 @intStartMonths = @intStartMonths, -- int 58 @intEndMonths = @intEndMonths; -- int 59 60 SELECT @tintResultValue AS 'Return Value (1:Have Error,0:No Error)' 61 GO
測試以上存儲的功能效果,如下圖: 總結語 本文提起了SQL Server的按位運算符,重點講解了日曆數據表中的工作日標誌的設置的處理方法,基於集合的處理方法,結合按位與運算符來處理的方法。 參考清單列表 1、https://msdn.microsoft.com/zh-cn/library/ms176122(v=sql.90).aspx 2、https://msdn.microsoft.com/zh-cn/library/ms188725(v=sql.90).aspx 3、https://msdn.microsoft.com/zh-cn/library/ms174965(v=sql.90).aspx 4、https://msdn.microsoft.com/zh-cn/library/ms186714(v=sql.90).aspx 5、https://msdn.microsoft.com/zh-cn/library/ms190277(v=sql.90).aspx