Om jag förstår det rätt vill du beräkna skillnaden mellan start- och slutdatum exklusive tiden före 10.00 och efter 19.00.
Här är exempelfrågan och sql-filen.
SELECT start_time,
finish_time,
interval_time,
EXTRACT (HOUR FROM interval_time), --extract the hours,mins and seconds from the interval
EXTRACT (MINUTE FROM interval_time),
EXTRACT (SECOND FROM interval_time)
FROM (SELECT start_time,
finish_time,
NUMTODSINTERVAL (
CASE
WHEN finish_time - TRUNC (finish_time) > (19 / 24) --if finish time is after 7pm
THEN
TRUNC (finish_time) + (19 / 24) --set it to 7pm
ELSE
finish_time --else set it to actual finish time
END
- CASE
WHEN start_time - TRUNC (start_time) < (10 / 24) --if start time is before 10 am
THEN
TRUNC (start_time) + (10 / 24) --set it to 10 am.
ELSE
start_time --else set it to the actual start time
END,
'day') --subtract the both and convert the resulting day to interval
interval_time
FROM timings);
Det jag har gjort är
- Kontrollera om starttiden är före kl. 10.00 och sluttiden är efter kl. 19.00. Om så är fallet, ställ in tiden på 10:00 och 19:00.
- Strahera sedan av datumen och konvertera de resulterande dagarna till Intervalltyp.
- Extrahera sedan timmarna, minuterna och sekunderna från intervallet.
Obs! Den här frågan förutsätter att båda datumen infaller på samma dag och att båda inte är före 10.00 eller efter 19.00.
UPPDATERING: För att utesluta helgdagar blir frågan komplicerad. Jag föreslår att du skriver tre funktioner och använder dessa funktioner i frågan.
Första funktionen:
FUNCTION modify_start_time (p_in_dte DATE) RETURN DATE
----------------------------------
IF p_in_dte - TRUNC (p_in_dte) < (10 / 24)
THEN
RETURN TRUNC (p_in_dte) + (10 / 24);
ELSIF p_in_dte - TRUNC (p_in_dte) > (19 / 24)
THEN
RETURN TRUNC (p_in_dte) + 1 + (10 / 24);
ELSE
RETURN p_in_dte;
END IF;
Om starttiden ligger utanför arbetstiden, ändra starttiden till närmaste starttid.
Andra funktion:
FUNCTION modify_finish_time (p_in_dte DATE) RETURN DATE
----------------------------------
IF p_in_dte - TRUNC (p_in_dte) > (19 / 24)
THEN
RETURN TRUNC (p_in_dte) + (19 / 24);
ELSIF p_in_dte - TRUNC (p_in_dte) < (10 / 24)
THEN
RETURN TRUNC (p_in_dte) - 1 + (19 / 24);
ELSE
RETURN p_in_dte;
END IF;
Om sluttiden ligger utanför arbetstiden, ändra den till föregående närmaste sluttid.
Tredje funktion:
FUNCTION get_days_to_exclude (p_in_start_date DATE,
p_in_finish_date DATE) RETURN NUMBER
--------------------------------------------------------
WITH cte --get all days between start and finish date
AS ( SELECT p_in_start_date + LEVEL - 1 dte
FROM DUAL
CONNECT BY LEVEL <= p_in_finish_date + 1 - p_in_starT_date)
SELECT COUNT (1) * 9 / 24 --mutiply the days with work hours in a day
INTO l_num_holidays
FROM cte
WHERE TO_CHAR (dte, 'dy') = 'sun' --find the count of sundays
OR dte IN --fins the count of holidays, assuming leaves are stored in separate table
(SELECT leave_date
FROM leaves
WHERE leave_date BETWEEN p_in_start_date
AND p_in_finish_date);
l_num_holidays :=
l_num_holidays + ( (p_in_finish_date - p_in_start_date) * (15 / 24)); --also, if the dates span more than a day find the non working hours.
RETURN l_num_holidays;
Den här funktionen hittar antalet dagar som ska exkluderas vid beräkning av varaktigheten.
Så den slutliga frågan borde vara ungefär så här,
SELECT start_time,
finish_time,
CASE
WHEN work_duration < 0 THEN NUMTODSINTERVAL (0, 'day')
ELSE NUMTODSINTERVAL (work_duration, 'day')
END
FROM (SELECT start_time, finish_time,
--modify_start_time (start_time), modify_finish_time (finish_time),
modify_finish_time (finish_time)
- modify_start_time (start_time)
- get_days_to_exclude (
TRUNC (modify_start_time (start_time)),
TRUNC (modify_finish_time (finish_time)))
work_duration
FROM timings);
Om varaktigheten är mindre än 0, ignorera den genom att ställa in den till 0.