SQL Server - 设置加拿大商务假期日期

时间:2017-05-18 14:54:45

标签: sql sql-server

注意:这是加拿大假期:

我需要填写加拿大商务假期的表格 我有以下查询来设置商务假日日期,并且还考虑假日在下周末假期到下一个工作日的移动。即如果假日发生在星期日,那么星期一就成了假日。我还用工作日标识了一个标志,并添加了当天的英文描述。

我添加了一些计算列以使作业更容易,这些列在创建完所有数据后再次删除。我不能放弃桌子;该表可能部分填充,我无法覆盖现有数据。这是我问题的根源。因为' Were'查询非常慢。

我用来填充表的查询是:

   DECLARE @StartDate DATE = '20000101'
     ,@EndDate DATE = '21631231';

   WITH    N10(n)
           AS (SELECT    1
              FROM      ( VALUES ( 0), ( 1), ( 2), ( 3), ( 4), ( 5), ( 6), ( 7), ( 8), ( 9) ) v (n)),
         N100(n)
           AS (SELECT    1
              FROM      N10
                     ,N10 n),
         N10000(n)
           AS (SELECT    1
              FROM      N100
                     ,N100 n),
         N100000(n)
           AS (SELECT    1
              FROM      N10
                     ,N10000 n),
         N AS (SELECT TOP (DATEDIFF(DAY,@StartDate,@EndDate) + 1)
                      n = ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1
              FROM      N100000)
      INSERT  INTO [dbo].[BusinessCalendarDetails2] (BusinessDate,CreatedById,CreatedTime,BusinessCalendarId,IsWeekday,
                                            IsHoliday)
      (SELECT InsertDate,1,CURRENT_TIMESTAMP,1,1,0
       FROM   N
       CROSS APPLY (SELECT DATEADD (DAY,n,@StartDate)) d (InsertDate)
       WHERE  NOT EXISTS ( SELECT 1 FROM  [BusinessCalendarDetails2]
                       WHERE  [BusinessDate] = InsertDate ));

这样可行,但问题是' WHERE'确定表中已存在的日期是否严重减慢(14分钟)。所以我想知道某人是否有更快的解决方案来识别已有的日期?

提前感谢您的协助。

以下是整个脚本,效果相当不错;其他人可能会发现它很有用。

    -- Populate table with business holidays 

    -- If the table is missing add it
    IF OBJECT_ID('BusinessCalendarDetails2') IS NULL
        BEGIN
            CREATE TABLE dbo.BusinessCalendarDetails2 (
                Id BIGINT IDENTITY(1,1)
                          NOT NULL
               ,BusinessDate [DATE] NOT NULL
               ,Day AS DAY(BusinessDate)
               ,Week AS DATEPART(WEEK,BusinessDate)
               ,[Month] AS MONTH(BusinessDate)
               ,Quarter AS DATEPART(QUARTER,BusinessDate)
               ,[Year] AS YEAR(BusinessDate)
               ,DayOfWeek AS DATEPART(WEEKDAY,BusinessDate)
               ,CreatedById BIGINT NOT NULL
               ,CreatedTime DATETIMEOFFSET(7) NOT NULL
               ,UpdatedById BIGINT NULL
               ,UpdatedTime DATETIMEOFFSET(7) NULL
               ,BusinessCalendarId BIGINT NOT NULL
               ,RowVersion TIMESTAMP NOT NULL
               ,IsWeekday BIT NOT NULL
               ,IsHoliday BIT NOT NULL
               ,Description NVARCHAR(50)
               ,PRIMARY KEY CLUSTERED (Id ASC)
                    WITH (PAD_INDEX = OFF,STATISTICS_NORECOMPUTE = OFF,IGNORE_DUP_KEY = OFF,ALLOW_ROW_LOCKS = ON,
                          ALLOW_PAGE_LOCKS = ON) ON [PRIMARY])
            ON  [PRIMARY];
        END;

    -- Check if this col exists, 
    IF COL_LENGTH('dbo.BusinessCalendarDetails2','Day') IS NULL
        BEGIN
            ALTER TABLE dbo.BusinessCalendarDetails2
            ADD   -- Add calculated fields
            Day AS DAY(BusinessDate), Week AS DATEPART(WEEK, BusinessDate), [Month] AS MONTH(BusinessDate), Quarter AS DATEPART(QUARTER, BusinessDate), [Year] AS YEAR(BusinessDate), DayOfWeek AS DATEPART(WEEKDAY, BusinessDate);
        END;
    GO

    -- Date range to populate
    DECLARE @StartDate DATE= '20000101' ,@EndDate DATE= '21631231';
    WITH    N10(n)
              AS (SELECT    1
                  FROM      ( VALUES ( 0 ), ( 1 ), ( 2 ), ( 3 ), ( 4 ), ( 5 ), ( 6 ), ( 7 ), ( 8 ), ( 9 ) ) AS v (n)),
            N100(n)
              AS (SELECT    1
                  FROM      N10
                           ,N10 AS n),
            N10000(n)
              AS (SELECT    1
                  FROM      N100
                           ,N100 AS n),
            N100000(n)
              AS (SELECT    1
                  FROM      N10
                           ,N10000 AS n),
            N AS (SELECT TOP (DATEDIFF(DAY,@StartDate,@EndDate) + 1)
                            n = ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1
                  FROM      N100000)
        INSERT  INTO dbo.BusinessCalendarDetails2 (BusinessDate,CreatedById,CreatedTime,BusinessCalendarId,IsWeekday,
                                                   IsHoliday)
        (SELECT InsertDate,1,CURRENT_TIMESTAMP,1,1,0
         FROM   N
         CROSS APPLY (SELECT DATEADD (DAY,n,@StartDate)) AS d (InsertDate)
         WHERE  NOT EXISTS ( SELECT 1
                             FROM   BusinessCalendarDetails2
                             WHERE  BusinessDate = InsertDate ));

    -- Set Descriptions
    UPDATE  dbo.BusinessCalendarDetails2
    SET     Description = DATENAME(dw,BusinessDate)
    FROM    dbo.BusinessCalendarDetails2;

    -- Set weekdays
    UPDATE  dbo.BusinessCalendarDetails2
    SET     IsWeekday = 0,Description = Description + ' Weekend'
    FROM    dbo.BusinessCalendarDetails2 AS c1
    WHERE   DATEPART(WEEKDAY,c1.BusinessDate) IN (1,7);

    -- New Years Day
    UPDATE  dbo.BusinessCalendarDetails2
    SET     IsHoliday = 1,Description = Description + ' New Years Day'
    FROM    dbo.BusinessCalendarDetails2
    WHERE   BusinessCalendarDetails2.[Month] = 1
            AND (SELECT CASE (@@DATEFIRST + DATEPART(DW,CAST(DATEPART(YEAR,BusinessDate) AS VARCHAR) + '-01-01')) % 7
                          WHEN 0 THEN 3 -- SAT
                          WHEN 1 THEN 2 -- Sunday
                          ELSE 1
                        END) = BusinessCalendarDetails2.Day;

    -- Family Day Day -- 3rd Monday in February 
    UPDATE  dbo.BusinessCalendarDetails2
    SET     IsHoliday = 1,Description = Description + ' Family Day'
    FROM    dbo.BusinessCalendarDetails2 AS c1
    WHERE   c1.[Month] = 2
            AND c1.DayOfWeek = 2
            AND c1.Day BETWEEN 15 AND 21;

    -- Canada Day
    UPDATE  dbo.BusinessCalendarDetails2
    SET     IsHoliday = 1,Description = Description + ' Canada Day'
    FROM    dbo.BusinessCalendarDetails2
    WHERE   BusinessCalendarDetails2.[Month] = 7
            AND (SELECT CASE (@@DATEFIRST + DATEPART(DW,CAST(DATEPART(YEAR,BusinessDate) AS VARCHAR) + '-07-01')) % 7
                          WHEN 0 THEN 3 -- SAT
                          WHEN 1 THEN 2 -- Sunday
                          ELSE 1
                        END) = BusinessCalendarDetails2.Day;

    -- Civic Holiday
    UPDATE  dbo.BusinessCalendarDetails2
    SET     IsHoliday = 1,Description = Description + ' Civic Holiday'
    FROM    dbo.BusinessCalendarDetails2 AS c1
    WHERE   c1.[Month] = 8
            AND c1.DayOfWeek = 2
            AND c1.Day BETWEEN 1 AND 7;


    -- Good Friday
    UPDATE  BusinessCalendarDetails2
    SET     IsHoliday = 1,Description = Description + ' Good Friday'
    FROM    dbo.BusinessCalendarDetails2 AS dimdate
    CROSS APPLY (SELECT dimdate.Year AS y) _y
    CROSS APPLY (SELECT y / 100 AS c,y - 19 * y / 19 AS n) _nc
    CROSS APPLY (SELECT (c - 17) / 25 AS k) _k
    CROSS APPLY (SELECT c - c / 4 - (c - k) / 3 + 19 * n + 15 AS i1) _i1
    CROSS APPLY (SELECT i1 - 30 * i1 / 30 AS i2) _i2
    CROSS APPLY (SELECT i2 - (i2 / 28) * (1 - (i2 / 28) * 29 / (i2 + 1) * (21 - n) / 11) AS i) _i
    CROSS APPLY (SELECT y + y / 4 + i + 2 - c + c / 4 AS j1) _j1
    CROSS APPLY (SELECT j1 - 7 * j1 / 7 AS j) _j
    CROSS APPLY (SELECT i - j AS el) _el
    CROSS APPLY (SELECT 3 + (el + 40) / 44 AS m) _m
    CROSS APPLY (SELECT el + 28 - 31 * m / 4 AS d) _d
    CROSS APPLY (SELECT DATEFROMPARTS (y,m,d) AS EasterSunday) _Easter
    WHERE   dimdate.BusinessDate = DATEADD(DAY,-2,EasterSunday);


    -- Easter Sunday
    UPDATE  BusinessCalendarDetails2
    SET     IsHoliday = 0,Description = Description + ' Easter Sunday'
    FROM    dbo.BusinessCalendarDetails2 AS dimdate
    CROSS APPLY (SELECT dimdate.Year AS y) _y
    CROSS APPLY (SELECT y / 100 AS c,y - 19 * y / 19 AS n) _nc
    CROSS APPLY (SELECT (c - 17) / 25 AS k) _k
    CROSS APPLY (SELECT c - c / 4 - (c - k) / 3 + 19 * n + 15 AS i1) _i1
    CROSS APPLY (SELECT i1 - 30 * i1 / 30 AS i2) _i2
    CROSS APPLY (SELECT i2 - (i2 / 28) * (1 - (i2 / 28) * 29 / (i2 + 1) * (21 - n) / 11) AS i) _i
    CROSS APPLY (SELECT y + y / 4 + i + 2 - c + c / 4 AS j1) _j1
    CROSS APPLY (SELECT j1 - 7 * j1 / 7 AS j) _j
    CROSS APPLY (SELECT i - j AS el) _el
    CROSS APPLY (SELECT 3 + (el + 40) / 44 AS m) _m
    CROSS APPLY (SELECT el + 28 - 31 * m / 4 AS d) _d
    CROSS APPLY (SELECT DATEFROMPARTS (y,m,d) AS EasterSunday) _Easter
    WHERE   dimdate.BusinessDate = EasterSunday;


    -- Labour Day  --  first Monday of September  
    UPDATE  dbo.BusinessCalendarDetails2
    SET     IsHoliday = 1,Description = Description + ' Labour Day'
    FROM    dbo.BusinessCalendarDetails2 AS c1
    WHERE   c1.[Month] = 9
            AND c1.DayOfWeek = 2
            AND c1.Day BETWEEN 1 AND 7;

    -- Set Thanksgiving
    UPDATE  dbo.BusinessCalendarDetails2
    SET     IsHoliday = 1,Description = Description + ' Thanksgiving'
    FROM    dbo.BusinessCalendarDetails2 AS c1
    WHERE   c1.[Month] = 10
            AND c1.DayOfWeek = 2
            AND c1.Day BETWEEN 8 AND 14;

    ---- Christmas
    UPDATE  dbo.BusinessCalendarDetails2
    SET     IsHoliday = 1,Description = Description + ' Christmas Holidays'
    FROM    dbo.BusinessCalendarDetails2
    WHERE   [Month] = 12
            AND Day BETWEEN 26 AND 28
            AND (DATEPART(WEEKDAY,CAST(DATEPART(YEAR,BusinessDate) AS VARCHAR) + '-12-25') IN (1,7)     -- Is Christmas on a weekend this year
                 OR DATEPART(WEEKDAY,CAST(DATEPART(YEAR,BusinessDate) AS VARCHAR) + '-12-26') IN (1,7))     -- Is Boxingday on a weekend this year
            AND DATEPART(dw,BusinessDate) <> 4 -- NOT ON Wednesday!
            AND DATEPART(DAY,(SELECT    CASE (@@DATEFIRST + DATEPART(dw,BusinessDate)) % 7
                                          WHEN 0 THEN DATEADD(DAY,2,BusinessDate) -- Saturday
                                          WHEN 1 THEN DATEADD(DAY,1,BusinessDate) -- Sunday
                                          ELSE BusinessDate
                                        END AS Weekday)) BETWEEN 26
                                                         AND     28
            OR [Month] = 12    -- Is christmas a week day?
            AND Day BETWEEN 25 AND 26
            AND IsWeekday = 1;
    GO

    -- Return table to orig state
    ALTER TABLE dbo.BusinessCalendarDetails2 DROP COLUMN Day, Week, [Month], Quarter, [Year], DayOfWeek;

2 个答案:

答案 0 :(得分:1)

如果BusinessDate上没有索引且表格足够大,那肯定会减慢速度。如果它已编制索引,您可能需要重建/重新组织它,甚至只需重做统计信息。

否则,根据您的磁盘,将不同的BusinessDate列值插入到带有索引的临时表中并将其与之进行比较可能会更快。

可能有很多事情。如果您还没有,那么您应该查看查询的ACTUAL执行计划和统计信息,以便更好地了解导致问题的原因。

答案 1 :(得分:0)

这是使用Temp表运行得更快的最终答案

 -- Populate table with business holidays 

    CREATE TABLE #BusCalDet (
        Id BIGINT IDENTITY(1,1)
                  NOT NULL
       ,BusinessDate [DATE] NOT NULL
       ,Day AS DAY(BusinessDate)
       ,Week AS DATEPART(WEEK,BusinessDate)
       ,[Month] AS MONTH(BusinessDate)
       ,Quarter AS DATEPART(QUARTER,BusinessDate)
       ,[Year] AS YEAR(BusinessDate)
       ,DayOfWeek AS DATEPART(WEEKDAY,BusinessDate)
       ,CreatedById BIGINT NOT NULL
       ,CreatedTime DATETIMEOFFSET(7) NOT NULL
       ,UpdatedById BIGINT NULL
       ,UpdatedTime DATETIMEOFFSET(7) NULL
       ,BusinessCalendarId BIGINT NOT NULL
       ,RowVersion TIMESTAMP NOT NULL
       ,IsWeekday BIT NOT NULL
       ,IsHoliday BIT NOT NULL
       ,Description NVARCHAR(50)
       ,PRIMARY KEY CLUSTERED (Id ASC)
            WITH (PAD_INDEX = OFF,STATISTICS_NORECOMPUTE = OFF,IGNORE_DUP_KEY = OFF,ALLOW_ROW_LOCKS = ON,
                  ALLOW_PAGE_LOCKS = ON) ON [PRIMARY])
    ON  [PRIMARY];


    -- Date range to populate
    DECLARE @StartDate DATE= '20000101'
       ,@EndDate DATE= '21631231';
    WITH    N10(n)
              AS (SELECT    1
                  FROM      ( VALUES ( 0 ), ( 1 ), ( 2 ), ( 3 ), ( 4 ), ( 5 ), ( 6 ), ( 7 ), ( 8 ), ( 9 ) ) AS v (n)),
            N100(n)
              AS (SELECT    1
                  FROM      N10
                           ,N10 AS n),
            N10000(n)
              AS (SELECT    1
                  FROM      N100
                           ,N100 AS n),
            N100000(n)
              AS (SELECT    1
                  FROM      N10
                           ,N10000 AS n),
            N AS (SELECT TOP (DATEDIFF(DAY,@StartDate,@EndDate) + 1)
                            n = ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1
                  FROM      N100000)
        INSERT  INTO #BusCalDet (BusinessDate,CreatedById,CreatedTime,BusinessCalendarId,IsWeekday,IsHoliday)
        SELECT  InsertDate,1,CURRENT_TIMESTAMP,1,1,0
        FROM    N
        CROSS APPLY (SELECT DATEADD (DAY,n,@StartDate)) AS d (InsertDate);


    -- Set Descriptions
    UPDATE  #BusCalDet
    SET     Description = DATENAME(dw,BusinessDate)
    FROM    #BusCalDet;

    -- Set weekdays
    UPDATE  #BusCalDet
    SET     IsWeekday = 0,Description = Description + ' Weekend'
    FROM    #BusCalDet AS c1
    WHERE   DATEPART(WEEKDAY,c1.BusinessDate) IN (1,7);

    -- New Years Day
    UPDATE  #BusCalDet
    SET     IsHoliday = 1,Description = Description + ' New Years Day'
    FROM    #BusCalDet
    WHERE   #BusCalDet.[Month] = 1
            AND (SELECT CASE (@@DATEFIRST + DATEPART(DW,CAST(DATEPART(YEAR,BusinessDate) AS VARCHAR) + '-01-01'))
                          % 7
                          WHEN 0 THEN 3 -- SAT
                          WHEN 1 THEN 2 -- Sunday
                          ELSE 1
                        END) = #BusCalDet.Day;

    -- Family Day Day -- 3rd Monday in February 
    UPDATE  #BusCalDet
    SET     IsHoliday = 1,Description = Description + ' Family Day'
    FROM    #BusCalDet AS c1
    WHERE   c1.[Month] = 2
            AND c1.DayOfWeek = 2
            AND c1.Day BETWEEN 15 AND 21;

    -- Canada Day
    UPDATE  #BusCalDet
    SET     IsHoliday = 1,Description = Description + ' Canada Day'
    FROM    #BusCalDet
    WHERE   #BusCalDet.[Month] = 7
            AND (SELECT CASE (@@DATEFIRST + DATEPART(DW,CAST(DATEPART(YEAR,BusinessDate) AS VARCHAR) + '-07-01'))
                          % 7
                          WHEN 0 THEN 3 -- SAT
                          WHEN 1 THEN 2 -- Sunday
                          ELSE 1
                        END) = #BusCalDet.Day;

    -- Civic Holiday
    UPDATE  #BusCalDet
    SET     IsHoliday = 1,Description = Description + ' Civic Holiday'
    FROM    #BusCalDet AS c1
    WHERE   c1.[Month] = 8
            AND c1.DayOfWeek = 2
            AND c1.Day BETWEEN 1 AND 7;


    -- Good Friday
    UPDATE  #BusCalDet
    SET     IsHoliday = 1,Description = Description + ' Good Friday'
    FROM    #BusCalDet AS dimdate
    CROSS APPLY (SELECT dimdate.Year AS y) _y
    CROSS APPLY (SELECT y / 100 AS c,y - 19 * y / 19 AS n) _nc
    CROSS APPLY (SELECT (c - 17) / 25 AS k) _k
    CROSS APPLY (SELECT c - c / 4 - (c - k) / 3 + 19 * n + 15 AS i1) _i1
    CROSS APPLY (SELECT i1 - 30 * i1 / 30 AS i2) _i2
    CROSS APPLY (SELECT i2 - (i2 / 28) * (1 - (i2 / 28) * 29 / (i2 + 1) * (21 - n) / 11) AS i) _i
    CROSS APPLY (SELECT y + y / 4 + i + 2 - c + c / 4 AS j1) _j1
    CROSS APPLY (SELECT j1 - 7 * j1 / 7 AS j) _j
    CROSS APPLY (SELECT i - j AS el) _el
    CROSS APPLY (SELECT 3 + (el + 40) / 44 AS m) _m
    CROSS APPLY (SELECT el + 28 - 31 * m / 4 AS d) _d
    CROSS APPLY (SELECT DATEFROMPARTS (y,m,d) AS EasterSunday) _Easter
    WHERE   dimdate.BusinessDate = DATEADD(DAY,-2,EasterSunday);


    -- Easter Sunday
    UPDATE  #BusCalDet
    SET     IsHoliday = 0,Description = Description + ' Easter Sunday'
    FROM    #BusCalDet AS dimdate
    CROSS APPLY (SELECT dimdate.Year AS y) _y
    CROSS APPLY (SELECT y / 100 AS c,y - 19 * y / 19 AS n) _nc
    CROSS APPLY (SELECT (c - 17) / 25 AS k) _k
    CROSS APPLY (SELECT c - c / 4 - (c - k) / 3 + 19 * n + 15 AS i1) _i1
    CROSS APPLY (SELECT i1 - 30 * i1 / 30 AS i2) _i2
    CROSS APPLY (SELECT i2 - (i2 / 28) * (1 - (i2 / 28) * 29 / (i2 + 1) * (21 - n) / 11) AS i) _i
    CROSS APPLY (SELECT y + y / 4 + i + 2 - c + c / 4 AS j1) _j1
    CROSS APPLY (SELECT j1 - 7 * j1 / 7 AS j) _j
    CROSS APPLY (SELECT i - j AS el) _el
    CROSS APPLY (SELECT 3 + (el + 40) / 44 AS m) _m
    CROSS APPLY (SELECT el + 28 - 31 * m / 4 AS d) _d
    CROSS APPLY (SELECT DATEFROMPARTS (y,m,d) AS EasterSunday) _Easter
    WHERE   dimdate.BusinessDate = EasterSunday;


    -- Labour Day  --  first Monday of September  
    UPDATE  #BusCalDet
    SET     IsHoliday = 1,Description = Description + ' Labour Day'
    FROM    #BusCalDet AS c1
    WHERE   c1.[Month] = 9
            AND c1.DayOfWeek = 2
            AND c1.Day BETWEEN 1 AND 7;

    -- Set Thanksgiving
    UPDATE  #BusCalDet
    SET     IsHoliday = 1,Description = Description + ' Thanksgiving'
    FROM    #BusCalDet AS c1
    WHERE   c1.[Month] = 10
            AND c1.DayOfWeek = 2
            AND c1.Day BETWEEN 8 AND 14;

    ---- Christmas
    UPDATE  #BusCalDet
    SET     IsHoliday = 1,Description = Description + ' Christmas Holidays'
    FROM    #BusCalDet
    WHERE   [Month] = 12
            AND Day BETWEEN 26 AND 28
            AND (DATEPART(WEEKDAY,CAST(DATEPART(YEAR,BusinessDate) AS VARCHAR) + '-12-25') IN (1,7)     -- Is Christmas on a weekend this year
                 OR DATEPART(WEEKDAY,CAST(DATEPART(YEAR,BusinessDate) AS VARCHAR) + '-12-26') IN (1,7))     -- Is Boxingday on a weekend this year
            AND DATEPART(dw,BusinessDate) <> 4 -- NOT ON Wednesday!
            AND DATEPART(DAY,(SELECT    CASE (@@DATEFIRST + DATEPART(dw,BusinessDate)) % 7
                                          WHEN 0 THEN DATEADD(DAY,2,BusinessDate) -- Saturday
                                          WHEN 1 THEN DATEADD(DAY,1,BusinessDate) -- Sunday
                                          ELSE BusinessDate
                                        END AS Weekday)) BETWEEN 26
                                                         AND     28
            OR [Month] = 12    -- Is christmas a week day?
            AND Day BETWEEN 25 AND 26
            AND IsWeekday = 1;

    -- Match orig table Schema
    ALTER TABLE #BusCalDet DROP COLUMN Day, Week, [Month], Quarter, [Year], DayOfWeek;

      -- Insert in to the main table'
    INSERT  INTO dbo.BusinessCalendarDetails (BusinessDate,CreatedById,CreatedTime,BusinessCalendarId,IsWeekday,
                                              IsHoliday,[Description])
        -- Find Rows that are missing
    SELECT  DT.BusinessDate,DT.CreatedById,DT.CreatedTime,DT.BusinessCalendarId,DT.IsWeekday,DT.IsHoliday,
            DT.[Description]
    FROM    #BusCalDet DT
    LEFT JOIN BusinessCalendarDetails ON BusinessCalendarDetails.BusinessDate = DT.BusinessDate
    WHERE   BusinessCalendarDetails.Id IS NULL
    ORDER BY DT.BusinessDate;-- id