powered by simpleCommunicator - 2.0.50     © 2025 Programmizd 02
Форумы / Oracle [игнор отключен] [закрыт для гостей] / Снова пересекающиеся диапазоны, просьба проверить
5 сообщений из 5, страница 1 из 1
Снова пересекающиеся диапазоны, просьба проверить
    #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
Снова пересекающиеся диапазоны, просьба проверить
    #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
Период между сообщениями больше года.
Снова пересекающиеся диапазоны, просьба проверить
    #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
Снова пересекающиеся диапазоны, просьба проверить
    #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
Снова пересекающиеся диапазоны, просьба проверить
    #40073939
Alibek B
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andrey_anonymous
b > A and a < B

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


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