Имя: Пароль:
1C
1С v8
Пересечение периодов в запросе.
, ,
0 qwasqu
 
24.11.16
13:50
Здравствуйте.

Есть таблица, например:
01.01.16 - 10.03.16
01.02.16 - 20.03.16
30.03.16 - 15.04.16
10.04.16 - 12.04.16
11.04.16 - 25.04.16
30.04-16 - 10.05.16

В результате они должны схлопнуться в:
01.01.16 - 20.03.16
30.03.16 - 25.04.16
30.04-16 - 10.05.16

Вообще таблица может быть какой угодно.

Я не нашел способа в запросе схлопнуть ее.

Подскажите, если кто сталкивался. Спасибо.
1 Злопчинский
 
24.11.16
13:54
задача определения пересечения периодов подробно рассмотрена на ИС у Ильдаровича - посмотри там, может найдешь полезное
2 qwasqu
 
24.11.16
14:09
(1) К сожалению у него не то, там он выбирает периоды, которые содержат дату.

ВЫБРАТЬ * ИЗ Дано
ГДЕ &МоментВремени МЕЖДУ НачалоИнтервала И КонецИнтервала

оптимизирует данный запрос.
3 azernot
 
24.11.16
14:12
ВЫБРАТЬ
    ДАТАВРЕМЯ(2016, 1, 1) КАК ДатаНачала,
    ДАТАВРЕМЯ(2016, 3, 10) КАК ДатаОкончания
ПОМЕСТИТЬ ТаблицаПериодов

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    ДАТАВРЕМЯ(2016, 2, 1),
    ДАТАВРЕМЯ(2016, 3, 20)

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    ДАТАВРЕМЯ(2016, 3, 30),
    ДАТАВРЕМЯ(2016, 4, 15)

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    ДАТАВРЕМЯ(2016, 4, 10),
    ДАТАВРЕМЯ(2016, 4, 12)

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    ДАТАВРЕМЯ(2016, 4, 11),
    ДАТАВРЕМЯ(2016, 4, 25)

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    ДАТАВРЕМЯ(2016, 4, 30),
    ДАТАВРЕМЯ(2016, 5, 10)
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
    ДатыНачала.ДатаНачала КАК ДатаНачала,
    МАКСИМУМ(ВЫБОР
            КОГДА ДатыНачала.ДатаОкончания > ДатыОкончания.ДатаОкончания
                ТОГДА ДатыНачала.ДатаОкончания
            ИНАЧЕ ДатыОкончания.ДатаОкончания
        КОНЕЦ) КАК ДатаОкончания
ПОМЕСТИТЬ ДатыНачалаСМаксимальнойДатойОкончания
ИЗ
    ТаблицаПериодов КАК ДатыНачала
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ ТаблицаПериодов КАК ДатыОкончания
        ПО ДатыНачала.ДатаНачала <= ДатыОкончания.ДатаНачала
            И ДатыНачала.ДатаОкончания >= ДатыОкончания.ДатаНачала

СГРУППИРОВАТЬ ПО
    ДатыНачала.ДатаНачала
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
    МИНИМУМ(ДатыНачала.ДатаНачала) КАК ДатаНачала,
    ДатыОкончания.ДатаОкончания
ИЗ
    ДатыНачалаСМаксимальнойДатойОкончания КАК ДатыНачала
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ ДатыНачалаСМаксимальнойДатойОкончания КАК ДатыОкончания
        ПО ДатыНачала.ДатаОкончания >= ДатыОкончания.ДатаНачала
            И ДатыНачала.ДатаНачала <= ДатыОкончания.ДатаНачала

СГРУППИРОВАТЬ ПО
    ДатыОкончания.ДатаОкончания

УПОРЯДОЧИТЬ ПО
    ДатаНачала
4 azernot
 
24.11.16
14:14
(3) + Не, облажался, не универсальный запрос
5 newbling
 
24.11.16
14:16
я делал недавно.

        |    И ВЫБОР
        |            КОГДА TopSports_СкидкаВЗависимостиОтВеличиныОборота.ПериодОкончанияДействия = ДАТАВРЕМЯ(1, 1, 1)
        |                ТОГДА ВЫБОР
        |                        КОГДА &ПериодОкончанияДействия = ДАТАВРЕМЯ(1, 1, 1)
        |                            ТОГДА ИСТИНА
        |                        ИНАЧЕ TopSports_СкидкаВЗависимостиОтВеличиныОборота.Период <= &ПериодОкончанияДействия
        |                    КОНЕЦ
        |            ИНАЧЕ ВЫБОР
        |                    КОГДА &ПериодОкончанияДействия = 0
        |                        ТОГДА TopSports_СкидкаВЗависимостиОтВеличиныОборота.ПериодОкончанияДействия >= &Период
        |                    ИНАЧЕ TopSports_СкидкаВЗависимостиОтВеличиныОборота.ПериодОкончанияДействия >= &Период
        |                            И TopSports_СкидкаВЗависимостиОтВеличиныОборота.Период <= &ПериодОкончанияДействия
        |                КОНЕЦ
        |        КОНЕЦ
6 newbling
 
24.11.16
14:17
с учётом правой границы периода до бесконечности
7 newbling
 
24.11.16
14:18
Если не учитывать, то достаточно
ПериодОкончанияДействия >= &Периодначала И ПериодНачала <= &ПериодОкончанияДействия
8 d546
 
24.11.16
14:18
(0) в исходных есть гарантия, что начало периода больше конца периода?
9 newbling
 
24.11.16
14:20
(5) (6) (7) пардон, задача немного другая
10 newbling
 
24.11.16
14:23
Хм, кажется, что проще циклом пробежаться, а ни в запросе мудрить.
11 qwasqu
 
24.11.16
14:23
(8) В таблице левая дата всегда меньше правой.
12 newbling
 
24.11.16
14:26
Я бы просто цикл сделал с конца.
13 Ildarovich
 
24.11.16
14:29
В статье http://catalog.mista.ru/public/460935/ есть задача 25,
а в статье http://catalog.mista.ru/public/306536/ - задача 14, которые ИМЕЮТ ОТНОШЕНИЕ к рассматриваемому вопросу.

Проблема задачи (0) в том, что периоды могут пересекаться, а 14 и 25 на это не рассчитаны.

Поэтому я бы сделал так:
сначала соединением с таблицей дат периода (получить ее можно многими способами, хоть из календаря) нашел все даты без повторений, а затем применил бы решение из 14.
14 newbling
 
24.11.16
14:34
(13) по быстродействию быстрее выгрузить, отработать ручками в цикле, а потом опять грузануть. И логически проще.
15 Ildarovich
 
24.11.16
14:49
Вот все решение:
ВЫБРАТЬ
    ДАТАВРЕМЯ(2016, 1, 1) КАК От,
    ДАТАВРЕМЯ(2016, 3, 10) КАК До
ПОМЕСТИТЬ Интервалы

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    ДАТАВРЕМЯ(2016, 2, 1),
    ДАТАВРЕМЯ(2016, 3, 20)

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    ДАТАВРЕМЯ(2016, 3, 30),
    ДАТАВРЕМЯ(2016, 4, 15)

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    ДАТАВРЕМЯ(2016, 4, 10),
    ДАТАВРЕМЯ(2016, 4, 12)

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    ДАТАВРЕМЯ(2016, 4, 11),
    ДАТАВРЕМЯ(2016, 4, 25)

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    ДАТАВРЕМЯ(2016, 4, 30),
    ДАТАВРЕМЯ(2016, 5, 10)
;

ВЫБРАТЬ
    0 КАК Х
ПОМЕСТИТЬ Х_4

ОБЪЕДИНИТЬ

ВЫБРАТЬ
    1

ОБЪЕДИНИТЬ

ВЫБРАТЬ
    2

ОБЪЕДИНИТЬ

ВЫБРАТЬ
    3
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    4 * Инь.Х + Янь.Х КАК Х
ПОМЕСТИТЬ Х_16
ИЗ
    Х_4 КАК Инь,
    Х_4 КАК Янь
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
    ДОБАВИТЬКДАТЕ(Интервалы.От, ДЕНЬ, 16 * Инь.Х + Янь.Х) КАК Дата
ПОМЕСТИТЬ ВТДаты
ИЗ
    Интервалы КАК Интервалы,
    Х_16 КАК Инь,
    Х_16 КАК Янь
ГДЕ
    ДОБАВИТЬКДАТЕ(Интервалы.От, ДЕНЬ, 16 * Инь.Х + Янь.Х) <= Интервалы.До
;

ВЫБРАТЬ
    Даты.Дата,
    КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ДатыДо.Дата) КАК Номер
ПОМЕСТИТЬ НомераДат
ИЗ
    ВТДаты КАК Даты
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТДаты КАК ДатыДо
        ПО (ДатыДо.Дата <= Даты.Дата)

СГРУППИРОВАТЬ ПО
    Даты.Дата
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    МИНИМУМ(НомераДат.Дата) КАК От,
    МАКСИМУМ(НомераДат.Дата) КАК До
ИЗ
    НомераДат КАК НомераДат

СГРУППИРОВАТЬ ПО
    ДОБАВИТЬКДАТЕ(НомераДат.Дата, ДЕНЬ, -НомераДат.Номер)
16 azernot
 
24.11.16
14:50
Вот запрос

ВЫБРАТЬ
    ДАТАВРЕМЯ(2016, 1, 1) КАК ДатаНачала,
    ДАТАВРЕМЯ(2016, 3, 10) КАК ДатаОкончания
ПОМЕСТИТЬ ТаблицаПериодов

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    ДАТАВРЕМЯ(2016, 2, 1),
    ДАТАВРЕМЯ(2016, 3, 20)

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    ДАТАВРЕМЯ(2016, 3, 30),
    ДАТАВРЕМЯ(2016, 4, 15)

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    ДАТАВРЕМЯ(2016, 4, 10),
    ДАТАВРЕМЯ(2016, 4, 12)

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    ДАТАВРЕМЯ(2016, 4, 11),
    ДАТАВРЕМЯ(2016, 4, 25)

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    ДАТАВРЕМЯ(2016, 4, 30),
    ДАТАВРЕМЯ(2016, 5, 10)
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
    ДатыНачала.ДатаНачала КАК ДатаНачала,
    МАКСИМУМ(ВЫБОР
            КОГДА ДатыНачала.ДатаОкончания > ДатыОкончания.ДатаОкончания
                ТОГДА ДатыНачала.ДатаОкончания
            ИНАЧЕ ДатыОкончания.ДатаОкончания
        КОНЕЦ) КАК ДатаОкончания
ПОМЕСТИТЬ ДатыНачалаСМаксимальнойДатойОкончания
ИЗ
    ТаблицаПериодов КАК ДатыНачала
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ ТаблицаПериодов КАК ДатыОкончания
        ПО ДатыНачала.ДатаНачала <= ДатыОкончания.ДатаНачала
            И ДатыНачала.ДатаОкончания >= ДатыОкончания.ДатаНачала

СГРУППИРОВАТЬ ПО
    ДатыНачала.ДатаНачала
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
    МИНИМУМ(ВЫБОР
            КОГДА ДатыОкончания.ДатаНачала < ДатыНачала.ДатаНачала
                ТОГДА ДатыОкончания.ДатаНачала
            ИНАЧЕ ДатыНачала.ДатаНачала
        КОНЕЦ) КАК ДатаНачала,
    ДатыОкончания.ДатаОкончания КАК ДатаОкончания
ПОМЕСТИТЬ ДатаОкончанияСМинимальнойДатойНачала
ИЗ
    ДатыНачалаСМаксимальнойДатойОкончания КАК ДатыОкончания
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ ДатыНачалаСМаксимальнойДатойОкончания КАК ДатыНачала
        ПО ДатыОкончания.ДатаНачала <= ДатыНачала.ДатаОкончания
            И ДатыОкончания.ДатаОкончания >= ДатыНачала.ДатаОкончания

СГРУППИРОВАТЬ ПО
    ДатыОкончания.ДатаОкончания
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ДатаОкончанияСМинимальнойДатойНачала.ДатаНачала,
    ДатаОкончанияСМинимальнойДатойНачала.ДатаОкончания
ИЗ
    ДатаОкончанияСМинимальнойДатойНачала КАК ДатаОкончанияСМинимальнойДатойНачала
        ЛЕВОЕ СОЕДИНЕНИЕ ДатаОкончанияСМинимальнойДатойНачала КАК ДатаОкончанияСМинимальнойДатойНачала1
        ПО (ДатаОкончанияСМинимальнойДатойНачала.ДатаНачала > ДатаОкончанияСМинимальнойДатойНачала1.ДатаНачала
                    И ДатаОкончанияСМинимальнойДатойНачала.ДатаОкончания < ДатаОкончанияСМинимальнойДатойНачала1.ДатаОкончания
                ИЛИ ДатаОкончанияСМинимальнойДатойНачала.ДатаНачала >= ДатаОкончанияСМинимальнойДатойНачала1.ДатаНачала
                    И ДатаОкончанияСМинимальнойДатойНачала.ДатаОкончания < ДатаОкончанияСМинимальнойДатойНачала1.ДатаОкончания
                ИЛИ ДатаОкончанияСМинимальнойДатойНачала.ДатаНачала > ДатаОкончанияСМинимальнойДатойНачала1.ДатаНачала
                    И ДатаОкончанияСМинимальнойДатойНачала.ДатаОкончания <= ДатаОкончанияСМинимальнойДатойНачала1.ДатаОкончания)
ГДЕ
    ДатаОкончанияСМинимальнойДатойНачала1.ДатаНачала ЕСТЬ NULL

УПОРЯДОЧИТЬ ПО
    ДатаОкончанияСМинимальнойДатойНачала.ДатаНачала


Я вроде бы дыр не нашёл
17 azernot
 
24.11.16
14:52
(15) Добавь период с 01.01.16 по 31.12.16.
Не сработает.
18 d546
 
24.11.16
15:00
ВЫБРАТЬ
    Данные.НачалоПериода,
    Данные.КонецПериода
ПОМЕСТИТЬ ВТДанные
ИЗ
    &Данные КАК Данные
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Данные.НачалоПериода,
    Данные.КонецПериода
ПОМЕСТИТЬ ВТБезПоглощаемых
ИЗ
    ВТДанные КАК Данные
        ЛЕВОЕ СОЕДИНЕНИЕ ВТДанные КАК Данные2
        ПО Данные.НачалоПериода > Данные2.НачалоПериода
            И Данные.КонецПериода < Данные2.КонецПериода
ГДЕ
    Данные2.НачалоПериода ЕСТЬ NULL
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
    ЕСТЬNULL(НижняяГраница.НачалоПериода, БезПоглощаемых.НачалоПериода) КАК НачалоПериода,
    ЕСТЬNULL(ВерхняяГраница.КонецПериода, БезПоглощаемых.КонецПериода) КАК КонецПериода
ИЗ
    ВТБезПоглощаемых КАК БезПоглощаемых
        ЛЕВОЕ СОЕДИНЕНИЕ ВТБезПоглощаемых КАК ВерхняяГраница
        ПО БезПоглощаемых.НачалоПериода <= ВерхняяГраница.НачалоПериода
            И БезПоглощаемых.КонецПериода >= ВерхняяГраница.НачалоПериода
            И БезПоглощаемых.КонецПериода < ВерхняяГраница.КонецПериода
        ЛЕВОЕ СОЕДИНЕНИЕ ВТБезПоглощаемых КАК НижняяГраница
        ПО БезПоглощаемых.НачалоПериода <= НижняяГраница.КонецПериода
            И БезПоглощаемых.КонецПериода >= НижняяГраница.КонецПериода
            И БезПоглощаемых.НачалоПериода > НижняяГраница.НачалоПериода
19 d546
 
24.11.16
15:01
как то так
20 Ildarovich
 
24.11.16
15:03
(17) Ну да, там максимум - 256 дней в периоде.
21 azernot
 
24.11.16
15:04
(18) Добавь период 01.01.16 - 30.03.16
Не сравботает
22 d546
 
24.11.16
15:06
(21) :)
23 Ildarovich
 
24.11.16
18:42
+(20) Ограничение 256 дат легко преодолеть, нарастив искусственную таблицу чисел. Если же вообще без нее, то вот вариант с использованием нарастающего итога:ВЫБРАТЬ
    ВЗ.От,
    СУММА(ВЗ.Знак) КАК Знак
ПОМЕСТИТЬ Концы
ИЗ
    (ВЫБРАТЬ
        Интервалы.От КАК От,
        1 КАК Знак
    ИЗ
        Интервалы КАК Интервалы
    
    ОБЪЕДИНИТЬ ВСЕ
    
    ВЫБРАТЬ
        Интервалы.До,
        -1
    ИЗ
        Интервалы КАК Интервалы) КАК ВЗ

СГРУППИРОВАТЬ ПО
    ВЗ.От
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Позже.От
ПОМЕСТИТЬ Края
ИЗ
    Концы КАК Позже
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ Концы КАК Раньше
        ПО (Раньше.От <= Позже.От)

СГРУППИРОВАТЬ ПО
    Позже.От,
    Позже.Знак

ИМЕЮЩИЕ
    (СУММА(Раньше.Знак) = 0
        ИЛИ СУММА(Раньше.Знак) = Позже.Знак)
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    МИНИМУМ(ВЗ.От) КАК От,
    МАКСИМУМ(ВЗ.От) КАК До
ИЗ
    (ВЫБРАТЬ
        КОЛИЧЕСТВО(Раньше.От) КАК Номер,
        Позже.От КАК От
    ИЗ
        Края КАК Позже
            ВНУТРЕННЕЕ СОЕДИНЕНИЕ Края КАК Раньше
            ПО (Раньше.От <= Позже.От)
    
    СГРУППИРОВАТЬ ПО
        Позже.От) КАК ВЗ

СГРУППИРОВАТЬ ПО
    ВЫРАЗИТЬ(ВЗ.Номер / 2 КАК ЧИСЛО(10, 0))


На четыре строчки длиннее, чем (16), нужно подсократить. Но зато показывает, что задачу можно решить за линейное время, если использовать идеи из http://catalog.mista.ru/public/201526/ .
24 Ildarovich
 
25.11.16
20:33
+(23) Вот самый короткий на текущий момент вариант. На 12 строчек короче, чем (16). Крайние точки выделяются условием несоединяемости не с каким интервалом, затем, в последнем запросе, они "спариваются":ВЫБРАТЬ РАЗЛИЧНЫЕ
    Край.От КАК От
ПОМЕСТИТЬ Края
ИЗ
    Интервалы КАК Край
        ЛЕВОЕ СОЕДИНЕНИЕ Интервалы КАК Интервалы
        ПО (Интервалы.От < Край.От)
            И Край.От <= Интервалы.До
ГДЕ
    Интервалы.От ЕСТЬ NULL

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ РАЗЛИЧНЫЕ
    Край.До
ИЗ
    Интервалы КАК Край
        ЛЕВОЕ СОЕДИНЕНИЕ Интервалы КАК Интервалы
        ПО (Интервалы.От <= Край.До)
            И Край.До < Интервалы.До
ГДЕ
    Интервалы.От ЕСТЬ NULL
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    МИНИМУМ(Края.От) КАК От,
    МАКСИМУМ(Края.От) КАК До
ИЗ
    (ВЫБРАТЬ
        КОЛИЧЕСТВО(Раньше.От) КАК Номер,
        Позже.От КАК От
    ИЗ
        Края КАК Позже
            ВНУТРЕННЕЕ СОЕДИНЕНИЕ Края КАК Раньше
            ПО (Раньше.От <= Позже.От)
    
    СГРУППИРОВАТЬ ПО
        Позже.От) КАК Края

СГРУППИРОВАТЬ ПО
    ВЫРАЗИТЬ(Края.Номер / 2 КАК ЧИСЛО(10, 0))
25 Ildarovich
 
25.11.16
21:16
+(24) Можно еще на шесть строк короче:ВЫБРАТЬ РАЗЛИЧНЫЕ
    Край.От
ПОМЕСТИТЬ ЛевыеКрая
ИЗ
    Интервалы КАК Край
        ЛЕВОЕ СОЕДИНЕНИЕ Интервалы КАК Интервалы
        ПО (Интервалы.От < Край.От)
            И Край.От <= Интервалы.До
ГДЕ
    Интервалы.От ЕСТЬ NULL
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
    Край.До
ПОМЕСТИТЬ ПравыеКрая
ИЗ
    Интервалы КАК Край
        ЛЕВОЕ СОЕДИНЕНИЕ Интервалы КАК Интервалы
        ПО (Интервалы.От <= Край.До)
            И Край.До < Интервалы.До
ГДЕ
    Интервалы.От ЕСТЬ NULL
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ЛевыеКрая.От,
    МИНИМУМ(ПравыеКрая.До) КАК До
ИЗ
    ЛевыеКрая КАК ЛевыеКрая
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ ПравыеКрая КАК ПравыеКрая
        ПО ЛевыеКрая.От <= ПравыеКрая.До

СГРУППИРОВАТЬ ПО
    ЛевыеКрая.От
26 Лефмихалыч
 
25.11.16
21:19
Ildarovich, снимаю шляпу, замерев в глубоком пардоне.
Я серьезно - мне нравятся эти штуки-дрюки с запросами, которые ты делаешь. Это офигенно! :)
27 Garykom
 
гуру
25.11.16
21:29
(26) +1

ЗЫ Моя тоже сильно уважать умеющих делать "операции на гландах автогеном"...