Имя: Пароль:
1C
1С v8
алгоритм поиска пересечений
,
0 Oz11
 
13.03.12
13:36
не могу придумать как принцип работы алгоритма поиска пересечения. задача такова: в регистре накопления есть данные (нам важен только период). например, занято с 01.01 - 06.01 и с 09.01 - 14.01. запрашивается период с 5.01 - 10.01. вопрос, как отследить пересечение и выдать пользователю результат. помогите, голова что-то вообще не варит.
1 DrShad
 
13.03.12
13:39
а задачу написать русским языком?
2 mzelensky
 
13.03.12
13:41
(0) все зависит от того, как ты хранишь (представляшь) эти данные "01.01 - 06.01 и с 09.01 - 14.01"
3 Oz11
 
13.03.12
13:43
(1) в регистре накопления хранятся данные об плановом отпуске сотрудника в виде:
период -
сотрудник - измерение
количестводней - ресурс,
например в документе запись о том что сотрудник с 01.01 по 09.01 планирует отпуск, в регистр пойдет 9 записей.
1.период - 01.01
сотрудник - сотрудник
количсетводней (1 или 0 в зависимости рабочий день или нет)
......
9.период - 09.01
сотрудник - сотрудник
количсетводней (1 или 0 в зависимости рабочий день или нет)
в другом доке запись на отпуск по тому же сотруднику с 7.01 - 10.01
нужно понять что запись пересекается с уже имеющимися в регистре и выдать сообщение
4 mzelensky
 
13.03.12
13:47
(3) а разве это не в регистрах сведений делается?!
5 DrShad
 
13.03.12
13:47
(4) +100501
6 Kreont
 
13.03.12
13:59
Задача о сложении/вычитании множеств :)
Сначала через сложение добавляются все "занятые" периоды что б получить одно "огромное" множество "занятых дней",
потом через вычитание определяется факт (не)вхождения в первое множество.
7 Kashemir
 
13.03.12
14:06
(0) При такой постановке  вроде все банально - получаешь все используемые интервалы и проверяешь пересечение с каждым интервалом в отдельности.
8 vmv
 
13.03.12
14:08
//
// Метод заключается в использовании декартова произведения, особенностью которого является то, что мощность
// результата (количество строк) равно произведению мощностей участвующих в декартовом произведении таблиц.
//
// Результат запроса можно использовать или выгружать в таблицы значений в качестве источника для менеджеров временных
// таблиц, выгружать в массивы в качестве параметра в запросах и отборах или использовать сам результат как вложенный запрос
//
// Естественно, не рекомендуется задавать интервал в тысячи лет, поэтому перед выполнением запроса
// сделаем ограничение всего на 50 лет.:)
//
// Тестирование на формирование интервала в годах и десятках годов
// запросом этого метода и созданием аналогичной выборки, например, в массив перебором - однозначно показывает,
// что запросный метод производительнее на порядки.
//
Функция ПолучитьДатыДнейВПериодеКакРезультатЗапроса(ДатаНачала, ДатаОкончания) Экспорт
   
   Если ДатаНачала < Дата(2000,1,1) Тогда
       ДатаНачалаВЗапросе = Дата(2000,1,1);
   Иначе
       ДатаНачалаВЗапросе = ДатаНачала;
   КонецЕсли;
   
   Если ДатаОкончания > Дата(2050,1,1) Тогда
       ДатаОкончанияВЗапросе = Дата(2050,1,1);
   Иначе
       ДатаОкончанияВЗапросе = ДатаОкончания;
   КонецЕсли;
   
   ЗапросТекст =
   "ВЫБРАТЬ
   |    ДОБАВИТЬКДАТЕ(&ДатаНачала, ДЕНЬ, 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 <= РАЗНОСТЬДАТ(&ДатаНачала, &ДатаОкончания, ДЕНЬ)";
   
   // Инициализируем переменную запроса, параметры запроса и установим текст запроса
   Запрос = Новый Запрос;
   Запрос.УстановитьПараметр("ДатаНачала"   , ДатаНачалаВЗапросе);
   Запрос.УстановитьПараметр("ДатаОкончания", ДатаОкончанияВЗапросе);
   Запрос.Текст = ЗапросТекст;
   
   // Собственно выполнение запроса и возврат результата его выполнения
   Возврат Запрос.Выполнить();
   
КонецФункции


Получаешь результаты запроса для указанных интервалов и делаешь их объединения
через МВТ для поиска дырок, аналогичный метод есть и для поиска дырок в числах

Можешь поискать дырки для интервалов в сотни лет, попробуй в тысячи, я тестил на тысячелетиях)
9 Kashemir
 
13.03.12
14:24
ВЫБРАТЬ
   ДАТАВРЕМЯ(2012, 1, 1) КАК НачалоИнтервала,
   ДАТАВРЕМЯ(2012, 1, 6) КАК КонецИнтервала
ПОМЕСТИТЬ ТЗЗанятыхИнтервалов

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

ВЫБРАТЬ
   ДАТАВРЕМЯ(2012, 1, 9),
   ДАТАВРЕМЯ(2012, 1, 14)
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
   ТЗЗанятыхИнтервалов.НачалоИнтервала,
   ТЗЗанятыхИнтервалов.КонецИнтервала
ИЗ
   ТЗЗанятыхИнтервалов КАК ТЗЗанятыхИнтервалов
ГДЕ
   (НЕ(ТЗЗанятыхИнтервалов.КонецИнтервала < &ДатаНач
               ИЛИ ТЗЗанятыхИнтервалов.НачалоИнтервала > &ДатаКон))
10 Oz11
 
13.03.12
14:26
всем спасибо огромное за поучения. все оказалось гораздо проще чем я думал. нужно было просто выбрать все записи за проверяемый период и сгруппировать по ресурсу, если число больше 0 - то пересечение есть. правда этот метод не дает самих пересечений (дат), так что возможно еще вернусь к вашим подсказкам. еще раз спасибо.
11 МихаилМ
 
13.03.12
14:46
ВЫБРАТЬ *
из
ГДЕ (ЗанятоС  МЕЖДУ &НачалоИскомогоПериода и &КонецИскомогоПериода )
 ИЛИ
   (ЗанятоПО МЕЖДУ &НачалоИскомогоПериода и &КонецИскомогоПериода )

!!!
в рег накопления Вы
 НЕ сможете хранить ХОТЬ СКОЛЬКО нибуть эффективный индекс
по интервалу. значит будет фул скан. и при количествах записей больше 100 000
жуткие тормоза.  

поэтому рег сведений подходит лучше.

далее чтобы повысить селективность

добовляют в начало  уточняющие поля (актуальность )
либо 1 запись заменяют кортежем сразбивкой на интервалы (месяц - 5 мин)
(подальше положешь - поближе возьмёшь)

в последних версиях ms sql и postgre sql

появились для этих целей пространственные индексы.