начало периода всегда sysdate
и конец sysdate - 5 years
Вы можете получить это с помощью: SYSDATE
и SYSDATE - INTERVAL '5' YEAR
Пункт 1) 01.01.2010 - 31.12.2010. Действующий диапазон: 15.05.2010 - 31.12.2010 = ~ 195 дней
Пункт 2) 01.01.2015 - 31.12.2015. Допустимый диапазон: 01.01.2015 - 15.05.2015 = ~ 170 дней
Предполагая, что эти примеры показывают start_date
- end_date
и допустимый диапазон ваш ожидаемый ответ для конкретного SYSDATE
, то вы можете использовать:
SQL Fiddle
Oracle 11g R2 Схема Установка:
CREATE TABLE items ("user", start_date, end_date) AS
SELECT 'me', DATE '2010-01-01', DATE '2010-12-31' FROM DUAL
UNION ALL SELECT 'me', DATE '2015-01-01', DATE '2015-12-31' FROM DUAL
UNION ALL SELECT 'me', DATE '2009-01-01', DATE '2009-12-31' FROM DUAL
UNION ALL SELECT 'me', DATE '2009-01-01', DATE '2016-12-31' FROM DUAL
UNION ALL SELECT 'me', DATE '2012-01-01', DATE '2012-12-31' FROM DUAL
UNION ALL SELECT 'me', DATE '2013-01-01', DATE '2013-01-01' FROM DUAL;
Запрос 1:
SELECT "user",
TO_CHAR(start_date, 'YYYY-MM-DD') AS start_date,
TO_CHAR(end_date, 'YYYY-MM-DD') AS end_date,
TO_CHAR(GREATEST(TRUNC(i.start_date), TRUNC(SYSDATE)-INTERVAL '5' YEAR), 'YYYY-MM-DD') AS valid_start,
TO_CHAR(LEAST(TRUNC(i.end_date),TRUNC(SYSDATE)), 'YYYY-MM-DD') AS valid_end,
LEAST(TRUNC(i.end_date),TRUNC(SYSDATE))
- GREATEST(TRUNC(i.start_date), TRUNC(SYSDATE)-INTERVAL '5' YEAR)
+ 1
AS total_days
FROM items i
WHERE i."user" = 'me'
AND TRUNC(i.start_date) <= TRUNC(SYSDATE)
AND TRUNC(i.end_date) >= TRUNC(SYSDATE) - INTERVAL '5' YEAR
Results:
| user | START_DATE | END_DATE | VALID_START | VALID_END | TOTAL_DAYS |
|------|------------|------------|-------------|------------|------------|
| me | 2010-01-01 | 2010-12-31 | 2010-05-21 | 2010-12-31 | 225 |
| me | 2015-01-01 | 2015-12-31 | 2015-01-01 | 2015-05-21 | 141 |
| me | 2009-01-01 | 2016-12-31 | 2010-05-21 | 2015-05-21 | 1827 |
| me | 2012-01-01 | 2012-12-31 | 2012-01-01 | 2012-12-31 | 366 |
| me | 2013-01-01 | 2013-01-01 | 2013-01-01 | 2013-01-01 | 1 |
Это предполагает, что дата начала в начале дня (00:00) и дата окончания в конце дня (24:00) - поэтому, если даты начала и окончания совпадают, тогда вы ожидаете, что результат будет 1 общий день (т.е. период 00:00 - 24:00). Если вы, вместо этого, ожидаете, что результат будет равен 0, удалите +1
из расчета общего значения дня.
Запрос 2:
Если вы хотите, чтобы сумма всех эти допустимых диапазонов и радуются рассчитывать даты в диапазонах перекрываются несколько раз, то просто обернуть его в агрегатной функции SUM
:
SELECT SUM(LEAST(TRUNC(i.end_date),TRUNC(SYSDATE))
- GREATEST(TRUNC(i.start_date), TRUNC(SYSDATE)-INTERVAL '5' YEAR)
+ 1)
AS total_days
FROM items i
WHERE i."user" = 'me'
AND TRUNC(i.start_date) <= TRUNC(SYSDATE)
AND TRUNC(i.end_date) >= TRUNC(SYSDATE) - INTERVAL '5' YEAR
Results:
| TOTAL_DAYS |
|------------|
| 2560 |
Query 3:
Теперь, если вы хотите, чтобы получить количество всех действительных дней в диапазоне и не рассчитывать перекрытия в диапазоне несколько раз, то вы можете сделать:
WITH ALL_DATES_IN_RANGE AS (
SELECT TRUNC(SYSDATE) - LEVEL + 1 AS valid_date
FROM DUAL
CONNECT BY LEVEL <= SYSDATE - (SYSDATE - INTERVAL '5' YEAR) + 1
)
SELECT COUNT(1) AS TOTAL_DAYS
FROM ALL_DATES_IN_RANGE a
WHERE EXISTS (SELECT 'X'
FROM items i
WHERE a.valid_date BETWEEN i.start_date AND i.end_date
AND i."user" = 'me')
Results:
| TOTAL_DAYS |
|------------|
| 1827 |
вы должны поменять 'start_date' и' end_date' в 'WHERE' положение в противном случае вы найдете только те результаты, которые полностью в допустимом диапазоне (а не те, которые частично в пределах диапазона). – MT0