Avere un tavolo calendario con l'elenco di tutte le possibili date è a portata di mano, ma in questo caso si può fare a meno.
Genererò la tua domanda un po '. Invece di guardare solo al trimestre in corso cerchiamo di avere due parametri che definiscono l'intervallo di date che si sono interessati a:
DECLARE @ParamStartDate date;
DECLARE @ParamEndDate date;
In un primo momento abbiamo bisogno di ottenere tutte le righe da Absence
che hanno una gamma FromDate
-UntilDate
che si interseca con il periodo indicato.
SELECT
...
FROM
Absence
WHERE
ABS_REASON='SICK'
-- all absence periods, which overlap with the given period
AND FromDate <= @ParamEndDate
AND UntilDate >= @ParamStartDate
Due periodi A e B si sovrappongono quando (StartA <= EndB)
e (EndA >= StartB)
.
Quindi abbiamo bisogno di calcolare quanti giorni sono nell'intersezione dei due periodi.
Il periodo di intersezione non può essere maggiore dell'intervallo di date date (@ParamStartDate
a @ParamEndDate
).
Il periodo di intersezione non può essere maggiore della durata della malattia (FromDate
a UntilDate
).
Così, l'inizio della intersezione è l'ultimo di FromDate
e @ParamStartDate
, cioè MAX(FromDate, @ParamStartDate)
La fine di intersezione è il più antico di UntilDate
e @ParamEndDate
, cioè MIN(UntilDate, @ParamEndDate)
Infine, la durata del intersezione in giorni è
DATEDIFF(day, MAX(FromDate, @ParamStartDate), MIN(UntilDate, @ParamEndDate))
Ma, solo se è positivo. Se è negativo, significa che il periodo di malattia è terminato prima dell'inizio del trimestre (o la malattia è iniziata dopo che il trimestre è terminato).
Non ci sono funzioni MIN, MAX incorporate che richiedono due parametri a seconda delle esigenze, quindi utilizzo CROSS APPLY
per calcolarli. Inoltre, calcolo il numero di giorni nel trimestre dato, solo per completezza. La query finale è simile al seguente:
SELECT
1+DATEDIFF(day, @ParamStartDate, @ParamEndDate) AS QuarterDays
,CASE WHEN 1+DATEDIFF(day, CTE_MaxStartDate.AbsenceStartDate, CTE_MinEndDate.AbsenceEndDate) > 0
THEN 1+DATEDIFF(day, CTE_MaxStartDate.AbsenceStartDate, CTE_MinEndDate.AbsenceEndDate)
ELSE 0 END AS AbsenceDays
FROM
Absence
CROSS APPLY
(
SELECT CASE WHEN UntilDate < @ParamEndDate THEN UntilDate ELSE @ParamEndDate END AS AbsenceEndDate
) AS CTE_MinEndDate
CROSS APPLY
(
SELECT CASE WHEN FromDate > @ParamStartDate THEN FromDate ELSE @ParamStartDate END AS AbsenceStartDate
) AS CTE_MaxStartDate
WHERE
ABS_REASON='SICK'
-- all absence periods, which overlap with the given period
AND FromDate <= @ParamEndDate
AND UntilDate >= @ParamStartDate
aggiungo 1-DATEDIFF
per ottenere una durata di un giorno, se sono le stesse date di inizio e di fine del periodo.
puoi condividere i dati di esempio della tabella 'ASSENZA'? –
Hai bisogno di tutte le assenze che sono iniziate prima della fine del trimestre e terminate dopo l'inizio del trimestre (o sono 'NULL' se non sai quando torneranno ancora), quindi la tua clausola' WHERE' è corretto (a meno che non sia necessario provvedere a 'NULL'' UNTILDATE'). Quindi è necessario contare solo i giorni nel trimestre corrente e l'intervallo di assenza, e per questo è necessario sapere quali date sono giorni di lavoro, come suggerito da Rob Farley. –