Логическая ошибка в выборе строк с правильным выводом - PullRequest
0 голосов
/ 13 июня 2019

Я понимаю основы, но я новичок в СУБД, и я изучаю курс.

Вот вопрос с заданием:

Напишите запрос для отображения числапродаж, которые были сделаны за последние 40 месяцев с использованием приведенной ниже таблицы:

SALEID  SID SLDATE
1001    1   01-JAN-14
1002    5   02-JAN-14
1003    4   01-FEB-14
1004    1   01-MAR-14
1005    2   01-FEB-14
1006    1   01-JUN-15

Мой запрос:

select count(sldate) as sale_count
from sale
where sldate >= add_months(sysdate, -40)

Ожидаемый результат, который я получаю:

SALE_COUNT
0

Но я получаю сообщение об ошибке:

Ошибка: выходные данные вашего запроса соответствуют ожидаемому результату, но есть логические ошибки.

I 'Я не уверен, где я неправильно понял логику.

1 Ответ

1 голос
/ 13 июня 2019

«Последние 40 месяцев» неоднозначны.

Существует несколько толкований «что последние n месяцев с даты x означают», а add_months оракула не имеет монополии на это (на самом деле большинстволюди скажут, что это не работает так, как ожидалось, просто подождите до 30.06 и спросите кого-нибудь, «какая была дата месяц назад» :))

Представьте, что сегодня 20 апреля.Включает ли «последний месяц» 15, 20, 21 или 25 марта?Это включает 02 апреля?Это зависит от того, кто-то может сказать, что «последний месяц» с 21 марта по 20 апреля.
Кто-то может сказать, что «последний месяц» с 01.03 по сегодняшний день.
Кто-то может сказать, что «последний месяц»начинается с 01.04.Кто-то может сказать, что «последний месяц» означает весь марш, но не один день апреля.

Становится еще сложнее, когда «сегодня» приближается к концу месяца, особенно в марте.

Не будьте осторожны с собой только потому, что вы не можете прочитать мысли человека, написавшего задание;)

Я написал запрос, показывающий, как разные подходы могут давать разные результаты.

CREATE OR REPLACE FUNCTION temp_can_subst_interval_months(p_date date, p_n_of_months number) RETURN NUMBER AS
V_date DATE;
BEGIN
    V_Date := p_date - (NUMTOYMINTERVAL(p_n_of_months, 'month'));
    RETURN 1;
EXCEPTION
   WHEN OTHERS THEN
      RETURN 0;
END;


with all_days as (
   select to_date('2016-01-01', 'YYYY-MM-DD') + (level - 1) as d
     from dual
    connect by level < 1462

),
all_days_2 as (
select d date_of_query_being_run,
       add_months(d, -40)as min_date_your_approach,
       add_months(d, -40) + 1 as min_date_your_approach_2, -- same, but exclude the first day
       trunc(add_months(d, -40), 'mm') as min_date_whole_month,
       case when temp_can_subst_interval_months(d, 40) = 1 then
         d - (interval '40' month)
       else null
       end as min_date_interval_approach
  from all_days ad
 order by ad.d
)
select ads.*
  from all_days_2 ads
  ;

Наиболее интересные результаты, когда ваш подход отличается от интервального:

1 (sysdate) 2 (yours)   3           4           5 (interval)
31.01.2016  30.09.2012  01.10.2012  01.09.2012  
29.02.2016  31.10.2012  01.11.2012  01.10.2012  29.10.2012
31.03.2016  30.11.2012  01.12.2012  01.11.2012  
30.04.2016  31.12.2012  01.01.2013  01.12.2012  30.12.2012
29.06.2016  28.02.2013  01.03.2013  01.02.2013  
30.06.2016  28.02.2013  01.03.2013  01.02.2013  
31.08.2016  30.04.2013  01.05.2013  01.04.2013  
30.09.2016  31.05.2013  01.06.2013  01.05.2013  30.05.2013
31.10.2016  30.06.2013  01.07.2013  01.06.2013  
30.11.2016  31.07.2013  01.08.2013  01.07.2013  30.07.2013
31.01.2017  30.09.2013  01.10.2013  01.09.2013  
28.02.2017  31.10.2013  01.11.2013  01.10.2013  28.10.2013
31.03.2017  30.11.2013  01.12.2013  01.11.2013  
30.04.2017  31.12.2013  01.01.2014  01.12.2013  30.12.2013
29.06.2017  28.02.2014  01.03.2014  01.02.2014  
30.06.2017  28.02.2014  01.03.2014  01.02.2014  
31.08.2017  30.04.2014  01.05.2014  01.04.2014  
30.09.2017  31.05.2014  01.06.2014  01.05.2014  30.05.2014
31.10.2017  30.06.2014  01.07.2014  01.06.2014  
30.11.2017  31.07.2014  01.08.2014  01.07.2014  30.07.2014
31.01.2018  30.09.2014  01.10.2014  01.09.2014  
28.02.2018  31.10.2014  01.11.2014  01.10.2014  28.10.2014
31.03.2018  30.11.2014  01.12.2014  01.11.2014  
30.04.2018  31.12.2014  01.01.2015  01.12.2014  30.12.2014
29.06.2018  28.02.2015  01.03.2015  01.02.2015  
30.06.2018  28.02.2015  01.03.2015  01.02.2015  
31.08.2018  30.04.2015  01.05.2015  01.04.2015  
30.09.2018  31.05.2015  01.06.2015  01.05.2015  30.05.2015
31.10.2018  30.06.2015  01.07.2015  01.06.2015  
30.11.2018  31.07.2015  01.08.2015  01.07.2015  30.07.2015
31.01.2019  30.09.2015  01.10.2015  01.09.2015  
28.02.2019  31.10.2015  01.11.2015  01.10.2015  28.10.2015
31.03.2019  30.11.2015  01.12.2015  01.11.2015  
30.04.2019  31.12.2015  01.01.2016  01.12.2015  30.12.2015
30.06.2019  29.02.2016  01.03.2016  01.02.2016  
31.08.2019  30.04.2016  01.05.2016  01.04.2016  
30.09.2019  31.05.2016  01.06.2016  01.05.2016  30.05.2016
31.10.2019  30.06.2016  01.07.2016  01.06.2016  
30.11.2019  31.07.2016  01.08.2016  01.07.2016  30.07.2016

Примечание:
Возможно, таблица содержит что-то вроде «ожидаемых будущих продаж», и они хотят, чтобы выотфильтровать даты позже sysdate;)?

...