Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / Oracle [игнор отключен] [закрыт для гостей] / Снова пересекающиеся диапазоны, просьба проверить / 5 сообщений из 5, страница 1 из 1
28.04.2014, 15:59
    #38627892
Alibek B
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Снова пересекающиеся диапазоны, просьба проверить
У меня глаз замылился, просьба посмотреть, не упустил ли чего.

Суть задачи — нужно начислить компенсацию за перебои в оказании сервисов.
Компенсации подлежат только те услуги, которые на период перебоев были оплачены.
Если услуга была активно только часть этого периода, то и компенсации подлежит только часть периода.

Таблица услуг и статуса услуг:
SERVICE_IDSERVICE_FEE_DAILY110021003500
SERVICE_IDDATE_BEGDATE_ENDSTATUS12010-01-012010-03-01112010-03-012010-03-10012010-03-10122010-01-202010-02-01122010-02-01032010-01-052010-01-20132010-01-202010-02-01032010-02-012010-03-01132010-03-012010-03-15032010-03-151

Таблица перерывов в оказании сервисов (дата и длительность в днях):
ACCIDENT_IDACCIDENT_DATEACCIDENT_DURATION12010-01-10122010-01-151032010-02-01542010-02-2510

Запрос я составил такой:
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
with
A as 
(
  select null as ACCIDENT_ID, null as ACCIDENT_DATE, null as ACCIDENT_DURATION from dual where 0=1
  union all select 1, date'2010-01-10',1 from dual
  union all select 2, date'2010-01-15',10 from dual
  union all select 3, date'2010-02-01',5 from dual
  union all select 4, date'2010-02-25',10 from dual
),
S as
(
  select null as SERVICE_ID, null as SERVICE_FEE from dual where 0=1
  union all select 1, 100 from dual
  union all select 2, 100 from dual
  union all select 3, 500 from dual
),
H as
(
  select null as SERVICE_ID, null as DATE_BEG, null as DATE_END, null as STATUS from dual where 0=1
  union all select 1, date'2010-01-01', date'2010-03-01', 1 from dual
  union all select 1, date'2010-03-01', date'2010-03-10', 0 from dual
  union all select 1, date'2010-03-10', null, 1             from dual
  union all select 2, date'2010-01-20', date'2010-02-01', 1 from dual
  union all select 2, date'2010-02-01', null, 0             from dual
  union all select 3, date'2010-01-05', date'2010-01-20', 1 from dual
  union all select 3, date'2010-01-20', date'2010-02-01', 0 from dual
  union all select 3, date'2010-02-01', date'2010-03-01', 1 from dual
  union all select 3, date'2010-03-01', date'2010-03-15', 0 from dual
  union all select 3, date'2010-03-15', null, 1             from dual
)
select S.SERVICE_ID
, greatest(A.ACCIDENT_DATE,H.DATE_BEG) as REFUND_BEG
, least(A.ACCIDENT_DATE+A.ACCIDENT_DURATION,nvl(H.DATE_END,date'9999-01-01')) as REFUND_END
, (least(A.ACCIDENT_DATE+A.ACCIDENT_DURATION,nvl(H.DATE_END,date'9999-01-01')) - greatest(A.ACCIDENT_DATE,H.DATE_BEG)) as REFUND_PERIOD
, S.SERVICE_FEE * (least(A.ACCIDENT_DATE+A.ACCIDENT_DURATION,nvl(H.DATE_END,date'9999-01-01')) - greatest(A.ACCIDENT_DATE,H.DATE_BEG)) as REFUND_VALUE
, A.ACCIDENT_DATE, A.ACCIDENT_DURATION, H.DATE_BEG, H.DATE_END
from S
join H on (H.SERVICE_ID = S.SERVICE_ID and H.STATUS = 1)
join A on (A.ACCIDENT_DATE >= H.DATE_BEG and A.ACCIDENT_DATE < nvl(H.DATE_END,date'9999-09-01'))


Вроде бы правильно, но как-то слишком просто.
Я ничего не упустил?
________________________
Мы смотрим с оптимизмом...
...в оптический прицел.
...
Рейтинг: 0 / 0
28.04.2014, 16:23
    #38627923
Alibek B
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Снова пересекающиеся диапазоны, просьба проверить
Небольшая поправка для учета пересечения:
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
select S.SERVICE_ID
, greatest(A.ACCIDENT_DATE,H.DATE_BEG) as REFUND_BEG
, least(A.ACCIDENT_DATE+A.ACCIDENT_DURATION,nvl(H.DATE_END,date'9999-01-01')) as REFUND_END
, (least(A.ACCIDENT_DATE+A.ACCIDENT_DURATION,nvl(H.DATE_END,date'9999-01-01')) - greatest(A.ACCIDENT_DATE,H.DATE_BEG)) as REFUND_PERIOD
, S.SERVICE_FEE * (least(A.ACCIDENT_DATE+A.ACCIDENT_DURATION,nvl(H.DATE_END,date'9999-01-01')) - greatest(A.ACCIDENT_DATE,H.DATE_BEG)) as REFUND_VALUE
, A.ACCIDENT_DATE, A.ACCIDENT_DURATION, H.DATE_BEG, H.DATE_END
from S
join H on (H.SERVICE_ID = S.SERVICE_ID and H.STATUS = 1)
join A on (A.ACCIDENT_DATE < nvl(H.DATE_END,date'9999-09-01') and A.ACCIDENT_DATE+A.ACCIDENT_DURATION > H.DATE_BEG)
...
Рейтинг: 0 / 0
Период между сообщениями больше года.
28.05.2021, 18:42
    #40073917
Alibek B
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Снова пересекающиеся диапазоны, просьба проверить
Не хочется создавать очередную тему по интервалам, но хочется уточнить, что я не ошибаюсь.
Допустим, есть набор различных периодов a-b.
Нужно выбрать из них те, которые полностью или частично пересекают заданный период A-B (1, 6, 7, 8).
То есть a-b может быть полностью внутри периода, может быть частично или полностью за его пределами.
Использовать greatest(a,A)>=A и least(b,B)<B внутри соединения join я не хочу, потому что перестанут работать индексы.
Самым простым и эффективным будет соединение not (b <= A or a >= B) ?
Или можно обойтись без not и or ? Не знаю, как в Oracle, но в MSSQL мне в свое время советовали всеми силами избегать OR.
...
Рейтинг: 0 / 0
28.05.2021, 18:47
    #40073919
andrey_anonymous
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Снова пересекающиеся диапазоны, просьба проверить
Alibek B.
not (b <= A or a >= B)

1. not (b <= A or a >= B) === b > A and a < B
2. Вопросы эффективности с т.з. БД формулируются и решаются иначе.
...
Рейтинг: 0 / 0
28.05.2021, 20:01
    #40073939
Alibek B
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Снова пересекающиеся диапазоны, просьба проверить
andrey_anonymous
b > A and a < B

Точно, спасибо.
...
Рейтинг: 0 / 0
Форумы / Oracle [игнор отключен] [закрыт для гостей] / Снова пересекающиеся диапазоны, просьба проверить / 5 сообщений из 5, страница 1 из 1
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]