Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

SQL füllt die Gesamtarbeitstage pro Monat abzüglich Bankfeiertage für das laufende Geschäftsjahr aus

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

Wenn Sie dies länger als 1 Jahr tun, müssen Sie die Zeile ändern:

OPTION (MAXRECURSION 366)

Andernfalls erhalten Sie eine Fehlermeldung - Die Zahl muss höher sein als die Anzahl der Tage, die Sie abfragen.

BEARBEITEN

Ich bin gerade auf diese alte Antwort von mir gestoßen und mag sie wirklich nicht, es gibt so viele Dinge, die ich jetzt als schlechte Praxis betrachte, also werde ich alle Probleme korrigieren:

  1. Ich habe keine Anweisungen mit beendet ein Semikolon richtig
  2. Benutzte einen rekursiven CTE, um eine Liste von Daten zu generieren
  3. Die Spaltenliste für eine Einfügung wurde nicht eingefügt
  4. DATENAME verwendet, um Wochenenden zu eliminieren, was sprachspezifisch ist, viel besser, um explizit DATEFIRST zu setzen und verwenden Sie DATEPART
  5. Verwendet LEFT JOIN/IS NULL statt NOT EXISTS Einträge aus der Feiertagstabelle zu entfernen. In SQL Server ist LEFT JOIN/IS NULL weniger effizient als NOT EXISTS

Dies sind alles Kleinigkeiten, aber es sind Dinge, die ich (zumindest in meinem Kopf, wenn nicht sogar laut) kritisieren würde, wenn ich die Anfrage eines anderen überprüfe, also kann ich meine eigene Arbeit nicht wirklich korrigieren! Umschreiben der Abfrage würde ergeben.

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;

Als letzter Punkt wird diese Abfrage viel einfacher mit einem Kalendertabelle die alle Daten speichert und Flags für Arbeitstage, Feiertage usw. hat, anstatt eine Feiertagstabelle zu verwenden, die nur Feiertage speichert.