Имя: Пароль:
1C
1С v8
Нужен алгоритм расчета вытеснения периодов
0 Gorr
 
07.02.19
11:32
Коллеги день добрый,
Прошу поделиться опытом. Нужен алгоритм расчета вытеснения периодов.
На входе имеем список периодов с разными приоритетами и интервал времени. На выходе необходимо получить список непересекающихся периодов в заданном диапазоне дат с учетом их приоритетов. Аналог получения Фактического периода действия записей регистра расчета. В идеале запросом. Быть может кто-нибудь сталкивался???
1 Garykom
 
гуру
07.02.19
14:32
Сколько платишь?
2 Cyberhawk
 
07.02.19
14:33
Зачем запросом? Хочешь быть незаменимым Васей? ))
3 Gorr
 
08.02.19
11:23
Добрый день, коллеги. Уже нашел как сделать запросом. Кому интересно суть - сначала создается временная таблица дат с каждой датой из интервала. Можно через генерацию числовой последовательности. Далее соединяем ее с исходной таблицей периодов по дате_периода между датой начала и датой окончания из исходной таблицы периодов. Таким образом получим таблицу со всеми периодами на каждую дату из интервала. Группировкой по дате определяем период с максимальным приоритетом на каждую дату интервала.
В итоге получим таблицу с каждой уникальной датой интервала и соответствующий этой дате период с максимальным приоритетом.
Нормализация периодов запросом.
Объединением таблицы с самой собой разбиваем ее на временные таблицу начал периодов и таблицу окончаний периодов. Соединением последних двух таблиц получаем результат. Здесь немного сложнее для понимания, но легко реализуется и пост обработкой результата запроса.

(2) на мой взгляд, запросом всегда красивее. и правильнее.
4 Garykom
 
гуру
08.02.19
11:38
(3) >на мой взгляд, запросом всегда красивее. и правильнее.

Заранее проверь что будет с твоим "запросом" при очень большом количестве периодов.
Ну скажем если их тысячи и больше.
5 Gorr
 
08.02.19
12:12
Специфика задачи такова, что периоды представлены видами расчетов сотрудника, а интервал год.
Нормально там все)
6 Cyberhawk
 
08.02.19
19:23
(3) "запросом всегда красивее. и правильнее" // Смотря что считается критерием правильности. Иногда красотой жертвуют ради повышения понятности кода и, как следствие, сопровождаемости оного.
7 Krendel
 
08.02.19
19:27
А что не сделать регистр сведений в котором считать пригритетность, а запросом просто собирать результат
8 Cyberhawk
 
08.02.19
20:21
(7) Тебя разработчики ЗУП 3 покусали?
9 Nyoko
 
08.02.19
20:45
в бит финансе подобное видал, но там рекурсия была .
10 Рэйв
 
08.02.19
20:46
(0)Помню свой вынос мозга в решении этой задачи на час примерно.
Не продам и не скажу.Это святое:-)
11 Garykom
 
гуру
08.02.19
22:19
Хз как запросом но кодом:
1. Делаем ТЗ всех дат (начала и конца) в одну колонку, не забываем добавить даты начала и конца интервала, сортируем по возрастанию.
2. Перебираем и пишем в новую ТЗ все интервалы 1-2, 2-3, 3-4, 4-5 и т.д., нумеруем их.
3. Перебираем все начальные периоды в порядке снижения приоритета и каждому забираем интервалы из которых он состоит (сравнение дат на > и <), если интервала не досталось (при заборе удаляем его откуда забрали) то он в пролете. Если в пролете вернуть интервалы назад в кучу.
Если все интервалы подобрали (сумма подобранных = длине периода) то в результат.
4. Получаем нужный результат, алгоритм работает очень шустро, сложность примерно N*N.
12 Krendel
 
09.02.19
04:17
(8) я же не программер, да и чем сложнее постановка, тем дольше сдавать ;-)
13 Конструктор1С
 
09.02.19
05:46
Лишь бы в запрос всё пихать. Задача решается за один проход

Функция ВыделитьНепересекающиесяПериоды(Знач ТаблицаПериодов)

    Если ТаблицаПериодов.Количество() <= 1 Тогда    
        Возврат ТаблицаПериодов;    
    КонецЕсли;
    
    ТаблицаНепересекающихсяПериодов = Новый ТаблицаЗначений;
    ТаблицаНепересекающихсяПериодов.Колонки.Добавить("ДатаНачала",       Новый ОписаниеТипов("Дата"));
    ТаблицаНепересекающихсяПериодов.Колонки.Добавить("ДатаОкончания", Новый ОписаниеТипов("Дата"));

    ТаблицаПериодов.Сортировать("ДатаНачала, ДатаОкончания");
    
    ДатаНачала    = ТаблицаПериодов[0].ДатаНачала;
    ДатаОкончания = ТаблицаПериодов[0].ДатаОкончания;
    
    Для Индекс = 1 По ТаблицаПериодов.Количество() - 1 Цикл
        
        СтрокаПериода = ТаблицаПериодов[Индекс];
        Если СтрокаПериода.ДатаНачала > ДатаОкончания Тогда
            
            НоваяСтрока = ТаблицаНепересекающихсяПериодов.Добавить();
            НоваяСтрока.ДатаНачала       = ДатаНачала;
            НоваяСтрока.ДатаОкончания = ДатаОкончания;

            ДатаНачала       = СтрокаПериода.ДатаНачала;
            ДатаОкончания = СтрокаПериода.ДатаОкончания;
            
        Иначе
            
            ДатаНачала       = Макс(ДатаНачала, СтрокаПериода.ДатаНачала);
            ДатаОкончания = Мин(ДатаОкончания, СтрокаПериода.ДатаОкончания);
        
        КонецЕсли;     
    
    КонецЦикла;
    
    // Последний период
    НоваяСтрока = ТаблицаНепересекающихсяПериодов.Добавить();
    НоваяСтрока.ДатаНачала       = ДатаНачала;
    НоваяСтрока.ДатаОкончания = ДатаОкончания;
    
    Возврат ТаблицаНепересекающихсяПериодов;
    
КонецФункции // ВыделитьНепересекающиесяПериоды()
14 Конструктор1С
 
09.02.19
05:51
(3) "на мой взгляд, запросом всегда красивее. и правильнее"

Ага, потом сидишь над этим "хитрым" запросом и гадаешь, чего же там такое автор пытался изобразить. А "ёмкие" имена временных таблиц, типа "ВТ1", "ВТ2", "Таблица", "ИтогТабл" и т.п., очень помогают разобраться в запросе. Запрос не панацея, его нужно использовать где он уместен
15 Конструктор1С
 
09.02.19
06:14
(13) функция получилась излишне громоздкой, каюсь

Функция ВыделитьНепересекающиесяПериоды(Знач ТаблицаПериодов)

    Если ТаблицаПериодов.Количество() <= 1 Тогда    
        Возврат ТаблицаПериодов;    
    КонецЕсли;
    
    ТаблицаНепересекающихсяПериодов = ТаблицаПериодов.СкопироватьКолонки("ДатаНачала, ДатаОкончания");
    
    ТаблицаПериодов.Сортировать("ДатаНачала, ДатаОкончания");
    
    ДатаНачала    = ТаблицаПериодов[0].ДатаНачала;
    ДатаОкончания = ТаблицаПериодов[0].ДатаОкончания;
    
    Для Индекс = 1 По ТаблицаПериодов.Количество() - 1 Цикл
        
        СтрокаПериода = ТаблицаПериодов[Индекс];
        Если СтрокаПериода.ДатаНачала > ДатаОкончания Тогда
            
            ДобавитьНовуюСтрокуПериода(ТаблицаНепересекающихсяПериодов, ДатаНачала, ДатаОкончания);

            ДатаНачала       = СтрокаПериода.ДатаНачала;
            ДатаОкончания = СтрокаПериода.ДатаОкончания;
            
        Иначе
            
            ДатаНачала       = Макс(ДатаНачала, СтрокаПериода.ДатаНачала);
            ДатаОкончания = Мин(ДатаОкончания, СтрокаПериода.ДатаОкончания);
        
        КонецЕсли;     
    
    КонецЦикла;
    
    // Последний период
    ДобавитьНовуюСтрокуПериода(ТаблицаНепересекающихсяПериодов, ДатаНачала, ДатаОкончания);
    
    Возврат ТаблицаНепересекающихсяПериодов;
    
КонецФункции // ВыделитьНепересекающиесяПериоды()

Процедура ДобавитьНовуюСтрокуПериода(Таблица, ДатаНачала, ДатаОкончания)

    НоваяСтрока = Таблица.Добавить();
    НоваяСтрока.ДатаНачала       = ДатаНачала;
    НоваяСтрока.ДатаОкончания = ДатаОкончания;    

КонецПроцедуры
16 Конструктор1С
 
09.02.19
06:38
А, стоп, нужны ведь непересекающиеся, у меня получаются наоборот перекрытия
17 SleepyHead
 
гуру
09.02.19
07:57
(0) Я на 77 на одной самописке такое делал на таблицах значений. Работало охренительно долго, но зато результат был проверяемый и понятный.