sql >> Databasteknik >  >> RDS >> Sqlserver

SQL fylla i totalt antal arbetsdagar per månad minus helgdagar för innevarande räkenskapsår

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

Om du gör detta i mer än 1 år måste du ändra raden:

OPTION (MAXRECURSION 366)

Annars får du ett felmeddelande - Antalet måste vara högre än antalet dagar du frågar.

REDIGERA

Jag har precis stött på det här gamla svaret av mig och gillar det verkligen inte, det finns så många saker som jag nu anser vara dålig praxis, så jag ska rätta till alla frågor:

  1. Jag avslutade inte uttalanden med ett semikolon korrekt
  2. Använde en rekursiv CTE för att generera en lista över datum
  3. Inkluderade inte kolumnlistan för en infogning
  4. Använde DATENAME för att eliminera helger, vilket är språkspecifikt, mycket bättre att uttryckligen ställa in DATEFIRST och använd DATEPART
  5. Använde LEFT JOIN/IS NULL istället för NOT EXISTS att eliminera poster från semestertabellen. I SQL Server är LEFT JOIN/IS NULL mindre effektivt än INTE FINNS

Dessa är alla mindre saker, men de är saker jag skulle kritisera (åtminstone i mitt huvud om inte outloud) när jag granskar någon annans fråga, så jag kan verkligen inte korrigera mitt eget arbete! Att skriva om frågan skulle ge.

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;

Som en sista punkt blir den här frågan mycket enklare med en kalendertabell som lagrar alla datum och har flaggor för arbetsdagar, helgdagar etc, snarare än att använda en semestertabell som bara lagrar helgdagar.



  1. Ha unpivot automatiskt ta tag i kolumnlista (oracle 11g)

  2. SQL SELECT allt efter ett visst tecken

  3. java-app, tråden hänger sig efter att MySQL-anslutningen dödats

  4. Åsidosätt seriell sekvens i PostgreSql med Entity Framework (C#)