Сначала нужно определить минимальную начальную дату и максимальную конечную дату для каждого рабочего идентификатора:
SELECT [WorkId]
, MIN([StartDate]) AS [StartDate]
, MAX([EndDate]) AS [EndDate]
FROM [#temp]
GROUP BY [WorkId]
При небольшом манипулировании датами вам необходимо выяснить даты начала и окончания годавы оцениваете что-то вроде:
DECLARE @Year INT = 2012;
DECLARE @YearBegin DATE;
DECLARE @YearEnd date;
SET @YearBegin = DATEADD(yy, @Year - 1900, 0);
SET @YearEnd = DATEADD(DAY, -1, DATEADD(yy, 1, @YearBegin));
SELECT @YearBegin
, @YearEnd;
После этого мы можем сравнить начальную и конечную даты с этими @YearBegin и @yearEnd, чтобы определить, что они «полностью заняты».
Это предполагаетнет пробелов в диапазонах данных для каждого идентификатора:
DECLARE @Year INT = 2012;
DECLARE @YearBegin DATE;
DECLARE @YearEnd DATE;
SET @YearBegin = DATEADD(yy, @Year - 1900, 0);
SET @YearEnd = DATEADD(DAY, -1, DATEADD(yy, 1, @YearBegin));
SELECT [WorkId]
, CASE WHEN [fr].[StartDate] <= @YearBegin
AND [fr].[EndDate] >= @YearEnd THEN 'Occupied'
ELSE 'Not Occupied'
END AS [Status]
FROM (
SELECT [WorkId]
, MIN([StartDate]) AS [StartDate]
, MAX([EndDate]) AS [EndDate]
FROM [#temp]
GROUP BY [WorkId]
) AS [fr];
Если потенциально могут быть пробелы в диапазонах дат и или перекрывающихся диапазонах дат для каждого идентификатора, вот пример рекурсивного CTE, который будет учитыватьэти ситуации:
DECLARE @Year INT = 2013
, @TotalDayYear INT;
--For the year we are evalutaing, how many days total in that year
--We'll use for comparison later
SET @TotalDayYear = DATEDIFF(
DAY
, CONCAT(@Year - 1, '-12-31')
, CONCAT(@Year, '-12-31')
);
DECLARE @YearOccupied TABLE
(
[WorkId] INT
, [YearOccupied] INT
, [CountDaysOccupied] INT
);
--Recursive CTE to get list of every day a particular ID occupied based on the date ranges.
WITH [Cte]
AS ( SELECT [WorkId]
, [StartDate]
, [EndDate]
, [StartDate] AS [DateOccupied]
FROM [#temp]
UNION ALL
SELECT [a].[WorkId]
, [a].[StartDate]
, [a].[EndDate]
, DATEADD(DAY, 1, [b].[DateOccupied]) AS [DateOccupied]
FROM [#temp] [a]
INNER JOIN [Cte] [b]
ON [b].[WorkId] = [a].[WorkId]
AND [b].[StartDate] = [a].[StartDate]
AND [b].[EndDate] = [a].[EndDate]
AND [b].[DateOccupied] < [a].[EndDate] )
--We'll insert into a table variable a count by year for each Id
INSERT INTO @YearOccupied (
[WorkId]
, [YearOccupied]
, [CountDaysOccupied]
)
SELECT [do].[WorkId]
, YEAR([DateOccupied]) AS [YearOccupied]
, COUNT(*) AS [DaysOccupiedCount]
FROM (
--Use a sub-query here to get a distinct Date Occupied
--To account for overlapping data ranges
SELECT DISTINCT [WorkId]
, [DateOccupied]
FROM [Cte]
) AS [do]
GROUP BY [do].[WorkId]
, YEAR([DateOccupied])
OPTION ( MAXRECURSION 0 );
SELECT [a].[WorkId]
, @Year AS [YearOccupied]
--Coaleace as we may have a year an ID didn't occupy at all
--compared to the total days of the year to determine fully occupied or not
, CASE WHEN COALESCE([b].[CountDaysOccupied], 0) = @TotalDayYear THEN
'Occupied'
ELSE 'Not Occupied'
END AS [Status]
FROM (
--We could have a year we want data for that a particular id didn't occupy.
--We'll build a distinct list of ID and our evalution year for doing a left outer to
--accually see if the year was occupied.
SELECT DISTINCT [WorkId]
, @Year AS [YearOccupied]
FROM @YearOccupied
) AS [a]
LEFT OUTER JOIN @YearOccupied [b]
ON [b].[WorkId] = [a].[WorkId]
AND [b].[YearOccupied] = @Year;