Имя: Пароль:
1C
1С v8
За период получить количество дней, в которые проходило хотя бы одно мероприятие
, , ,
0 presto29
 
12.02.18
00:32
Есть регистр сведений - непериодический и без регистратора. Одно измерение "Мероприятие" (справочник) и два ресурса "ДатаНачала" и "ДатаОкончания". В регистре хранятся записи о проводимых мероприятиях, которые во времени могут пересекаться, накладываться и т.д.

Задача: Нужно ЗАПРОСОМ за период получить количество дней, в которые проходило хотя бы одно мероприятие.

P.S. Мероприятие может начаться в прошлом месяце и переходить в текущий, также может начинаться в текущем и уходить в следующий, а может вообще начаться в прошлом и длиться аж до следующего.

Например: Если за выбранный период проходило два мероприятия, в один и тот же период, длительностью три дня, то запрос должен вернуть «3». А если эти мероприятия проходили в разное время и не пересекались, то запрос должен вернуть «6».
1 dmg515
 
12.02.18
00:37
Выбрать все даты за требуемый период из регламентированного календаря, соединить внутренним соединением с РС по Дата <=РС.ДатаОкончания И Дата>=РС.ДатаНачала
2 presto29
 
12.02.18
00:40
спасибо (1)
3 Tateossian
 
12.02.18
00:59
Вот:

ВЫБРАТЬ
    "Мероприятие: пьянка" Мероприятие, ДАТАВРЕМЯ(2018,1,9) НачДата, ДАТАВРЕМЯ(2018,1,11) КонДата
ПОМЕСТИТЬ ГлавТаблица
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
    "Корпоратив" Мероприятие, ДАТАВРЕМЯ(2018,1,8) НачДата, ДАТАВРЕМЯ(2018,1,15) КонДата
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
    "Дедлайн" Мероприятие, ДАТАВРЕМЯ(2018,1,17) НачДата, ДАТАВРЕМЯ(2018,2,15) КонДата;
    
ВЫБРАТЬ
    ДОБАВИТЬКДАТЕ(&НачалоПериода, ДЕНЬ, aa.a * 1000 + bb.b * 100 + cc.c * 10 + dd.d) КАК Период
ПОМЕСТИТЬ Периоды
ИЗ
    (ВЫБРАТЬ
        0 КАК a
    ОБЪЕДИНИТЬ
    ВЫБРАТЬ
        1
    ОБЪЕДИНИТЬ
    ВЫБРАТЬ
        2
    ОБЪЕДИНИТЬ
    ВЫБРАТЬ
        3
    ОБЪЕДИНИТЬ
    ВЫБРАТЬ
        4
    ОБЪЕДИНИТЬ
    ВЫБРАТЬ
        5
    ОБЪЕДИНИТЬ
    ВЫБРАТЬ
        6
    ОБЪЕДИНИТЬ
    ВЫБРАТЬ
        7
    ОБЪЕДИНИТЬ
    ВЫБРАТЬ
        8
    ОБЪЕДИНИТЬ
    ВЫБРАТЬ
        9) КАК aa,
    (ВЫБРАТЬ
        0 КАК b
    ОБЪЕДИНИТЬ
    ВЫБРАТЬ
        1
    ОБЪЕДИНИТЬ
    ВЫБРАТЬ
        2
    ОБЪЕДИНИТЬ
    ВЫБРАТЬ
        3
    ОБЪЕДИНИТЬ
    ВЫБРАТЬ
        4
    ОБЪЕДИНИТЬ
    ВЫБРАТЬ
        5
    ОБЪЕДИНИТЬ
    ВЫБРАТЬ
        6
    ОБЪЕДИНИТЬ
    ВЫБРАТЬ
        7
    ОБЪЕДИНИТЬ
    ВЫБРАТЬ
        8
    ОБЪЕДИНИТЬ
    ВЫБРАТЬ
        9) КАК bb,
    (ВЫБРАТЬ
        0 КАК c
    ОБЪЕДИНИТЬ
    ВЫБРАТЬ
        1
    ОБЪЕДИНИТЬ
    ВЫБРАТЬ
        2
    ОБЪЕДИНИТЬ
    ВЫБРАТЬ
        3
    ОБЪЕДИНИТЬ
    ВЫБРАТЬ
        4
    ОБЪЕДИНИТЬ
    ВЫБРАТЬ
        5
    ОБЪЕДИНИТЬ
    ВЫБРАТЬ
        6
    ОБЪЕДИНИТЬ
    ВЫБРАТЬ
        7
    ОБЪЕДИНИТЬ
    ВЫБРАТЬ
        8
    ОБЪЕДИНИТЬ
    ВЫБРАТЬ
        9) КАК cc,
    (ВЫБРАТЬ
        0 КАК d
    ОБЪЕДИНИТЬ
    ВЫБРАТЬ
        1
    ОБЪЕДИНИТЬ
    ВЫБРАТЬ
        2
    ОБЪЕДИНИТЬ
    ВЫБРАТЬ
        3
    ОБЪЕДИНИТЬ
    ВЫБРАТЬ
        4
    ОБЪЕДИНИТЬ
    ВЫБРАТЬ
        5
    ОБЪЕДИНИТЬ
    ВЫБРАТЬ
        6
    ОБЪЕДИНИТЬ
    ВЫБРАТЬ
        7
    ОБЪЕДИНИТЬ
    ВЫБРАТЬ
        8
    ОБЪЕДИНИТЬ
    ВЫБРАТЬ
        9) КАК dd
ГДЕ
    aa.a * 1000 + bb.b * 100 + cc.c * 10 + dd.d <= РАЗНОСТЬДАТ(&НачалоПериода, &КонецПериода, ДЕНЬ);
    
ВЫБРАТЬ
    Периоды.Период,
    ГлавТаблица.Мероприятие
ПОМЕСТИТЬ ВсеДниМероприятий
ИЗ
    ПЕРИОДЫ КАК ПЕРИОДЫ ЛЕВОЕ СОЕДИНЕНИЕ ГлавТаблица ГлавТаблица ПО (Периоды.Период МЕЖДУ ГлавТаблица.НачДата И ГлавТаблица.КонДата)
ГДЕ НЕ ГлавТаблица.Мероприятие ЕСТЬ NULL ;

ВЫБРАТЬ
    КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Период)
ИЗ
    ВсеДниМероприятий
4 Fram
 
12.02.18
02:41
ВЫБРАТЬ
    Мероприятия.Мероприятие КАК Мероприятие,
    ВЫБОР
        КОГДА Мероприятия.ДатаНачала < &НачДата
            ТОГДА &НачДата
        ИНАЧЕ Мероприятия.ДатаНачала
    КОНЕЦ КАК ДатаНачала,
    ВЫБОР
        КОГДА Мероприятия.ДатаОкончания > &КонДата
            ТОГДА &КонДата
        ИНАЧЕ Мероприятия.ДатаОкончания
    КОНЕЦ КАК ДатаОкончания
ПОМЕСТИТЬ МероприятияЗаПериод
ИЗ
    РегистрСведений.Мероприятия КАК Мероприятия
ГДЕ
    Мероприятия.ДатаОкончания >= &НачДата
    И Мероприятия.ДатаНачала <= &КонДата
;

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

СГРУППИРОВАТЬ ПО
    Мероприятия.ДатаНачала,
    Мероприятия.ДатаОкончания
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    СУММА(ДнейВрем.Дней) КАК Дней
ИЗ
    ДнейВрем КАК ДнейВрем
5 Fram
 
12.02.18
02:42
(4) не проверял, но должно сработать
6 Fram
 
12.02.18
02:46
(6) 2 подзапрос надо сгруппировать по Мероприятия.Мероприятие. конструктор подвел
7 Fram
 
12.02.18
03:23
все таки, налажал. вот так будет правильнее

ВЫБРАТЬ РАЗЛИЧНЫЕ
    ВЫБОР
        КОГДА Мероприятия.ДатаНачала < &НачДата
            ТОГДА &НачДата
        ИНАЧЕ Мероприятия.ДатаНачала
    КОНЕЦ КАК ДатаНачала,
    ВЫБОР
        КОГДА Мероприятия.ДатаОкончания > &КонДата
            ТОГДА &КонДата
        ИНАЧЕ Мероприятия.ДатаОкончания
    КОНЕЦ КАК ДатаОкончания
ПОМЕСТИТЬ Периоды
ИЗ
    РегистрСведений.Мероприятия КАК Мероприятия
ГДЕ
    Мероприятия.ДатаОкончания >= &НачДата
    И Мероприятия.ДатаНачала <= &КонДата
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВЫБОР
        КОГДА МИНИМУМ(ПериодыДоп.ДатаНачала) ЕСТЬ NULL
            ТОГДА 1 + РАЗНОСТЬДАТ(Периоды.ДатаНачала, Периоды.ДатаОкончания, ДЕНЬ)
        КОГДА МИНИМУМ(ПериодыДоп.ДатаНачала) <= Периоды.ДатаНачала
            ТОГДА 0
        ИНАЧЕ РАЗНОСТЬДАТ(Периоды.ДатаНачала, МИНИМУМ(ПериодыДоп.ДатаНачала), ДЕНЬ)
    КОНЕЦ КАК Дней
ПОМЕСТИТЬ ДнейВрем
ИЗ
    Периоды КАК Периоды
        ЛЕВОЕ СОЕДИНЕНИЕ Периоды КАК ПериодыДоп
        ПО (Периоды.ДатаОкончания МЕЖДУ ПериодыДоп.ДатаНачала И ПериодыДоп.ДатаОкончания)

СГРУППИРОВАТЬ ПО
    Периоды.ДатаНачала,
    Периоды.ДатаОкончания
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    СУММА(ДнейВрем.Дней) КАК Дней
ИЗ
    ДнейВрем КАК ДнейВрем
8 presto29
 
13.02.18
23:37
Tateossian (3) и Fram (4),(7), спасибо огромное за поддержку и подробный код!!! ;)
9 presto29
 
16.02.18
03:47
(7) не считает, а если править условие, то выдает общее количество.
Так как выбрать различные относиться уже просто к вычетанию между датами.
вот как выбрать пересечение множеств без повторений?
10 Fram
 
16.02.18
06:50
(9) да, похоже, не додумал немного.. давай, сам. там немного осталось уже.
или (3) юзай
11 Ildarovich
 
17.02.18
19:18
Вот такой вариант будет работать без использования искусственной таблицы дат либо производственного календаря и в случае, когда занятость требуется с точностью до секунд узнать (в разности дат параметр тогда нужно будет поменять):ВЫБРАТЬ ДАТАВРЕМЯ(2018, 2, 23) КАК ДатаНачала, ДАТАВРЕМЯ(2018, 2, 23) КАК ДатаОкончания
ПОМЕСТИТЬ Дано
ОБЪЕДИНИТЬ ВЫБРАТЬ ДАТАВРЕМЯ(2018, 3, 8), ДАТАВРЕМЯ(2018, 3, 8)
ОБЪЕДИНИТЬ ВЫБРАТЬ ДАТАВРЕМЯ(2018, 2, 23), ДАТАВРЕМЯ(2018, 2, 25)
;
ВЫБРАТЬ
    СУММА(ВложенныйЗапрос.Разность) КАК ЗанятыхДней
ИЗ
    (ВЫБРАТЬ РАЗЛИЧНЫЕ
        1 - РАЗНОСТЬДАТ(ДАТАВРЕМЯ(1, 1, 1), Дано.ДатаНачала, ДЕНЬ) КАК Разность
    ИЗ
        Дано КАК Дано
            ЛЕВОЕ СОЕДИНЕНИЕ Дано КАК Левее
            ПО (Левее.ДатаНачала < Дано.ДатаНачала)
                И Дано.ДатаНачала <= Левее.ДатаОкончания
    ГДЕ
        Левее.ДатаНачала ЕСТЬ NULL

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

    ВЫБРАТЬ РАЗЛИЧНЫЕ
        РАЗНОСТЬДАТ(ДАТАВРЕМЯ(1, 1, 1), Дано.ДатаОкончания, ДЕНЬ)
    ИЗ
        Дано КАК Дано
            ЛЕВОЕ СОЕДИНЕНИЕ Дано КАК Правее
            ПО (Правее.ДатаНачала <= Дано.ДатаОкончания)
                И Дано.ДатаОкончания < Правее.ДатаОкончания
    ГДЕ
        Правее.ДатаОкончания ЕСТЬ NULL) КАК ВложенныйЗапрос
Запрос основан на идеях статьи http://catalog.mista.ru/public/92490/ .

Во вложенном запросе определяются края "островов". Расстояние от начала времен до левого края вычитается, а до правого края - прибавляется.

Тут не учитывается требование попадания мероприятий в заданный интервал, но это нетрудно решить предварительно (обрезав выходящие за край заданного интервала мероприятия).
Закон Брукера: Даже маленькая практика стоит большой теории.