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 :
- Je n'ai pas terminé les déclarations avec un point-virgule correctement
- Utilisation d'un CTE récursif pour générer une liste de dates
- N'a pas inclus la liste des colonnes pour une insertion
- Utilisé DATENAME pour éliminer les week-ends, qui sont spécifiques à la langue, bien mieux pour définir explicitement
DATEFIRST
et utilisezDATEPART
- Utilisé
LEFT JOIN/IS NULL
au lieu deNOT 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.