powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Oracle [игнор отключен] [закрыт для гостей] / Пересечение диапазонов (переписать start of group)
24 сообщений из 24, страница 1 из 1
Пересечение диапазонов (переписать start of group)
    #39459204
Задача условно.
На полке склада лежат товары, но период каждого товара учитывается отдельно.
То есть:
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
Пересечение диапазонов (переписать start of group)
    #39459208
ацкийсотонаТ.е. если два диапазона пересекаются (но не четко по границам), на выходе в любом случае получаем уже три записи
Не записи 3, а 3 диапазона, а записей будет от 3 до 4 (1-2 для каждого из id в пересечении и по одной исходной с границами, отредактированными с учетом пересечения).
...
Рейтинг: 0 / 0
Пересечение диапазонов (переписать start of group)
    #39459245
ацкийсотона,

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

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

похожая тема
...
Рейтинг: 0 / 0
Пересечение диапазонов (переписать start of group)
    #39459272
Добрый Э - Эхпохожая тема
Смотрел по результатам примеров - не нашел там агрегации при схлопывании.
Сейчас вижу так, разворачиваем на точки, потом идем по списку и в зависимости от типа даты либо добавляем Id в список, либо удаляем (но тогда запрос видимо должен быть рекурсивным и еще список id должен быть отсортирован, дабы проще в дальнейшем искать изменения в результирующем наборе), потом схлопываем.
...
Рейтинг: 0 / 0
Пересечение диапазонов (переписать start of group)
    #39459333
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.
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
Пересечение диапазонов (переписать start of group)
    #39459379
stax..
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Добрый Э - Эхацкийсотона,

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

.....
stax
...
Рейтинг: 0 / 0
Пересечение диапазонов (переписать start of group)
    #39459410
stax..
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ацкийсотонаЗадача условно.
На полке склада лежат товары, но период каждого товара учитывается отдельно.
То есть:
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
Пересечение диапазонов (переписать start of group)
    #39459421
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.
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
Пересечение диапазонов (переписать start of group)
    #39459423
stax..
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
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
Пересечение диапазонов (переписать start of group)
    #39459438
MaximaXXL
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
stax..,

вводил t3 больше для понимания/тестирования (чтоб не перегружать один селект) ...
как его реализовать через union или "моим хитрым" =) тут уже автор пусть тестонет по скорости
...
Рейтинг: 0 / 0
Пересечение диапазонов (переписать start of group)
    #39459471
stax..
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
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
Пересечение диапазонов (переписать start of group)
    #39459556
MaximaXXL
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
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
Пересечение диапазонов (переписать start of group)
    #39459566
stax..
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
MaximaXXLstax..,

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




select level cBeg from dual connect by level = 2;

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

.....
stax
...
Рейтинг: 0 / 0
Пересечение диапазонов (переписать start of group)
    #39459991
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
Пересечение диапазонов (переписать start of group)
    #39460006
Фотография dbms_photoshop
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ацкийсотона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
Пересечение диапазонов (переписать start of group)
    #39460281
MaximaXXL
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ацкийсотона,

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

Код: 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
Пересечение диапазонов (переписать start of group)
    #39460568
MaximaXXL
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ацкийсотона,

Вот еще Вариант 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
Пересечение диапазонов (переписать start of group)
    #39460617
stax..
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
MaximaXXL,
імхо хуже будет

зи
в т3 order by Date_line ?

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


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

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

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

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

самое простое
товар лежал на полке один день
как учитываете?
Код: 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
Пересечение диапазонов (переписать start of group)
    #39460643
stax..
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
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
Пересечение диапазонов (переписать start of group)
    #39460685
MaximaXXL
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ацкийсотона,

Вариант 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
24 сообщений из 24, страница 1 из 1
Форумы / Oracle [игнор отключен] [закрыт для гостей] / Пересечение диапазонов (переписать start of group)
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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