Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

SQL remplit le nombre total de jours ouvrables par mois moins les jours fériés pour l'exercice en cours

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

Si vous faites cela pendant plus d'un an, vous devrez modifier la ligne :

OPTION (MAXRECURSION 366)

Sinon, vous obtiendrez une erreur - Le nombre doit être supérieur au nombre de jours que vous interrogez.

MODIFIER

Je viens de tomber sur cette ancienne réponse et je ne l'aime vraiment pas, il y a tellement de choses que je considère maintenant comme une mauvaise pratique, je vais donc corriger tous les problèmes :

  1. Je n'ai pas terminé les déclarations avec un point-virgule correctement
  2. Utilisation d'un CTE récursif pour générer une liste de dates
  3. N'a pas inclus la liste des colonnes pour une insertion
  4. Utilisé DATENAME pour éliminer les week-ends, qui sont spécifiques à la langue, bien mieux pour définir explicitement DATEFIRST et utilisez DATEPART
  5. Utilisé LEFT JOIN/IS NULL au lieu de NOT EXISTS pour éliminer les enregistrements de la table des vacances. Dans SQL Server LEFT JOIN/IS NULL est moins efficace que NOT EXISTS

Ce sont toutes des choses mineures, mais ce sont des choses que je critiquerais (au moins dans ma tête si ce n'est à voix haute) lors de l'examen de la requête de quelqu'un d'autre, donc je ne peux vraiment pas corriger mon propre travail ! Réécrire la requête donnerait.

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;

Enfin, cette requête devient beaucoup plus simple avec un tableau calendrier qui stocke toutes les dates et a des drapeaux pour les jours ouvrables, les vacances, etc., plutôt que d'utiliser une table de vacances qui stocke uniquement les jours fériés.