Row_Number上的条件计数

时间:2016-11-22 14:56:48

标签: sql sql-server database tsql

我有一个查询,根据存储所有公共假日的表计算一个月内的工作日数。

当前输出将显示所有工作日,不包括公众假期以及周六和周日,我希望显示每月的每一天,但不会在公众假期或周六或周日递增。

有没有办法有条件地增加行号?

查询如下:

DECLARE @startnum INT=0
DECLARE @endnum INT=365;

WITH gen AS 
(
    SELECT @startnum AS num
    UNION ALL
    SELECT num + 1 
    FROM gen 
    WHERE num + 1 <= @endnum   
)
, holidays AS
( 
    SELECT CONVERT(DATE, transdate)  AS HolidayDate 
    FROM WORKCALENDER w  
    WHERE w.CALENDARID = 'PubHoliday'
)
, allDays AS
( 
    SELECT DATEADD( d, num, CONVERT( DATE, '1 Jan 2016' ) ) AS DateOfYear
    , DATENAME( dw, DATEADD( d, num, CONVERT( DATE, '1 Jan 2016' ))) AS [dayOfWeek]
    FROM gen 
)
select number = ROW_NUMBER() OVER ( ORDER BY DateOfYear  )     
, *
from allDays
    LEFT OUTER JOIN holidays
        ON allDays.DateOfYear = holidays.HolidayDate
WHERE holidays.HolidayDate IS NULL
    AND allDays.dayOfWeek NOT IN ( 'Saturday', 'Sunday')
    AND DateOfYear >= CONVERT( DATE, '1 ' + DATENAME( MONTH, GETDATE() ) + ' 2016' )
    AND DateOfYear < CONVERT( DATE, '1 ' + DATENAME( MONTH, DATEADD( month, 1, GETDATE()) ) + ' 2016' )
option (maxrecursion 10000)

4 个答案:

答案 0 :(得分:0)

种伪代码

select date, row_number() over (order by date) as num  
from  ( select date 
        from allDates 
        where month = x and weekday  
        exept 
        select date 
        from holidays 
        where month is x 
      ) as t
union all
select date, null
from holidays 
where month is x  
order by date 

答案 1 :(得分:0)

您可以使用窗口总和,查看WorkdaySequenceInMonth的输出是如何组合的。

DECLARE   @startDate  DATE = '20160101'
        , @numDays    INT =  365
        , @num        INT  =  0;

DECLARE @Holidays TABLE (Holiday DATE);

INSERT INTO @Holidays(Holiday)
     VALUES ('20160101')
          , ('20160115')
          , ('20160714');

WITH nums AS
(
    SELECT row_number() OVER (ORDER BY object_id) - 1 as num
    FROM sys.columns
),
dateRange as
(
  SELECT 
      DATEADD(DAY, num, @startDate) AS Dt
    , num
  FROM nums
  WHERE num < @numDays
),
Parts AS
(
    SELECT
         R.Dt as [Date]
       , Year(R.Dt) as [Year]
       , Month(R.Dt) as [Month]
       , Day(R.Dt) as [Day]
       , Datename(weekday, R.Dt) as [Weekday]
       , CASE WHEN H.Holiday IS NOT NULL 
                   OR Datename(weekday, R.Dt) IN ('Saturday', 'Sunday')
              THEN 0
              ELSE 1
              END AS IsWorkday

    FROM dateRange R    
    LEFT JOIN @Holidays H ON R.Dt = H.Holiday
)
-- 
select 
    *   
    , sum(IsWorkday) over (PARTITION BY [Year],[month]
                           ORDER BY [Day]
                           ROWS UNBOUNDED PRECEDING) as WorkdaySequenceInMonth      
from Parts
order by [Year], [Month]

答案 2 :(得分:0)

您好您可以尝试此查询,初始部分是数据生成,也许您不会需要它。 然后我生成一个临时表,其中包含@StartYear,@ EndYear中设置的时间段的所有日期 然后只是简单的查询返回数据

            -- generate holidays table
            select holiday 
            into #tempHolidays
            from
            (
            select '20160101' as holiday
            union all 
            select '20160201' as holiday
            union all 
            select '20160205' as holiday
            union all 
            select '20160301' as holiday
            union all 
            select '20160309' as holiday
            union all 
            select '20160315' as holiday
            )  as t


            create table #tempCalendar (Date_temp date)

            select * from 
            #tempHolidays

            declare @startYear int , @endYear int,  @i int,  @dateStart datetime , @dateEnd datetime,  @date datetime, @i = 0
            Select  @startYear = '2016'
                    ,@endYear = '2016'
                    ,@dateStart = (Select cast( (cast(@startYear as varchar(4)) +'0101') as datetime))
                    ,@dateEnd = (Select cast( (cast(@startYear as varchar(4)) +'1231') as datetime))
                    ,@date = @dateStart


                    --Insert dates of the period of time
            while (@date <> @dateEnd)
            begin

            insert into #tempCalendar
            Select @date
            set @date = (select DATEADD(dd,1,@date))

            end

            -- Retrive Date list
            Select Date_temp 
            from #tempCalendar
            where Date_temp not in (Select holiday from #tempHolidays)
            and datename(weekday,Date_temp) not in ('Saturday','Sunday')

            --REtrieve sum of working days per month
            select DATEPART(year,Date_temp) as year
                    ,DATEPART(month,Date_temp) as Month
                    ,Count(*) as CountOfWorkingDays

            from #tempCalendar
            where Date_temp not in (Select holiday from #tempHolidays)
            and datename(weekday,Date_temp) not in ('Saturday','Sunday')
            Group by DATEPART(year,Date_temp)
                    ,DATEPART(month,Date_temp) 

您应该为您的假期表更改#tempHolidays,并使用@StarYear和@EndYear作为您的时间段。

答案 3 :(得分:0)

这是一个简单的演示,演示如何使用partition by子句在非假期的排序中保持连续性

IF OBJECT_ID('tempdb.dbo.#dates') IS NOT null
    DROP TABLE #dates;
CREATE TABLE #dates (d DATE);

IF OBJECT_ID('tempdb.dbo.#holidays') IS NOT null
    DROP TABLE #holidays;
CREATE TABLE #holidays (d DATE);

INSERT INTO [#holidays]
        ( [d] )
VALUES
    ('2016-12-25'),
    ('2017-12-25'),
    ('2018-12-25');

INSERT INTO [#dates]
        ( [d] )
SELECT TOP 1000 DATEADD(DAY, n, '2015-12-31')
FROM [Util].dbo.[Numbers] AS [n];


WITH holidays AS (
    SELECT d.*, CASE WHEN h.d IS NULL THEN 0 ELSE 1 END AS [IsHoliday]
    FROM [#dates] AS [d]
    LEFT JOIN [#holidays] AS [h]
        ON [d].[d] = [h].[d]
)
SELECT d, ROW_NUMBER() OVER (PARTITION BY [holidays].[IsHoliday] ORDER BY d)
FROM [holidays]
ORDER BY d;

请原谅我的标记只有圣诞节作为假期!