Отбор и сортировка данных в запросе с использованием приоритетов

Опубликовал Viktor Makovkin (unmensch) в раздел Программирование - Практика программирования

Небольшая хитрость, позволяющая упростить сложный отбор в запросе

Имеется компания, максимально лояльно относящаяся к своим сотрудникам. Лояльность подразумевает возможность брать отпуск любой продолжительности, любое количество раз в пределах накопленного количества + авансового отпуска будущего периода + доп. дни отпуска. При всем при этом учет отпусков ведется в рабочих днях. После накопления определенного количества фактических отпускных дней они группируются по неделям, переводятся в календарные дни и "проводятся официально". Компания имеет несколько юр. лиц, и большое количество сотрудников. Учет всего, в том числе кадровый и зарплаты, ведется в 1С ERP 2 с минимальными изменениями в типовой системе учета. Для реализации возможности ведения учета отпусков была внедрена отдельная подсистема, описание которой - тема для отдельной статьи.

Имеется в этой компании некоторый портал, на котором можно посмотреть информацию по каждому из сотрудников, в том числе, находится ли он сейчас в отпуске, либо отсутствует по какой-либо другой причине. Также, на своей странице можно посмотреть все свои фактические отсутствия, накопленное количество дней отпуска, а также оставшееся количество дней дополнительного отпуска. Данные запрашиваются на лету из 1С через HTTP сервис при открытии страницы сотрудника. В качестве идентификатора сотрудника используется его логин в AD. Поле с логином сделали обязательным для заполнения в карточке физ.лица.

Люди, знакомые с особенностями ведения кадрового учета в ERP 2 и ЗУП 3.0, знают, что в указанных конфигурациях имеются справочники физ. лиц и сотрудников с соответствием один ко многим, т.е. одному физ.лицу может соответствовать несколько сотрудников. Поскольку организаций в компании несколько и сотрудников много, нередки случаи работы одного физ.лица в разных организациях, либо в одной организации на разных должностях, с разными датами приема, а, следовательно, и с разными рабочими периодами. Учет фактических отсутствий ведется в разрезе сотрудников, т.к. "официально проводить" их нужно по фактически работающим штатным единицам, но  на портале эта информация пользователю не нужна. Ему интересно знать, какой он может взять отпуск сегодня и сколько у него после этого останется накопленных дней, отсюда встает задача определения основного сотрудника, по которому будет определяться информация об отпусках.

Совместно с руководством было прниято следующее определение: основной сотрудник - это сотрудник, принятый на основное место работы, а если такого нет (т.е. на всех местах работы значится совместителем), то сотрудник с минимальной датой приема.

Как уже упоминалось, данные формируются на лету, а значит, очень важна скорость их получения, поэтому решено было получить всех основных сотрудников одним запросом, принимая в качестве параметра массив логинов. Здесь и кроется основная сложность и хитрое изощрение. Не годится простой отбор по минимальной дате приема, поскольку вводится дополнительный отбор по виду занятости. Для простоты и стройности конструкции запроса добавили приоритет  и вместо сортировки и отбора по дате приема выполняется сортировка и отбор по приоритету.

ВЫБРАТЬ РАЗРЕШЕННЫЕ
	ФизическиеЛицаКонтактнаяИнформация.Ссылка КАК ФизЛицо,
	ФизическиеЛицаКонтактнаяИнформация.Представление КАК Логин
ПОМЕСТИТЬ ВТФЛ
ИЗ
	Справочник.ФизическиеЛица.КонтактнаяИнформация КАК ФизическиеЛицаКонтактнаяИнформация
ГДЕ
	ФизическиеЛицаКонтактнаяИнформация.Вид.Наименование ПОДОБНО "%Логин%AD%"
	И ФизическиеЛицаКонтактнаяИнформация.Представление В(&Логины)

ИНДЕКСИРОВАТЬ ПО
	ФизЛицо
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗРЕШЕННЫЕ
	ТекущиеКадровыеДанныеСотрудников.Сотрудник КАК Сотрудник,
	ТекущиеКадровыеДанныеСотрудников.ДатаПриема КАК ДатаПриема,
	ТекущиеКадровыеДанныеСотрудников.ТекущийВидЗанятости КАК ТекущийВидЗанятости,
	ВТФЛ.ФизЛицо КАК ФизЛицо,
	ВТФЛ.Логин
ПОМЕСТИТЬ ВТСотрудники
ИЗ
	ВТФЛ КАК ВТФЛ
		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ТекущиеКадровыеДанныеСотрудников КАК ТекущиеКадровыеДанныеСотрудников
		ПО ВТФЛ.ФизЛицо = ТекущиеКадровыеДанныеСотрудников.ФизическоеЛицо
			И (ТекущиеКадровыеДанныеСотрудников.ДатаПриема <> ДАТАВРЕМЯ(1, 1, 1))
			И (ТекущиеКадровыеДанныеСотрудников.ДатаУвольнения = ДАТАВРЕМЯ(1, 1, 1))

ИНДЕКСИРОВАТЬ ПО
	ДатаПриема,
	ФизЛицо
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗРЕШЕННЫЕ
	ВТСотрудники.ДатаПриема КАК ДатаПриема,
	КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ВТСотрудники1.ДатаПриема) КАК Порядок,
	ВТСотрудники.Логин КАК Логин,
	ВТСотрудники.Сотрудник
ПОМЕСТИТЬ ВТПриоритеты
ИЗ
	ВТСотрудники КАК ВТСотрудники
		ЛЕВОЕ СОЕДИНЕНИЕ ВТСотрудники КАК ВТСотрудники1
		ПО ВТСотрудники.ФизЛицо = ВТСотрудники1.ФизЛицо
			И ВТСотрудники.ДатаПриема >= ВТСотрудники1.ДатаПриема
			И (ВТСотрудники1.ТекущийВидЗанятости = ЗНАЧЕНИЕ(Перечисление.ВидыЗанятости.ОсновноеМестоРаботы))
ГДЕ
	ВТСотрудники.ТекущийВидЗанятости = ЗНАЧЕНИЕ(Перечисление.ВидыЗанятости.ОсновноеМестоРаботы)

СГРУППИРОВАТЬ ПО
	ВТСотрудники.Логин,
	ВТСотрудники.ДатаПриема,
	ВТСотрудники.Сотрудник

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

ВЫБРАТЬ
	ВТСотрудники.ДатаПриема,
	КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ВТСотрудники1.ДатаПриема) + 100,
	ВТСотрудники.Логин,
	ВТСотрудники.Сотрудник
ИЗ
	ВТСотрудники КАК ВТСотрудники
		ЛЕВОЕ СОЕДИНЕНИЕ ВТСотрудники КАК ВТСотрудники1
		ПО ВТСотрудники.ФизЛицо = ВТСотрудники1.ФизЛицо
			И ВТСотрудники.ДатаПриема >= ВТСотрудники1.ДатаПриема
			И (ВТСотрудники1.ТекущийВидЗанятости <> ЗНАЧЕНИЕ(Перечисление.ВидыЗанятости.ОсновноеМестоРаботы))
ГДЕ
	ВТСотрудники.ТекущийВидЗанятости <> ЗНАЧЕНИЕ(Перечисление.ВидыЗанятости.ОсновноеМестоРаботы)

СГРУППИРОВАТЬ ПО
	ВТСотрудники.Логин,
	ВТСотрудники.ДатаПриема,
	ВТСотрудники.Сотрудник
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗРЕШЕННЫЕ
	ОтборФЛ.Логин КАК Логин,
	ВТПриоритеты.ДатаПриема,
	ВТПриоритеты.Сотрудник
ИЗ
	(ВЫБРАТЬ
		ВТПриоритеты.Логин КАК Логин,
		МИНИМУМ(ВТПриоритеты.Порядок) КАК Порядок
	ИЗ
		ВТПриоритеты КАК ВТПриоритеты
	
	СГРУППИРОВАТЬ ПО
		ВТПриоритеты.Логин) КАК ОтборФЛ
		ЛЕВОЕ СОЕДИНЕНИЕ ВТПриоритеты КАК ВТПриоритеты
		ПО ОтборФЛ.Логин = ВТПриоритеты.Логин
			И ОтборФЛ.Порядок = ВТПриоритеты.Порядок

Во временной таблице ВТПриоритеты вводится поле Приоритет. Помимо того, что числовой показатель приоритета увеличивается для сотрудников с большей датой приема, он также увеличивается на некоторую константу для совместителей. Это вводит некоторое ограничение на количество рабоатющих сотрудников одного физ.лица, но на практике это ограничение вряд ли будет достигнуто.

Использование такого нехитрого приема позволило достигнуть приемлемой скорости получения данных и снизить задержки при формирование ответа HTTP-сервиса.

См. также

Комментарии
1. Сергей Б (KotorVB) 11.01.17 18:14 Сейчас в теме
Зачем индекс у ВТФЛ и почему нет у ВТПриоритеты?