Добрый день, коллеги! Прошу помочь с запросом.
Имеется регистр сведений "СостояниеФизическихЛиц", периодичность - "В пределах секунды", режим записи - "Подчинение регистратору". У регистра одно измерение - "ФизическоеЛицо" (СправочникСсылка.ФизическиеЛица), один ресурс - "Статус" (ссылка на справочник) и один реквизит - "ДатаОкончания" (Дата и время).
В регистре имеются записи по сотруднику (на скриншоте). Т.е., документом движения № 1 назначен простой с 19.03.2025 по 17.05.2025, а документом движения № 2 вытеснен период командировки с 05.04.2025 по 10.04.2025.
Задача в следующем: необходимо составить запрос к этому регистру сведений за определенный период таким образом, чтобы в результате выводилась колонка "ПериодДень", содержащая каждый день от начала периода запроса до конца периода запроса, которой бы соответствовал именно тот статус, который назначен сотруднику. Т.е., например, запрос за период с 01.04.2025 по 30.04.2025 должен вывести:
01.04.2025 Сотрудник1 Простой
02.04.2025 Сотрудник1 Простой
03.04.2025 Сотрудник1 Простой
04.04.2025 Сотрудник1 Простой
05.04.2025 Сотрудник1 Командировка
06.04.2025 Сотрудник1 Командировка
07.04.2025 Сотрудник1 Командировка
08.04.2025 Сотрудник1 Командировка
09.04.2025 Сотрудник1 Командировка
10.04.2025 Сотрудник1 Командировка
11.04.2025 Сотрудник1 Простой
12.04.2025 Сотрудник1 Простой
13.04.2025 Сотрудник1 Простой
14.04.2025 Сотрудник1 Простой
и т.д.
Никак мне не удается "оборвать" командировку в нужном месте. Видимо, что-то упускаю, но никак не могу понять что именно.
Регистр сведений и запрос никак не относятся к конфигурации ЗУП и вообще ни к одной типовой конфигурации.
(1) Если ты подготовил более/менее рабочий запрос, но "Никак мне не удается "оборвать" командировку в нужном месте", то приведи этот твой запрос. Посмотрим. Может и подскажем - как не упустить то, что ты упустил.
(25) Пока так (&ДатаНачала = 01.04.2025, &ДатаОкончания = 30.04.2025)
ВЫБРАТЬ
0 КАК Цифра
ПОМЕСТИТЬ ВТЦифры
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
1
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
2
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
3
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
4
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
5
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
6
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
7
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
8
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
9
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ФизическиеЛица.Ссылка КАК Сотрудник,
&ДатаНачала КАК ДатаНачала,
&ДатаОкончания КАК ДатаОкончания
ПОМЕСТИТЬ ВТСотрудникиПериоды
ИЗ
Справочник.ФизическиеЛица КАК ФизическиеЛица
ГДЕ
ФизическиеЛица.Ссылка = &Сотрудник
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(СотрудникиПериоды.ДатаНачала, ДЕНЬ), ДЕНЬ, Единицы.Цифра + ЕСТЬNULL(Десятки.Цифра, 0) * 10 + ЕСТЬNULL(Сотни.Цифра, 0) * 100) КАК ТекущаяДата
ПОМЕСТИТЬ ВТДатыПериода
ИЗ
ВТСотрудникиПериоды КАК СотрудникиПериоды
ЛЕВОЕ СОЕДИНЕНИЕ ВТЦифры КАК Единицы
ПО (ИСТИНА)
ЛЕВОЕ СОЕДИНЕНИЕ ВТЦифры КАК Десятки
ПО (ИСТИНА)
ЛЕВОЕ СОЕДИНЕНИЕ ВТЦифры КАК Сотни
ПО (ИСТИНА)
ГДЕ
ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(СотрудникиПериоды.ДатаНачала, ДЕНЬ), ДЕНЬ, Единицы.Цифра + ЕСТЬNULL(Десятки.Цифра, 0) * 10 + ЕСТЬNULL(Сотни.Цифра, 0) * 100) <= СотрудникиПериоды.ДатаОкончания
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
СотрудникиПериоды.Сотрудник КАК Сотрудник,
СостояниеФизическихЛицСрезПоследних.Статус КАК Статус,
СостояниеФизическихЛицСрезПоследних.Период КАК ДатаНачала,
КОНЕЦПЕРИОДА(СостояниеФизическихЛицСрезПоследних.ДатаОкончания, ДЕНЬ) КАК ДатаОкончания
ПОМЕСТИТЬ ВТСтатусыСотрудников
ИЗ
ВТСотрудникиПериоды КАК СотрудникиПериоды
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.СостояниеФизическихЛиц.СрезПоследних(&ДатаНачала, ФизическоеЛицо = &Сотрудник) КАК СостояниеФизическихЛицСрезПоследних
ПО СотрудникиПериоды.Сотрудник = СостояниеФизическихЛицСрезПоследних.ФизическоеЛицо
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
СотрудникиПериоды.Сотрудник,
СостояниеФизическихЛиц.Статус,
СостояниеФизическихЛиц.Период,
КОНЕЦПЕРИОДА(СостояниеФизическихЛиц.ДатаОкончания, ДЕНЬ)
ИЗ
ВТСотрудникиПериоды КАК СотрудникиПериоды
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.СостояниеФизическихЛиц КАК СостояниеФизическихЛиц
ПО СотрудникиПериоды.Сотрудник = СостояниеФизическихЛиц.ФизическоеЛицо
И (СостояниеФизическихЛиц.Период >= СотрудникиПериоды.ДатаНачала)
ГДЕ
СостояниеФизическихЛиц.ФизическоеЛицо = &Сотрудник
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ДатыПериода.ТекущаяДата КАК ТекущаяДата,
СтатусыСотрудников.Сотрудник КАК Сотрудник,
СтатусыСотрудников.Статус КАК Статус
ИЗ
ВТДатыПериода КАК ДатыПериода
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТСтатусыСотрудников КАК СтатусыСотрудников
ПО (ДатыПериода.ТекущаяДата МЕЖДУ СтатусыСотрудников.ДатаНачала И СтатусыСотрудников.ДатаОкончания)
УПОРЯДОЧИТЬ ПО
ТекущаяДата
Показать
Но при таком исполнении в результирующем запросе задваиваются даты, так как во временной таблице ВТСтатусыСотрудников для статуса, который является возвратным начало и конец периода не прерываются статусами внутри этого периода.
(27) Дык, естественно. Поэтому и запрос у тебя спрашивал, чтобы понять - как у тебя устроено "вытеснение". И исходя из твоего запроса - вытеснения у тебя нет. Надо смотреть - что у тебя в справочнике "Статусы", есть ли какой-то признак, который показывает, что такой-то статус вытесняет другой статус. В частности - сравни статусы "Простой" и "Командировка"
(1) Тестовое задание? Задача почти определение фактического периода действия для начислений. Только в вашем случае разбитие на все даты и ваша задача проще.
Сформируйте перечень всех дат, которые необходимы.
Прицепите к каждой дате первое попавшееся действующее состояние с учетом приоритета.
Сложность реальной задачи, от вашей тестовой задачи в том, что БЛ вытесняет командировку, Командировка вытесняет простой, а Простой вытесняет БЛ (во время простоя БЛ не оплачивается) :-)
И в общем случае такая задача решения не имеет. :-)
Выбрать
Даты.Период, Физик, Статус
ИЗ Статусы
ЛЕВОЕ СОЕДИНЕНИЕ Даты
ПО Статусы.Период В (выбрать максимум(период) из Статусы ГДЕ период <= Даты.Период)
Ну ты сам уже отладь и красоты наведи, но суть тут. Соединение по индексу, ибо максимум(период) - это чтение индекса. Так что все летать будет.
(7) (1) Максимум периода тут не при чем.... Тут важно кто кого вытесняет.
Но по сути запрос такой же, но соединение не по периоду, а по состоянию (периартерииту состояния) и его надо проставить для каждого состояния заранее.
Увидел слово "Состояния", подумал про ЗУП, дал ответ по ЗУПу
ТекстЗапроса
ВЫБРАТЬ
ДАТАВРЕМЯ(1, 1, 1) КАК Период
ПОМЕСТИТЬ Представления_Периоды
ГДЕ
"НачалоИнтервала" = &НачалоПериода
И "ОкончаниеИнтервала" = &ОкончаниеПериода
И "Периодичность" = "День"
;
ВЫБРАТЬ
ПредставленияПериоды.Период КАК Период,
Сотрудники.Ссылка КАК Сотрудник
ПОМЕСТИТЬ ВТСотрудникиПериоды
ИЗ
ПредставленияПериоды Как ПредставленияПериоды,
Справочник.Сотрудники КАК Сотрудники
ГДЕ
Сотрудники.ГоловнаяОрганизация = &Организация
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ДАТАВРЕМЯ(1, 1, 1) КАК Период,
ЗНАЧЕНИЕ(Справочник.Сотрудники.ПустаяСсылка) КАК Сотрудник,
ЗНАЧЕНИЕ(Перечисление.СостоянияСотрудника.ПустаяСсылка) КАК Состояние
ПОМЕСТИТЬ Представления_КадровыеДанныеСотрудников
ИЗ
ВТСотрудникиПериоды КАК СотрудникиПериоды
Потом объявляйте объект Запрос,
устанавливайте ему ТекстЗапроса,
Устанавливайте параметры (зависят от ваших фильтров дат и сотрудников) и так далее
В моём примере будут 4 параметра
НачалоПериода
ОкончаниеПериода
Организация
и
ОкончаниеИнтервалаПредставленияПериоды, вот ему дайте значение 31.12.3999
Выходная таблица с данными "ПредставленияКадровыеДанныеСотрудников"
ВЫБРАТЬ
0 КАК Цифра
ПОМЕСТИТЬ ВТЦифры
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
1
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
2
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
3
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
4
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
5
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
6
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
7
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
8
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
9
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(ВЫБОР
КОГДА &НачалоПериода = ДАТАВРЕМЯ(1, 1, 1)
ТОГДА ДАТАВРЕМЯ(1980, 1, 1)
ИНАЧЕ &НачалоПериода
КОНЕЦ, ДЕНЬ), ДЕНЬ, Цифры.Цифра + ЕСТЬNULL(Цифры10.Цифра, 0) * 10 + ЕСТЬNULL(Цифры100.Цифра, 0) * 100 + ЕСТЬNULL(Цифры1000.Цифра, 0) * 1000 + ЕСТЬNULL(Цифры10000.Цифра, 0) * 10000 + ЕСТЬNULL(Цифры100000.Цифра, 0) * 100000), ДЕНЬ) КАК Период
ПОМЕСТИТЬ ПредставленияПериоды
ИЗ
ВТЦифры КАК Цифры
ЛЕВОЕ СОЕДИНЕНИЕ ВТЦифры КАК Цифры10
ПО (РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(ВЫБОР
КОГДА &НачалоПериода = ДАТАВРЕМЯ(1, 1, 1)
ТОГДА ДАТАВРЕМЯ(1980, 1, 1)
ИНАЧЕ &НачалоПериода
КОНЕЦ, ДЕНЬ), НАЧАЛОПЕРИОДА(ВЫБОР
КОГДА &ОкончаниеПериода = КОНЕЦПЕРИОДА(ДАТАВРЕМЯ(1, 1, 1), ДЕНЬ)
ТОГДА &ОкончаниеИнтервалаПредставленияПериоды
ИНАЧЕ &ОкончаниеПериода
КОНЕЦ, ДЕНЬ), ДЕНЬ) > 9)
ЛЕВОЕ СОЕДИНЕНИЕ ВТЦифры КАК Цифры100
ПО (РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(ВЫБОР
КОГДА &НачалоПериода = ДАТАВРЕМЯ(1, 1, 1)
ТОГДА ДАТАВРЕМЯ(1980, 1, 1)
ИНАЧЕ &НачалоПериода
КОНЕЦ, ДЕНЬ), НАЧАЛОПЕРИОДА(ВЫБОР
КОГДА &ОкончаниеПериода = КОНЕЦПЕРИОДА(ДАТАВРЕМЯ(1, 1, 1), ДЕНЬ)
ТОГДА &ОкончаниеИнтервалаПредставленияПериоды
ИНАЧЕ &ОкончаниеПериода
КОНЕЦ, ДЕНЬ), ДЕНЬ) > 99)
ЛЕВОЕ СОЕДИНЕНИЕ ВТЦифры КАК Цифры1000
ПО (РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(ВЫБОР
КОГДА &НачалоПериода = ДАТАВРЕМЯ(1, 1, 1)
ТОГДА ДАТАВРЕМЯ(1980, 1, 1)
ИНАЧЕ &НачалоПериода
КОНЕЦ, ДЕНЬ), НАЧАЛОПЕРИОДА(ВЫБОР
КОГДА &ОкончаниеПериода = КОНЕЦПЕРИОДА(ДАТАВРЕМЯ(1, 1, 1), ДЕНЬ)
ТОГДА &ОкончаниеИнтервалаПредставленияПериоды
ИНАЧЕ &ОкончаниеПериода
КОНЕЦ, ДЕНЬ), ДЕНЬ) > 999)
ЛЕВОЕ СОЕДИНЕНИЕ ВТЦифры КАК Цифры10000
ПО (РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(ВЫБОР
КОГДА &НачалоПериода = ДАТАВРЕМЯ(1, 1, 1)
ТОГДА ДАТАВРЕМЯ(1980, 1, 1)
ИНАЧЕ &НачалоПериода
КОНЕЦ, ДЕНЬ), НАЧАЛОПЕРИОДА(ВЫБОР
КОГДА &ОкончаниеПериода = КОНЕЦПЕРИОДА(ДАТАВРЕМЯ(1, 1, 1), ДЕНЬ)
ТОГДА &ОкончаниеИнтервалаПредставленияПериоды
ИНАЧЕ &ОкончаниеПериода
КОНЕЦ, ДЕНЬ), ДЕНЬ) > 9999)
ЛЕВОЕ СОЕДИНЕНИЕ ВТЦифры КАК Цифры100000
ПО (РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(ВЫБОР
КОГДА &НачалоПериода = ДАТАВРЕМЯ(1, 1, 1)
ТОГДА ДАТАВРЕМЯ(1980, 1, 1)
ИНАЧЕ &НачалоПериода
КОНЕЦ, ДЕНЬ), НАЧАЛОПЕРИОДА(ВЫБОР
КОГДА &ОкончаниеПериода = КОНЕЦПЕРИОДА(ДАТАВРЕМЯ(1, 1, 1), ДЕНЬ)
ТОГДА &ОкончаниеИнтервалаПредставленияПериоды
ИНАЧЕ &ОкончаниеПериода
КОНЕЦ, ДЕНЬ), ДЕНЬ) > 99999)
ГДЕ
НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(ВЫБОР
КОГДА &НачалоПериода = ДАТАВРЕМЯ(1, 1, 1)
ТОГДА ДАТАВРЕМЯ(1980, 1, 1)
ИНАЧЕ &НачалоПериода
КОНЕЦ, ДЕНЬ), ДЕНЬ, Цифры.Цифра + ЕСТЬNULL(Цифры10.Цифра, 0) * 10 + ЕСТЬNULL(Цифры100.Цифра, 0) * 100 + ЕСТЬNULL(Цифры1000.Цифра, 0) * 1000 + ЕСТЬNULL(Цифры10000.Цифра, 0) * 10000 + ЕСТЬNULL(Цифры100000.Цифра, 0) * 100000), ДЕНЬ) МЕЖДУ ВЫБОР
КОГДА &НачалоПериода = ДАТАВРЕМЯ(1, 1, 1)
ТОГДА ДАТАВРЕМЯ(1980, 1, 1)
ИНАЧЕ &НачалоПериода
КОНЕЦ И ВЫБОР
КОГДА &ОкончаниеПериода = КОНЕЦПЕРИОДА(ДАТАВРЕМЯ(1, 1, 1), ДЕНЬ)
ТОГДА &ОкончаниеИнтервалаПредставленияПериоды
ИНАЧЕ &ОкончаниеПериода
КОНЕЦ
;
////////////////////////////////////////////////////////////////////////////////
УНИЧТОЖИТЬ ВТЦифры
;
Показать
Далее к ней примотать физлиц, получить временную таблицу с именем, скажем, "ВТОтборовРазличныхСотрудников"
Далее так
ВЫБРАТЬ
ИзмеренияДаты.ФизическоеЛицо КАК ФизическоеЛицо,
ИзмеренияДаты.Период КАК Период,
СостоянияСотрудников.Период КАК ПериодЗаписи,
СостоянияСотрудников.Статус КАК Статус
ПОМЕСТИТЬ ВТСведенияОСостоянииСотрудников
ИЗ
ВТОтборовРазличныхСотрудников КАК ИзмеренияДаты
ЛЕВОЕ СОЕДИНЕНИЕ ВАШ_РегистрСведений.СостоянияСотрудников КАК СостоянияСотрудников
ПО ИзмеренияДаты.ФизическоеЛицо= СостоянияСотрудников.ФизическоеЛицо
И (СостоянияСотрудников.Период <= ИзмеренияДаты.Период)
И (КОНЕЦПЕРИОДА(СостоянияСотрудников.ДатаОкончания, ДЕНЬ) >= ИзмеренияДаты.Период
ИЛИ СостоянияСотрудников.ДатаОкончания= ДАТАВРЕМЯ(1, 1, 1))
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ТаблицаОтборов.Период КАК Период,
ТаблицаОтборов.ФизическоеЛицо КАК ФизическоеЛицо,
СведенияОСостоянииСотрудников.Статус КАК Статус
ПОМЕСТИТЬ ВыходнаяТаблица
ИЗ
ВТОтборовРазличныхСотрудников КАК ТаблицаОтборов
{ЛЕВОЕ СОЕДИНЕНИЕ ВТСведенияОСостоянииСотрудников КАК СведенияОСостоянииСотрудников
ПО ТаблицаОтборов.ФизическоеЛицо= СведенияОСостоянииСотрудников.ФизическоеЛицо
И ТаблицаОтборов.Период = СведенияОСостоянииСотрудников.Период}
Показать
Проще писать на реальных базах. Хоть синтаксис и работу проверить можно
(12) А Вы надеетесь, что кто-то будет делать за вас тестовое задание?
Идеи вам рассказали. Даже куски вашего задания сделали по отдельности. Дальше сами.
..дату окончания, в которой собственно и весь затык
Нет там никакого "затыка". Дата окончания либо действует либо нет. В ЗиУП 2.5 все аналогичные регистры подобной конструкции. К выходу ЗиУП 3.1 уже устали объяснять как из такого регистра данные читать, разбирать на периоды и просто создали ещё один регистр с суффиксом "Интервальный" и актуализируют его при каждой записи в исходный регистр. Собственно "Интервальный" регистр, содержит ту же информацию, но уже разбитую на периоды.
Примерно так же по сути работают Регистры расчета в части фактического периода действия.
(17) Вы все верно говорите. Но сдается мне, что интервальные регистры были введены не для упрощения, а как раз для подобных задач. С этими исходными данными без интервальных регистров или какого-либо дополнительного реквизита, обозначающего приоритет статуса, эту задачу не решить.
(21) Интервальный заполняется на основании данных исходного. Сделали Интервальный, что бы данные было проще брать уже готовые, а не писать каждый раз один и тот же запрос с "перелеваниями" из пустого в порожнее, так как запросы в 1С многое чего не умеют и их уже 20 лет практически не развивают и, по всей видимости, столько же ещё не будут развивать.
Разработчики конфигурации ЗиУП, наверняка в курсе планов развития Запросов в языке 1С, а точнее, скорее всего, отсутствия планов. Поэтому как-то своими силами.
(23) Ну эти сложные запросы по неинтервальному регистру еще и системы подвешивали.
А собери ка "кадровые состояния" 5000 сотрудников на каждый день периода календарного месяца из неинтервального регистра?
В интервальном - просто и очень быстро. Одна - две секунды и готово.
В обычном - даже не берусь предполагать.
(23) Ну эти сложные запросы по неинтервальному регистру еще и системы подвешивали.
Эти запросы сложные в 1С. Вне 1С эти запросы выглядят совсем иначе и выполняются они быстро. Эти тормоза в 1С - это проблемы платформы 1С, которая поддерживает очень древний и даже не полный стандарт SQL 1992 года. Причина - наличие файловой, версии 1С. Никак не могут отказаться от файловой версии, в которой надо реализовывать всё тоже самое, что и Постгри и в MS SQL и т.д.
Там где приоритет - там фактический период действия регистра расчетов. Суть та же, но решение другое. В платформе есть уже готовая реализация.
Правда тоже без косяков в платформе не обходилось. С какого-то перепуга одно время не учитывались сторно записи, у которых период регистрации и действия были в одном расчетном периоде. Из жизни как пример отпуск будущего периода (переходящий отпуск) и сторно запись его в будущем в следствии новых обстоятельств (БЛ, отзыв, и т.д.). При этом в данных графика потом поправились и стали учитывать такие сторно записи, а в фактическом периоде действия нет. Сейчас не знаю - давно не проверялось.
С интервального вообще легче не бывает
Опорная таблица с периодами (по-дневно), соединяется с интервальным регистром по условию "Дата из регистра периодов между ДатаНачала и ДатаОкончания регистра с состояниями)