Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / Oracle [игнор отключен] [закрыт для гостей] / Пересечение диапазонов (переписать start of group) / 24 сообщений из 24, страница 1 из 1
24.05.2017, 23:53
    #39459204
Пересечение диапазонов (переписать start of group)
Задача условно.
На полке склада лежат товары, но период каждого товара учитывается отдельно.
То есть:
id полки,
id товара,
date_from,
date_to
То есть каждый товар учтен отдельно в отношении каждой полки.
Необходимо вывести историю полки, в которой отразить периоды с полным списком товаров, лежащих в ней в определенный период.
Примерный вариант (id считаем, как id товара и полка для примера одна, т.е. id полки в исходных данных нет):
Код: 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.
WITH t0 AS (SELECT 1 ID, TO_DATE('01021999', 'ddmmyyyy') beg_date, TO_DATE('01031999', 'ddmmyyyy') end_date FROM dual UNION ALL
            SELECT 2 ID, TO_DATE('01021999', 'ddmmyyyy') beg_date, TO_DATE('01031999', 'ddmmyyyy') end_date FROM dual UNION ALL
            SELECT 3 ID, TO_DATE('01022000', 'ddmmyyyy') beg_date, TO_DATE('01032000', 'ddmmyyyy') end_date FROM dual UNION ALL
            SELECT 4 ID, TO_DATE('01012000', 'ddmmyyyy') beg_date, TO_DATE('01122000', 'ddmmyyyy') end_date FROM dual UNION ALL            
            SELECT 5 ID, TO_DATE('01082000', 'ddmmyyyy') beg_date, TO_DATE('01112000', 'ddmmyyyy') end_date FROM dual UNION ALL            
            SELECT 6 ID, TO_DATE('01072000', 'ddmmyyyy') beg_date, TO_DATE('01012002', 'ddmmyyyy') end_date FROM dual),
     t1 AS (SELECT MIN(beg_date) date_from,
                   MAX(end_date) date_to
              FROM t0),
     t3 AS (SELECT date_from + LEVEL - 1 dat
              FROM t1
           CONNECT BY LEVEL < date_to - date_from + 2),
     t4 AS (SELECT dat, LISTAGG(ID, ', ') WITHIN GROUP(ORDER BY id) id_list
              FROM t3,
                   t0
             WHERE t3.dat BETWEEN t0.beg_date AND t0.end_date
            GROUP BY dat)
SELECT MIN(dat) date_from,
       MAX(dat) date_to,
       id_list
  FROM (SELECT  t5.*, SUM(sog) OVER(ORDER BY dat) grp
          FROM (SELECT t4.*,
                       CASE WHEN id_list <> LAG(id_list) OVER(ORDER BY dat) THEN 1 END sog
                  FROM t4) t5)
 GROUP BY grp, id_list
 ORDER BY date_from,
          date_to


Но на относительно больших данных запрос работает категорически долго.
Поэтому вопрос, может есть вариант не раскладывать все периоды на количество дней с последующим джойном и схлопыванием диапазонов по sog? Как-то сразу может с sog сообразить?
--
Была еще мысль, исходить из того, что если два диапазона пересекаются, то:
1. если они полностью совпадают - все без изменений
2. если совпадает только одна из границ или границы не совпадают, то один из диапазонов бьется на части, т.е. из двух записей образуется третья (пересечение), + корректириуются границы исходных диапазонов с учетом пересечения.
Т.е. если два диапазона пересекаются (но не четко по границам), на выходе в любом случае получаем уже три записи.
Т.е. лефт джоин исходного набора с самим с собой (условия отд. тема) + крос джоин с level < 4 (3 диапазона) и в зависимости от левела кейсами выставляем date_from и date_to. Но работающего варианта пока не получил.
...
Рейтинг: 0 / 0
24.05.2017, 23:59
    #39459208
Пересечение диапазонов (переписать start of group)
ацкийсотонаТ.е. если два диапазона пересекаются (но не четко по границам), на выходе в любом случае получаем уже три записи
Не записи 3, а 3 диапазона, а записей будет от 3 до 4 (1-2 для каждого из id в пересечении и по одной исходной с границами, отредактированными с учетом пересечения).
...
Рейтинг: 0 / 0
25.05.2017, 05:04
    #39459245
Пересечение диапазонов (переписать start of group)
ацкийсотона,

развернуть существующие диапазоны в список точек вида:
дата точки, тип точки (начало кончало)
после чего свернуть точки в новые диапазоны...
...
Рейтинг: 0 / 0
25.05.2017, 07:29
    #39459263
Пересечение диапазонов (переписать start of group)
Добрый Э - Эх,

Спасибо, попробую, правда не очень понимаю пока как сворачивать, одновременно агрегируя id в список.
Видимо надо развернуть в список точек, потом агрегация id, а потом свернуть диапазоны?
Т.е. разница только в том, что разворачиваем не на весь список дат, а в граничные точки диапазонов?
...
Рейтинг: 0 / 0
25.05.2017, 07:45
    #39459270
Пересечение диапазонов (переписать start of group)
ацкийсотона,

похожая тема
...
Рейтинг: 0 / 0
25.05.2017, 08:00
    #39459272
Пересечение диапазонов (переписать start of group)
Добрый Э - Эхпохожая тема
Смотрел по результатам примеров - не нашел там агрегации при схлопывании.
Сейчас вижу так, разворачиваем на точки, потом идем по списку и в зависимости от типа даты либо добавляем Id в список, либо удаляем (но тогда запрос видимо должен быть рекурсивным и еще список id должен быть отсортирован, дабы проще в дальнейшем искать изменения в результирующем наборе), потом схлопываем.
...
Рейтинг: 0 / 0
25.05.2017, 09:50
    #39459333
MaximaXXL
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение диапазонов (переписать start of group)
ацкийсотона,

Я наверно как обычно туплю =) но первый мой вариант ...
Код: 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.
with t0 (id,beg_date, end_date) as(
select 1, to_date('01.02.1999','dd.mm.yyyy') , to_date('01.03.1999','dd.mm.yyyy') from dual union all
select 2, to_date('01.02.1999','dd.mm.yyyy') , to_date('01.03.1999','dd.mm.yyyy')  from dual union all
select 3, to_date('01.02.2000','dd.mm.yyyy') , to_date('01.03.2000','dd.mm.yyyy')  from dual union all
select 4, to_date('01.01.2000','dd.mm.yyyy') , to_date('01.12.2000','dd.mm.yyyy')  from dual union all
select 5, to_date('01.08.2000','dd.mm.yyyy') , to_date('01.11.2000','dd.mm.yyyy')  from dual union all
select 6, to_date('01.07.2000','dd.mm.yyyy') , to_date('01.01.2002','dd.mm.yyyy')  from dual )

,t1 as (select  to_date('10.02.1999','dd.mm.yyyy') vStart_date, to_date('10.02.2001','dd.mm.yyyy') vEnd_date from dual)


, t3 as(select Date_Line Start_D, lead(Date_Line) over(ORDER by Date_Line) End_D
from (
SELECT distinct decode(cBeg,1,Beg_date,End_Date) Date_Line
from t0, t1, (select level cBeg from dual connect by level = 2)
where end_date >= vStart_date
  and beg_date <= vEnd_date 
order by Date_line)
)


SELECT Start_D, End_D, listagg(id,', ') within group (order by id)  
from t0, t3
where end_date >= Start_D
  and beg_date <= End_D
  and End_D is not null
group by Start_D, End_D



где таблица t1 создана для входных переменных (мне лень было передавать переменные vStart_date, vEnd_date и я ввел таблицу t1)
...
Рейтинг: 0 / 0
25.05.2017, 10:21
    #39459379
stax..
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение диапазонов (переписать start of group)
Добрый Э - Эхацкийсотона,

развернуть существующие диапазоны в список точек вида:
дата точки, тип точки (начало кончало)
после чего свернуть точки в новые диапазоны...
ему нельзя сворачивать, потеряет ид

.....
stax
...
Рейтинг: 0 / 0
25.05.2017, 10:41
    #39459410
stax..
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение диапазонов (переписать start of group)
ацкийсотонаЗадача условно.
На полке склада лежат товары, но период каждого товара учитывается отдельно.
То есть:
id полки,
id товара,
date_from,
date_to
То есть каждый товар учтен отдельно в отношении каждой полки.
Необходимо вывести историю полки, в которой отразить периоды с полным списком товаров, лежащих в ней в определенный период.
Примерный вариант (id считаем, как id товара и полка для примера одна, т.е. id полки в исходных данных нет):
Код: 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.
WITH t0 AS (SELECT 1 ID, TO_DATE('01021999', 'ddmmyyyy') beg_date, TO_DATE('01031999', 'ddmmyyyy') end_date FROM dual UNION ALL
            SELECT 2 ID, TO_DATE('01021999', 'ddmmyyyy') beg_date, TO_DATE('01031999', 'ddmmyyyy') end_date FROM dual UNION ALL
            SELECT 3 ID, TO_DATE('01022000', 'ddmmyyyy') beg_date, TO_DATE('01032000', 'ddmmyyyy') end_date FROM dual UNION ALL
            SELECT 4 ID, TO_DATE('01012000', 'ddmmyyyy') beg_date, TO_DATE('01122000', 'ddmmyyyy') end_date FROM dual UNION ALL            
            SELECT 5 ID, TO_DATE('01082000', 'ddmmyyyy') beg_date, TO_DATE('01112000', 'ddmmyyyy') end_date FROM dual UNION ALL            
            SELECT 6 ID, TO_DATE('01072000', 'ddmmyyyy') beg_date, TO_DATE('01012002', 'ddmmyyyy') end_date FROM dual),
     t1 AS (SELECT MIN(beg_date) date_from,
                   MAX(end_date) date_to
              FROM t0),
     t3 AS (SELECT date_from + LEVEL - 1 dat
              FROM t1
           CONNECT BY LEVEL < date_to - date_from + 2),
     t4 AS (SELECT dat, LISTAGG(ID, ', ') WITHIN GROUP(ORDER BY id) id_list
              FROM t3,
                   t0
             WHERE t3.dat BETWEEN t0.beg_date AND t0.end_date
            GROUP BY dat)
SELECT MIN(dat) date_from,
       MAX(dat) date_to,
       id_list
  FROM (SELECT  t5.*, SUM(sog) OVER(ORDER BY dat) grp
          FROM (SELECT t4.*,
                       CASE WHEN id_list <> LAG(id_list) OVER(ORDER BY dat) THEN 1 END sog
                  FROM t4) t5)
 GROUP BY grp, id_list
 ORDER BY date_from,
          date_to


Но на относительно больших данных запрос работает категорически долго.
Поэтому вопрос, может есть вариант не раскладывать все периоды на количество дней с последующим джойном и схлопыванием диапазонов по sog? Как-то сразу может с sog сообразить?
--
Была еще мысль, исходить из того, что если два диапазона пересекаются, то:
1. если они полностью совпадают - все без изменений
2. если совпадает только одна из границ или границы не совпадают, то один из диапазонов бьется на части, т.е. из двух записей образуется третья (пересечение), + корректириуются границы исходных диапазонов с учетом пересечения.
Т.е. если два диапазона пересекаются (но не четко по границам), на выходе в любом случае получаем уже три записи.
Т.е. лефт джоин исходного набора с самим с собой (условия отд. тема) + крос джоин с level < 4 (3 диапазона) и в зависимости от левела кейсами выставляем date_from и date_to. Но работающего варианта пока не получил.

1) "больших данных" ето сколько?
2) t4 долго считает? на каком етапе главний тормоз?

дальше надо пробовать, напр
1) отказаться от генерации мах/мин диапазона дат t3, попробовать генерить дни для каждой строки
2) отказаться от старт оф групп, свернуть через ровнум

.....
stax
...
Рейтинг: 0 / 0
25.05.2017, 10:50
    #39459421
MaximaXXL
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение диапазонов (переписать start of group)
ацкийсотона,

Второй вариант (корректированный первый) =)

Код: 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.
with t0 (id,beg_date, end_date) as(
select 1, to_date('01.02.1999','dd.mm.yyyy') , to_date('01.03.1999','dd.mm.yyyy') from dual union all
select 2, to_date('01.02.1999','dd.mm.yyyy') , to_date('01.03.1999','dd.mm.yyyy')  from dual union all
select 3, to_date('01.02.2000','dd.mm.yyyy') , to_date('01.03.2000','dd.mm.yyyy')  from dual union all
select 4, to_date('01.01.2000','dd.mm.yyyy') , to_date('01.12.2000','dd.mm.yyyy')  from dual union all
select 5, to_date('01.08.2000','dd.mm.yyyy') , to_date('01.11.2000','dd.mm.yyyy')  from dual union all
select 6, to_date('01.07.2000','dd.mm.yyyy') , to_date('01.01.2002','dd.mm.yyyy')  from dual )

,t1 as (select  to_date('10.02.1999','dd.mm.yyyy') vStart_date, to_date('10.02.2001','dd.mm.yyyy') vEnd_date from dual)


, t3 as(select Date_Line Start_D, lead(Date_Line) over(ORDER by Date_Line) End_D
from (
SELECT distinct decode(cBeg,1,greatest(Beg_date,vStart_date),least(End_Date,vEnd_date)) Date_Line
from t0, t1, (select level cBeg from dual connect by level = 2)
where end_date >= vStart_date
  and beg_date <= vEnd_date 
order by Date_line)
)

select min(Start_D), max(End_D), List_id
from (
         SELECT Start_D, End_D, listagg(id,', ') within group (order by id)  List_id
            from t0, t3
          where end_date >= Start_D
             and beg_date <= End_D
             and End_D is not null
           group by Start_D, End_D) 
group by List_id
...
Рейтинг: 0 / 0
25.05.2017, 11:01
    #39459423
stax..
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение диапазонов (переписать start of group)
MaximaXXL,

t3 ето ж "развернуть существующие диапазоны в список точек вида: ..."
Ваш хитрый
select start_date from t0
union
select end_date from t0


зи
обычная задачка на пересечение интервалов
часто рашают в лоб (без т3)
select ... from t0 t1,t0 t2 where пересечение t1 i t2
но как поведет себя алгоритм "на относительно больших данных " я не знаю, надо пробывать

.....
stax
...
Рейтинг: 0 / 0
25.05.2017, 11:14
    #39459438
MaximaXXL
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение диапазонов (переписать start of group)
stax..,

вводил t3 больше для понимания/тестирования (чтоб не перегружать один селект) ...
как его реализовать через union или "моим хитрым" =) тут уже автор пусть тестонет по скорости
...
Рейтинг: 0 / 0
25.05.2017, 11:38
    #39459471
stax..
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение диапазонов (переписать start of group)
MaximaXXLацкийсотона,

Второй вариант (корректированный первый) =)


Код: 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.
40.
41.
with t0 (id,beg_date, end_date) as(
select 1, to_date('01.02.1999','dd.mm.yyyy') , to_date('01.03.1999','dd.mm.yyyy') from dual union all
select 2, to_date('01.02.1999','dd.mm.yyyy') , to_date('01.03.1999','dd.mm.yyyy')  from dual union all
select 3, to_date('01.02.2000','dd.mm.yyyy') , to_date('01.03.2000','dd.mm.yyyy')  from dual union all
select 4, to_date('01.01.2000','dd.mm.yyyy') , to_date('01.12.2000','dd.mm.yyyy')  from dual union all
select 5, to_date('01.08.2000','dd.mm.yyyy') , to_date('01.11.2000','dd.mm.yyyy')  from dual union all
--
select 1, to_date('01.02.2016','dd.mm.yyyy') , to_date('01.03.2016','dd.mm.yyyy') from dual union all
select 2, to_date('01.02.2016','dd.mm.yyyy') , to_date('01.03.2016','dd.mm.yyyy')  from dual union all
--
select 6, to_date('01.07.2000','dd.mm.yyyy') , to_date('01.01.2002','dd.mm.yyyy')  from dual )
,t1 as (select  to_date('10.02.1999','dd.mm.yyyy') vStart_date, to_date('10.02.2099','dd.mm.yyyy') vEnd_date from dual)
, t3 as(select Date_Line Start_D, lead(Date_Line) over(ORDER by Date_Line) End_D
from (
SELECT distinct decode(cBeg,1,greatest(Beg_date,vStart_date),least(End_Date,vEnd_date)) Date_Line
from t0, t1, (select level cBeg from dual connect by level = 2)
where end_date >= vStart_date
  and beg_date <= vEnd_date 
order by Date_line)
)
select min(Start_D), max(End_D), List_id
from (
         SELECT Start_D, End_D, listagg(id,', ') within group (order by id)  List_id
            from t0, t3
          where end_date >= Start_D
             and beg_date <= End_D
             and End_D is not null
           group by Start_D, End_D) 
group by List_id
/
SQL> /

MIN(STAR MAX(END_ LIST_ID
-------- -------- ----------------------------------------
01.03.99 01.01.00 1, 2, 4
01.01.02 01.02.16 1, 2, 6
10.02.99 01.03.16 1, 2
01.01.00 01.03.00 3, 4
01.07.00 01.12.00 4, 5, 6
01.03.00 01.07.00 3, 4, 6
01.12.00 01.01.02 4, 6



.....
stax
...
Рейтинг: 0 / 0
25.05.2017, 12:40
    #39459556
MaximaXXL
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение диапазонов (переписать start of group)
stax..,

Ваша правда Уважаемый

тогда выкинем группировочку т.о. задача будет удовлетворять условию, а надо ли делать 1 непрерывный интервал для одинаковых наборов автор не уточнял =)

Код: 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.
with t0 (id,beg_date, end_date) as(
select 1, to_date('01.02.1999','dd.mm.yyyy') , to_date('01.03.1999','dd.mm.yyyy') from dual union all
select 2, to_date('01.02.1999','dd.mm.yyyy') , to_date('01.03.1999','dd.mm.yyyy')  from dual union all
select 3, to_date('01.02.2000','dd.mm.yyyy') , to_date('01.03.2000','dd.mm.yyyy')  from dual union all
select 4, to_date('01.01.2000','dd.mm.yyyy') , to_date('01.12.2000','dd.mm.yyyy')  from dual union all
select 5, to_date('01.08.2000','dd.mm.yyyy') , to_date('01.11.2000','dd.mm.yyyy')  from dual union all
--
select 1, to_date('01.02.2016','dd.mm.yyyy') , to_date('01.03.2016','dd.mm.yyyy') from dual union all
select 2, to_date('01.02.2016','dd.mm.yyyy') , to_date('01.03.2016','dd.mm.yyyy')  from dual union all
--
select 6, to_date('01.07.2000','dd.mm.yyyy') , to_date('01.01.2002','dd.mm.yyyy')  from dual )
,t1 as (select  to_date('10.02.1999','dd.mm.yyyy') vStart_date, to_date('10.02.2099','dd.mm.yyyy') vEnd_date from dual)
, t3 as(select Date_Line Start_D, lead(Date_Line) over(ORDER by Date_Line) End_D
from (
SELECT distinct decode(cBeg,1,greatest(Beg_date,vStart_date),least(End_Date,vEnd_date)) Date_Line
from t0, t1, (select level cBeg from dual connect by level = 2)
where end_date >= vStart_date
  and beg_date <= vEnd_date 
order by Date_line)
)

         SELECT Start_D, End_D, listagg(id,', ') within group (order by id)  List_id
            from t0, t3
          where end_date >= Start_D
             and beg_date <= End_D
             and End_D is not null
           group by Start_D, End_D
...
Рейтинг: 0 / 0
25.05.2017, 12:51
    #39459566
stax..
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение диапазонов (переписать start of group)
MaximaXXLstax..,

Ваша правда Уважаемый




select level cBeg from dual connect by level = 2;

интересная фича, можно спрашивать на собеседованиях

.....
stax
...
Рейтинг: 0 / 0
25.05.2017, 21:04
    #39459991
Пересечение диапазонов (переписать start of group)
MaximaXXL, спасибо бальшущее, работает отлично, но с небольшой неточностью, берем упрощенный набор данных:
1. Исходный вариант (тяжелый):
Код: 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.
SQL> WITH t0 AS (SELECT 5 ID, TO_DATE('01082000', 'ddmmyyyy') beg_date, TO_DATE('01112000', 'ddmmyyyy') end_date FROM dual UNION ALL
  2              SELECT 6 ID, TO_DATE('01072000', 'ddmmyyyy') beg_date, TO_DATE('01012002', 'ddmmyyyy') end_date FROM dual),
  3       t1 AS (SELECT MIN(beg_date) date_from,
  4                     MAX(end_date) date_to
  5                FROM t0),
  6       t3 AS (SELECT date_from + LEVEL - 1 dat
  7                FROM t1
  8             CONNECT BY LEVEL < date_to - date_from + 2),
  9       t4 AS (SELECT dat, LISTAGG(ID, ', ') WITHIN GROUP(ORDER BY id) id_list
 10                FROM t3,
 11                     t0
 12               WHERE t3.dat BETWEEN t0.beg_date AND t0.end_date
 13              GROUP BY dat)
 14  SELECT MIN(dat) date_from,
 15         MAX(dat) date_to,
 16         id_list
 17    FROM (SELECT  t5.*, SUM(sog) OVER(ORDER BY dat) grp
 18            FROM (SELECT t4.*,
 19                         CASE WHEN id_list <> LAG(id_list) OVER(ORDER BY dat) THEN 1 END sog
 20                    FROM t4) t5)
 21   GROUP BY grp, id_list
 22   ORDER BY date_from,
 23            date_to
 24  ;
DATE_FROM   DATE_TO     ID_LIST
----------- ----------- --------------------------------------------------------------------------------
01.07.2000  31.07.2000  6
01.08.2000  01.11.2000  5, 6
02.11.2000  01.01.2002  6


Вариант 1:
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
SQL> with t0 (id,beg_date, end_date) as(select 5, to_date('01.08.2000','dd.mm.yyyy') , to_date('01.11.2000','dd.mm.yyyy')  from dual union all
  2                                     select 6, to_date('01.07.2000','dd.mm.yyyy') , to_date('01.01.2002','dd.mm.yyyy')  from dual )
  3  ,t1 as (select  to_date('10.02.1999','dd.mm.yyyy') vStart_date, to_date('10.02.2001','dd.mm.yyyy') vEnd_date from dual)
  4  , t3 as(select Date_Line Start_D, lead(Date_Line) over(ORDER by Date_Line) End_D
  5  from (SELECT distinct decode(cBeg,1,Beg_date,End_Date) Date_Line
  6          from t0, t1, (select level cBeg from dual connect by level = 2)
  7         where end_date >= vStart_date
  8           and beg_date <= vEnd_date
  9        order by Date_line))
 10  SELECT Start_D, End_D, listagg(id,', ') within group (order by id)
 11  from t0, t3
 12  where end_date >= Start_D
 13    and beg_date <= End_D
 14    and End_D is not null
 15  group by Start_D, End_D
 16  ;
START_D     END_D       LISTAGG(ID,',')WITHINGROUP(ORD
----------- ----------- --------------------------------------------------------------------------------
01.07.2000  01.08.2000  5, 6
01.08.2000  01.11.2000  5, 6
01.11.2000  01.01.2002  5, 6


Вариант 2 (корректированный первый):
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
SQL> with t0 (id,beg_date, end_date) as(select 5, to_date('01.08.2000','dd.mm.yyyy') , to_date('01.11.2000','dd.mm.yyyy')  from dual union all
  2                                     select 6, to_date('01.07.2000','dd.mm.yyyy') , to_date('01.01.2002','dd.mm.yyyy')  from dual )
  3  ,t1 as (select  to_date('10.02.1999','dd.mm.yyyy') vStart_date, to_date('10.02.2001','dd.mm.yyyy') vEnd_date from dual)
  4  , t3 as(select Date_Line Start_D, lead(Date_Line) over(ORDER by Date_Line) End_D
  5  from (SELECT distinct decode(cBeg,1,greatest(Beg_date,vStart_date),least(End_Date,vEnd_date)) Date_Line
  6          from t0, t1, (select level cBeg from dual connect by level = 2)
  7         where end_date >= vStart_date
  8           and beg_date <= vEnd_date
  9         order by Date_line))
 10  select min(Start_D), max(End_D), List_id
 11  from (SELECT Start_D, End_D, listagg(id,', ') within group (order by id)  List_id
 12          from t0, t3
 13         where end_date >= Start_D
 14           and beg_date <= End_D
 15           and End_D is not null
 16         group by Start_D, End_D)
 17  group by List_id
 18  ;
MIN(START_D) MAX(END_D)  LIST_ID
------------ ----------- --------------------------------------------------------------------------------
01.07.2000   10.02.2001  5, 6


Вариант 3:
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
SQL> with t0 (id,beg_date, end_date) as(select 5, to_date('01.08.2000','dd.mm.yyyy') , to_date('01.11.2000','dd.mm.yyyy')  from dual union all
  2                                       select 6, to_date('01.07.2000','dd.mm.yyyy') , to_date('01.01.2002','dd.mm.yyyy')  from dual)
  3    ,t1 as (select  to_date('10.02.1999','dd.mm.yyyy') vStart_date, to_date('10.02.2099','dd.mm.yyyy') vEnd_date from dual)
  4    , t3 as(select Date_Line Start_D, lead(Date_Line) over(ORDER by Date_Line) End_D
  5    from (SELECT distinct decode(cBeg,1,greatest(Beg_date,vStart_date),least(End_Date,vEnd_date)) Date_Line
  6            from t0, t1, (select level cBeg from dual connect by level = 2)
  7           where end_date >= vStart_date
  8             and beg_date <= vEnd_date
  9           order by Date_line))
 10   SELECT Start_D, End_D, listagg(id,', ') within group (order by id)  List_id
 11      from t0, t3
 12     where end_date >= Start_D
 13        and beg_date <= End_D
 14        and End_D is not null
 15      group by Start_D, End_D;
START_D     END_D       LIST_ID
----------- ----------- --------------------------------------------------------------------------------
01.07.2000  01.08.2000  5, 6
01.08.2000  01.11.2000  5, 6
01.11.2000  01.01.2002  5, 6


То есть ни один из трех последних не дает тот же результат, что исходный вариант.
Но работает конечно несравнимо быстрее, немного поправили:
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
with t0 (id,beg_date, end_date) as (select 5 id, to_date('01082000', 'ddmmyyyy') beg_date, to_date('01112000', 'ddmmyyyy') end_date from dual union all            
                                    select 6 id, to_date('01072000', 'ddmmyyyy') beg_date, to_date('01012002', 'ddmmyyyy') end_date from dual)
 ,t1 as (select  to_date('10.02.1999','dd.mm.yyyy') vStart_date, to_date('10.02.2099','dd.mm.yyyy') vEnd_date from dual)
 ,t3 as (select Date_Line Start_D, lead(Date_Line) over(ORDER by Date_Line) End_D
           from (select distinct decode(cBeg,1, greatest(Beg_date,vStart_date), least(End_Date,vEnd_date)) Date_Line
                   from t0, t1, (select level cBeg from dual connect by level = 2)))
select Start_D, End_D, listagg(id,', ') within group (order by id)  List_id
  from t0, t3
 ==>where beg_date <= Start_D
  ==>and end_date >= End_D
   and End_D is not null
 group by Start_D, End_D


Немного лучше, но до исходного результата пока тоже не дотягивает, надо как-то хитрее формировать пограничные даты что ли.
stax..интересная фича, можно спрашивать на собеседованиях
select 2 ++ (-2) from dual;
Я б даже на такой вопрос ответил бы неверно (но это наверно баян, да еще может и из доки ))
...
Рейтинг: 0 / 0
25.05.2017, 21:47
    #39460006
dbms_photoshop
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение диапазонов (переписать start of group)
ацкийсотонаselect 2 ++ (-2) from dual;
Зачем так просто, лучше сразу спрашивать что вернет и почему результаты отличаются

Код: plsql
1.
2.
exec dbms_output.put_line(3**3+-3);
exec dbms_output.put_line(3**3+-+-3);
...
Рейтинг: 0 / 0
26.05.2017, 11:40
    #39460281
MaximaXXL
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение диапазонов (переписать start of group)
ацкийсотона,

Да, немного накосячил с <>, сорри

Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
with t0 (id,beg_date, end_date) as(select 5, to_date('01.08.2000','dd.mm.yyyy') , to_date('01.11.2000','dd.mm.yyyy')  from dual union all
                                      select 6, to_date('01.07.2000','dd.mm.yyyy') , to_date('01.01.2002','dd.mm.yyyy')  from dual)
   ,t1 as (select  to_date('10.02.1999','dd.mm.yyyy') vStart_date, to_date('10.02.2099','dd.mm.yyyy') vEnd_date from dual)
   , t3 as(select Date_Line Start_D, lead(Date_Line) over(ORDER by Date_Line) End_D
    from (SELECT distinct decode(cBeg,1,greatest(Beg_date,vStart_date),least(End_Date,vEnd_date)) Date_Line
            from t0, t1, (select level cBeg from dual connect by level = 2)
           where end_date >= vStart_date
              and beg_date <= vEnd_date
            order by Date_line))
   SELECT Start_D, End_D, listagg(id,', ') within group (order by id)  List_id
      from t0, t3
     where end_date <= Start_D
       and beg_date >= End_D
       and End_D is not null
     group by Start_D, End_D;



Но от условия в t3
Код: plsql
1.
2.
3.
           where end_date >= vStart_date
             and beg_date <= vEnd_date
          order by Date_line


я бы не отказывался, без него вся таблица попадет под выборку
...
Рейтинг: 0 / 0
26.05.2017, 16:36
    #39460568
MaximaXXL
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение диапазонов (переписать start of group)
ацкийсотона,

Вот еще Вариант 4 например:

Код: 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.
with t0 (id,beg_date, end_date) as(
select 1, to_date('01.02.1999','dd.mm.yyyy') , to_date('01.03.1999','dd.mm.yyyy') from dual union all
select 2, to_date('01.02.1999','dd.mm.yyyy') , to_date('01.03.1999','dd.mm.yyyy')  from dual union all
select 3, to_date('01.02.2000','dd.mm.yyyy') , to_date('01.03.2000','dd.mm.yyyy')  from dual union all
select 4, to_date('01.01.2000','dd.mm.yyyy') , to_date('01.12.2000','dd.mm.yyyy')  from dual union all
select 5, to_date('01.08.2000','dd.mm.yyyy') , to_date('01.11.2000','dd.mm.yyyy')  from dual union all

select 6, to_date('01.07.2000','dd.mm.yyyy') , to_date('01.01.2002','dd.mm.yyyy')  from dual )

--,t1 as (select  to_date('10.02.1999','dd.mm.yyyy') vStart_date, to_date('10.02.2010','dd.mm.yyyy') vEnd_date from dual)

, t3 as(select Date_Line Start_D, lead(Date_Line) over(ORDER by Date_Line) End_D
from (
SELECT distinct  
 decode(cBeg, 1, greatest(Beg_date,to_date(:vStart_date,'dd.mm.yyyy')),least(End_Date,to_date(:vEnd_date,'dd.mm.yyyy'))) Date_Line
from t0,  (select level cBeg from dual connect by level = 2)
where end_date >= to_date(:vStart_date,'dd.mm.yyyy')
  and beg_date <= to_date(:vEnd_date ,'dd.mm.yyyy')
order by Date_line)
)

select distinct decode(greatest(Beg_date,to_date(:vStart_date,'dd.mm.yyyy')),Start_D, Start_D, Start_D+1) SDate,decode(least(End_Date,to_date(:vEnd_date,'dd.mm.yyyy')),End_D,End_D,End_D-1) EDate, List_id
from (
SELECT Start_D, End_D, listagg(id,', ') within group (order by id)  List_id
from t0, t3
where beg_date <= Start_D
  and end_date >= End_D
  and End_D is not null
group by Start_D, End_D) m
left join t0 on (instr(', '||List_id||', ',', '||id||', ') >0 
             and (greatest(Beg_date,to_date(:vStart_date,'dd.mm.yyyy')) = Start_D 
               or least(End_Date,to_date(:vEnd_date,'dd.mm.yyyy')) = End_D)
               )

order by 1



отказался от t1, таки ввел переменные =)

попробуй такой, но по времени даже не скажу =( может и не лучший вариант.
была бы база - может и по другому бы сделал =)
...
Рейтинг: 0 / 0
26.05.2017, 18:02
    #39460617
stax..
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение диапазонов (переписать start of group)
MaximaXXL,
імхо хуже будет

зи
в т3 order by Date_line ?

.....
stax
...
Рейтинг: 0 / 0
26.05.2017, 18:08
    #39460622
stax..
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение диапазонов (переписать start of group)
ацкийсотонаНемного лучше, но до исходного результата пока тоже не дотягивает, надо как-то хитрее формировать пограничные даты что ли.


то что сразу вылезло, ето отлично

берете конкретную полку и конкретный товар по которому не сходится, и сличаете, ищете непредвиденный/неописанный нюанс

возможно в ісходном неправильно, и такое бывает

......
stax
...
Рейтинг: 0 / 0
26.05.2017, 18:36
    #39460639
stax..
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение диапазонов (переписать start of group)
ацкийсотонаНемного лучше, но до исходного результата пока тоже не дотягивает, надо как-то хитрее формировать пограничные даты что ли.

самое простое
товар лежал на полке один день
как учитываете?
Код: 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.
with t0 (id,beg_date, end_date) as(
select 5, to_date('01.08.2000','dd.mm.yyyy') , to_date('01.08.2000','dd.mm.yyyy')  from dual union all
select 6, to_date('01.08.2000','dd.mm.yyyy') , to_date('01.01.2002','dd.mm.yyyy')  from dual)
   ,t1 as (select  to_date('10.02.1999','dd.mm.yyyy') vStart_date, to_date('10.02.2099','dd.mm.yyyy') vEnd_date from dual)
   , t3 as(select Date_Line Start_D, lead(Date_Line) over(ORDER by Date_Line) End_D
    from (
   SELECT distinct
   decode(cBeg,1,greatest(Beg_date,vStart_date),least(End_Date,vEnd_date)) Date_Line
            from t0, t1, (select level cBeg from dual connect by level <= 2)
           where end_date >= vStart_date
              and beg_date <= vEnd_date
            order by Date_line
)
)
,t4 as (
   SELECT Start_D, End_D, listagg(id,', ') within group (order by id)  List_id
      from t0, t3
--     where end_date <= Start_D
--       and beg_date >= End_D
--       and End_D is not null     
       where end_date >= Start_D
         and beg_date <= End_D
         and End_D is not null
group by Start_D, End_D
order by Start_D
)
select * from t4
/
SQL> /

START_D    END_D      LIST_ID
---------- ---------- ----------------------------------------
01.08.2000 01.01.2002 5, 6



.....
stax
...
Рейтинг: 0 / 0
26.05.2017, 18:43
    #39460643
stax..
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение диапазонов (переписать start of group)
MaximaXXLацкийсотона,

Да, немного накосячил с <>, сорри




было луче, нарезанные кусочки t3 должны полностью входить в исхожные интервальчики

Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
SQL> ed
Wrote file afiedt.buf

  1  with t0 (id,beg_date, end_date) as(
  2  select 5, to_date('01.08.2000','dd.mm.yyyy') , to_date('01.08.2000','dd.mm.yyyy')  from dual union all
  3  select 6, to_date('01.08.2000','dd.mm.yyyy') , to_date('01.01.2002','dd.mm.yyyy')  from dual)
  4     ,t1 as (select  to_date('10.02.1999','dd.mm.yyyy') vStart_date, to_date('10.02.2099','dd.mm.yyyy') vEnd_date from dual)
  5     , t3 as(select Date_Line Start_D, lead(Date_Line) over(ORDER by Date_Line) End_D
  6      from (SELECT distinct decode(cBeg,1,greatest(Beg_date,vStart_date),least(End_Date,vEnd_date)) Date_Line
  7              from t0, t1, (select level cBeg from dual connect by level = 2)
  8             where end_date >= vStart_date
  9                and beg_date <= vEnd_date
 10              order by Date_line))
 11     SELECT Start_D, End_D, listagg(id,', ') within group (order by id)  List_id
 12        from t0, t3
 13       where end_date <= Start_D
 14         and beg_date >= End_D
 15         and End_D is not null
 16*      group by Start_D, End_D
 17  /

no rows selected



.....
stax
...
Рейтинг: 0 / 0
26.05.2017, 20:46
    #39460685
MaximaXXL
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение диапазонов (переписать start of group)
ацкийсотона,

Вариант 5 (модификация 4-ки).
Вернул t1 как входные параметра.
Сделал t2 как t0 с наложенными первичными условиями.
Код: 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.
with t0 (id,beg_date, end_date) as(
select 1, to_date('01.02.1999','dd.mm.yyyy') , to_date('01.03.1999','dd.mm.yyyy') from dual union all
select 2, to_date('01.02.1999','dd.mm.yyyy') , to_date('01.03.1999','dd.mm.yyyy')  from dual union all
select 3, to_date('01.02.2000','dd.mm.yyyy') , to_date('01.03.2000','dd.mm.yyyy')  from dual union all
select 4, to_date('01.01.2000','dd.mm.yyyy') , to_date('01.12.2000','dd.mm.yyyy')  from dual union all
select 5, to_date('01.08.2000','dd.mm.yyyy') , to_date('01.11.2000','dd.mm.yyyy')  from dual union all

select 6, to_date('01.07.2000','dd.mm.yyyy') , to_date('01.01.2002','dd.mm.yyyy')  from dual )

,t1 as (select  to_date('10.02.1999','dd.mm.yyyy') vStart_date, to_date('10.02.2010','dd.mm.yyyy') vEnd_date from dual)
,t2 as (select greatest(Beg_date,vStart_date) Beg_Date, least(End_Date,vEnd_date) End_Date, id
          from t0, t1
         where end_date >= vStart_date
           and beg_date <= vEnd_date
         )

, t3 as(select Date_Line Start_D, lead(Date_Line) over(ORDER by Date_Line) End_D
from ( SELECT distinct decode(cBeg, 1, Beg_date, End_Date) Date_Line
         from t2,  (select level cBeg from dual connect by level = 2)
     order by Date_line
     )
       )

select distinct decode(Beg_date,Start_D, Start_D, Start_D+1) SDate,decode(End_Date,End_D,End_D,End_D-1) EDate, List_id
from (SELECT Start_D, End_D, listagg(id,', ') within group (order by id)  List_id
        from t2, t3
       where beg_date <= Start_D
         and end_date >= End_D
         and End_D is not null
    group by Start_D, End_D) m
left join t2 on (instr(', '||List_id||', ',', '||id||', ') >0 
                 and (Beg_date = Start_D 
                      or End_Date = End_D)
                )
order by 1
...
Рейтинг: 0 / 0
Форумы / Oracle [игнор отключен] [закрыт для гостей] / Пересечение диапазонов (переписать start of group) / 24 сообщений из 24, страница 1 из 1
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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