Имя: Пароль:
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/ .

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

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