Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

SQL попълва общия брой работни дни на месец минус банковите празници за текущата финансова година

DECLARE @StartDate DATETIME, @EndDate DATETIME

SELECT  @StartDate = '01/04/2011',
        @EndDate = '31/03/2012'
        
CREATE TABLE #Data (FirstDay DATETIME NOT NULL PRIMARY KEY, WorkingDays INT NOT NULL)

;WITH DaysCTE ([Date]) AS
(   SELECT  @StartDate
    UNION ALL
    SELECT  DATEADD(DAY, 1, [Date])
    FROM    DaysCTE
    WHERE   [Date] <= @Enddate
)

INSERT INTO #Data
SELECT  MIN([Date]),
        COUNT(*) [Day]
FROM    DaysCTE
        LEFT JOIN HolidayTable
            ON [Date] BETWEEN HolStart AND HolEnd
WHERE   HolidayTypeID IS NULL
AND     DATENAME(WEEKDAY, [Date]) NOT IN ('Saturday', 'Sunday')
GROUP BY DATEPART(MONTH, [Date]), DATEPART(YEAR, [Date])
OPTION (MAXRECURSION 366)

DECLARE @Date DATETIME
SET @Date = (SELECT MIN(FirstDay) FROM #Data)

SELECT  Period,
        WorkingDays [Days Available (Minus the Holidays)]
FROM    (   SELECT  DATENAME(MONTH, Firstday) [Period],
                    WorkingDays,
                    0 [SortField],
                    FirstDay
            FROM    #Data
            UNION
            SELECT  DATENAME(MONTH, @Date) + ' - ' + DATENAME(MONTH, Firstday),
                    (   SELECT  SUM(WorkingDays)
                        FROM    #Data b
                        WHERE   b.FirstDay <= a.FirstDay
                    ) [WorkingDays],
                    1 [SortField],
                    FirstDay 
            FROM    #Data a
            WHERE   FirstDay > @Date
        ) data
ORDER BY SortField, FirstDay

DROP TABLE #Data

Ако правите това повече от 1 година, ще трябва да промените реда:

OPTION (MAXRECURSION 366)

В противен случай ще получите грешка - Числото трябва да е по-голямо от броя на дните, които заявявате.

РЕДАКТИРАНЕ

Току-що попаднах на този мой стар отговор и наистина не ми харесва, има толкова много неща, които сега смятам за лоша практика, така че ще коригирам всички проблеми:

  1. Не съм прекратил изявления с правилно точка и запетая
  2. Използван е рекурсивен CTE за генериране на списък с дати
  3. Не е включен списъкът с колони за вмъкване
  4. Използва се DATENAME за елиминиране на почивните дни, което е специфично за езика, много по-добре изрично да се зададе DATEFIRST и използвайте DATEPART
  5. Използван е LEFT JOIN/IS NULL вместо NOT EXISTS за премахване на записи от празничната трапеза. В SQL Server LEFT JOIN/IS NULL е по-малко ефективен от NOT EXISTS

Всичко това са незначителни неща, но те са неща, които бих критикувал (поне в главата си, ако не на глас), когато преглеждам нечие друго запитване, така че наистина не мога да не коригирам собствената си работа! Пренаписването на заявката ще даде.

SET DATEFIRST 1;

DECLARE @StartDate DATETIME = '20110401',
        @EndDate DATETIME = '20120331';

CREATE TABLE #Data (FirstDay DATETIME NOT NULL PRIMARY KEY, WorkingDays INT NOT NULL);

WITH DaysCTE ([Date]) AS
(   SELECT  TOP (DATEDIFF(DAY, @StartDate, @EndDate) + 1)
            DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, @StartDate)
    FROM    sys.all_objects a
)
INSERT INTO #Data (FirstDay, WorkingDays)
SELECT  FirstDay =  MIN([Date]),
        WorkingDays = COUNT(*) 
FROM    DaysCTE d
WHERE   DATEPART(WEEKDAY, [Date]) NOT IN (6, 7)
AND     NOT EXISTS
        (   SELECT  1
            FROM    dbo.HolidayTable ht
            WHERE   d.[Date] BETWEEN ht.HolStart AND ht.HolEnd
        )
GROUP BY DATEPART(MONTH, [Date]), DATEPART(YEAR, [Date]);

DECLARE @Date DATETIME = (SELECT MIN(FirstDay) FROM #Data);

SELECT  Period,
        [Days Available (Minus the Holidays)] = WorkingDays 
FROM    (   SELECT  DATENAME(MONTH, Firstday) [Period],
                    WorkingDays,
                    0 [SortField],
                    FirstDay
            FROM    #Data
            UNION
            SELECT  DATENAME(MONTH, @Date) + ' - ' + DATENAME(MONTH, Firstday),
                    (   SELECT  SUM(WorkingDays)
                        FROM    #Data b
                        WHERE   b.FirstDay <= a.FirstDay
                    ) [WorkingDays],
                    1 [SortField],
                    FirstDay 
            FROM    #Data a
            WHERE   FirstDay > @Date
        ) data
ORDER BY SortField, FirstDay;

DROP TABLE #Data;

Като последна точка, тази заявка става много по-проста с календарна таблица която съхранява всички дати и има флагове за работни дни, празници и т.н., вместо да използва празнична таблица, която съхранява само празници.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Как да актуализирате две таблици в един израз в SQL Server 2005?

  2. SQL присъединявания срещу SQL подзаявки (производителност)?

  3. Временната таблица на Sql Server изчезва

  4. Най-лесният начин да разграничите две схеми на таблици в SQL Server 2008?

  5. Как да генерирате скриптове за всички тригери в базата данни с помощта на Microsoft SQL Server Management Studio