sql >> Databáze >  >> RDS >> Sqlserver

SQL vyplní celkový počet pracovních dnů za měsíc mínus státní svátky pro aktuální finanční rok

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

Pokud to budete dělat déle než 1 rok, budete muset změnit řádek:

OPTION (MAXRECURSION 366)

Jinak se zobrazí chyba – Číslo musí být vyšší než počet dní, na které se dotazujete.

UPRAVIT

Právě jsem narazil na tuto svou starou odpověď a opravdu se mi nelíbí, je tolik věcí, které nyní považuji za špatné praktiky, takže opravím všechny problémy:

  1. Neukončil jsem prohlášení pomocí správně středníkem
  2. Použil rekurzivní CTE k vygenerování seznamu dat
  3. Nezahrnuje seznam sloupců pro vložení
  4. Použil DATENAME k vyloučení víkendů, což je jazyk specifický, mnohem lepší je explicitně nastavit DATEFIRST a použijte DATEPART
  5. Použito LEFT JOIN/IS NULL místo NOT EXISTS k odstranění záznamů ze svátečního stolu. V SQL Serveru je LEFT JOIN/IS NULL méně efektivní než NOT EXISTS

To všechno jsou drobné věci, ale jsou to věci, které bych kritizoval (alespoň v hlavě, ne-li nahlas), když přezkoumávám dotaz někoho jiného, ​​takže opravdu nemohu neopravit svou vlastní práci! Přepsání dotazu by dalo.

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;

Jako poslední bod je tento dotaz mnohem jednodušší díky kalendářní tabulka který ukládá všechna data a má příznaky pro pracovní dny, svátky atd., namísto použití tabulky svátků, která pouze ukládá svátky.



  1. Mysql, kde 1=0 záměna

  2. Poslední provedené dotazy pro konkrétní databázi

  3. Many to Many uvnitř Many to Many Table

  4. návrh databáze pro nastavení upozornění