2015-05-20 3 views
1

У меня есть список предметов, у которых есть дата начала и окончания. Элементы принадлежат пользователю. Для одного предмета период может составлять от 1 до 5 лет. Я хочу найти количество дней, которые находятся между заданным диапазоном дат, который я передал бы из запроса. Начало периода всегда sysdate и конец sysdate - 5 yearsКоличество дней в периоде

Количество возвращенных дней должно также находиться в диапазоне периодов.

Пример:

Я инициировать запрос от 15.05.2015), как я, будучи пользователем, так что мне нужно, чтобы найти все дни между 15.05.2010 и 15.05.2015

В течение этого периода 2 предметы принадлежат мне:

Пункт 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 дней

Мне нужна сумма этих дней, которая находится именно в этот период.

Для запроса прямо сейчас я просто счетчик, который принимает полный спектр элемента (что делает его простым):

SELECT SUM(i.end_date - i.start_date) AS total_days 
FROM items i 
WHERE i.start_date >= to_date('2010-15-05', 'yyyy-mm-dd') 
AND i.end_date <= to_date('2015-15-05', 'yyyy-mm-dd') 
AND i.user = 'me' 

Так прямо сейчас это дало бы мне о подсчете даты периода 2 года, который неправильно, как я должен обновить свою выбранную сумму, чтобы включить даты, которые находятся в периоде? Правильный результат будет равен 195 + 170. В настоящее время я получаю 365 + 365 или что-то в этом роде.

+0

вы должны поменять 'start_date' и' end_date' в 'WHERE' положение в противном случае вы найдете только те результаты, которые полностью в допустимом диапазоне (а не те, которые частично в пределах диапазона). – MT0

ответ

1

начало периода всегда 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 | 
+0

Спасибо за примеры, если бы мне было действительно ПОСЛЕДНЕЕ и БОЛЬШОЕ! – Vaelyr

1

Если предположить, что периоды времени, не имеют совпадений:

SELECT SUM(LEAST(i.end_date, DATE '2015-05-15') - 
      GREATEST(i.start_date, DATE '2010-05-15') 
     ) AS total_days 
FROM items i 
WHERE i.start_date >= DATE '2010-05-15' AND 
     i.end_date <= DATE '2015-05-15' AND 
     i.user = 'me'; 
+0

Есть перекрытия, но это позволяет считать их так, что это не проблема, я попробую, спасибо. – Vaelyr

1

Используйте случай заявление, чтобы оценить сроки, установленные даты начала и окончания, основанные на случае.

Select SUM(
    (case when i.end_date > to_date('2015-15-05','yyyy-mm-dd') then 
    to_date('2015-15-05','yyyy-mm-dd') else 
    i.end_date end) - 
    (case when i.start_date< to_date('2010-15-05','yyyy-mm-dd') then 
    to_date('2010-15-05','yyyy-mm-dd') else 
    i.end_date end)) as total_days 
FROM items i 
WHERE i.start_date >= to_date('2010-15-05', 'yyyy-mm-dd') 
AND i.end_date <= to_date('2015-15-05', 'yyyy-mm-dd') 
AND i.user = 'me' 
Смежные вопросы