Посчитать количество ночных минут работы сотрудников запросом.

1. Glebis 13 06.06.18 17:14 Сейчас в теме
Есть набор данных работы сотрудников:
Сотрудник, ДатаВремяНачала, ДатаВремяОкончания.
Задача тривиальная и простая:
Нужно запросом получить количество ночных минут работы каждого сотрудника. Ночь начинается в 22 часа и заканчивается в 6 утра. ДатаВремяНачала и ДатаВремяОкончания могут отличаться на несколько суток.

Пробовал делать таблицу от МинДатаВремяНачала до МаксДатаВремяОкончания со строкой на каждый час и булевом ночной час или нет, затем присоединяя справа набор данных. В итоге все-равно для поля количества минут получается много "выбор когда конец" конструкций, который не срабатывает при длительности работы больше суток.

Прихожу к мысли, что не обойтись без использования алгоритма остатка от деления на 24, когда остаток проверять <6 или >22.

Есть ли у кого-нить уже простой алгоритм, работающий исключительно в запросе?
Найденные решения
4. ildarovich 7865 07.06.18 11:20 Сейчас в теме
(3) Этот вариант действительно лучше, так как искусственная таблица минут как в (2) в этой задаче ни к чему.
Но можно гораздо короче (ввод данных как в (2) и (3)):
ВЫБРАТЬ
	Сотрудник,
	ДатаВремяНачала,
	ДатаВремяОкончания,
	РАЗНОСТЬДАТ(ДОБАВИТЬКДАТЕ(ДатаВремяНачала, ЧАС, 2), ДОБАВИТЬКДАТЕ(ДатаВремяОкончания, ЧАС, 2), ДЕНЬ) * 480 - ВЫБОР
		КОГДА ЧАС(ДОБАВИТЬКДАТЕ(ДатаВремяНачала, ЧАС, 2)) > 7
			ТОГДА 480
		ИНАЧЕ ЧАС(ДОБАВИТЬКДАТЕ(ДатаВремяНачала, ЧАС, 2)) * 60 + МИНУТА(ДатаВремяНачала)
	КОНЕЦ + ВЫБОР
		КОГДА ЧАС(ДОБАВИТЬКДАТЕ(ДатаВремяОкончания, ЧАС, 2)) > 7
			ТОГДА 480
		ИНАЧЕ ЧАС(ДОБАВИТЬКДАТЕ(ДатаВремяОкончания, ЧАС, 2)) * 60 + МИНУТА(ДатаВремяОкончания)
	КОНЕЦ КАК НочныеМинуты
ИЗ
	Данные
Показать
Результаты те же.

Здесь две идеи:
1) Считать ночные минуты лучше "по екатеринбурскому времени" (с плюсом два часа), так как тогда ночное время будет с 0 до 8;
2) Ночные минуты интервала можно считать, отнимая ночные минуты до левого края интервала от ночных минут до правого края интервала. Та же идея применялась в решении задачи 71 из Минимализмов 3.

Можно заметить много повторных вычислений в выражении для ночных минут:
трижды повторяется
ДОБАВИТЬКДАТЕ(Данные.ДатаВремяНачала, ЧАС, 2)

и
ДОБАВИТЬКДАТЕ(Данные.ДатаВремяОкончания, ЧАС, 2)

по два раза
ЧАС(ДОБАВИТЬКДАТЕ(Данные.ДатаВремяНачала, ЧАС, 2)) 
и
ЧАС(ДОБАВИТЬКДАТЕ(Данные.ДатаВремяОкончания, ЧАС, 2)) 
В первом случае выручит вложенный запрос, во втором - вычисление агрегатного максимума (ВЫБОР по сути вычисляет максимум).

Но, сокращаясь по числу символов, запрос удлинняется в строках, поэтому лучше оставить так как есть.
Остальные ответы
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
2. SlavaKron 06.06.18 20:56 Сейчас в теме
Диапазон дат - не более 4096 дней с &ДатаНач.
ВЫБРАТЬ
	"Сотрудник1" КАК Сотрудник,
	ДАТАВРЕМЯ(2018, 4, 1, 18, 30, 0) КАК ДатаВремяНачала,
	ДАТАВРЕМЯ(2018, 4, 2, 2, 30, 0) КАК ДатаВремяОкончания
ПОМЕСТИТЬ Данные

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

ВЫБРАТЬ
	"Сотрудник2",
	ДАТАВРЕМЯ(2018, 4, 10, 8, 30, 0),
	ДАТАВРЕМЯ(2018, 4, 10, 17, 30, 0)

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

ВЫБРАТЬ
	"Сотрудник3",
	ДАТАВРЕМЯ(2018, 4, 12, 17, 30, 0),
	ДАТАВРЕМЯ(2018, 4, 13, 1, 30, 0)

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

ВЫБРАТЬ
	"Сотрудник4",
	ДАТАВРЕМЯ(2018, 4, 13, 22, 30, 0),
	ДАТАВРЕМЯ(2018, 4, 14, 1, 30, 0)

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

ВЫБРАТЬ
	"Сотрудник5",
	ДАТАВРЕМЯ(2018, 4, 16, 5, 30, 0),
	ДАТАВРЕМЯ(2018, 4, 16, 10, 30, 0)

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

ВЫБРАТЬ
	"Сотрудник6",
	ДАТАВРЕМЯ(2018, 4, 17, 20, 30, 0),
	ДАТАВРЕМЯ(2018, 4, 19, 10, 30, 0)
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	0 КАК Ч
ПОМЕСТИТЬ ВТ1

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

ВЫБРАТЬ
	1
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	Т1.Ч * 2 + Т2.Ч КАК Ч
ПОМЕСТИТЬ ВТ2
ИЗ
	ВТ1 КАК Т1,
	ВТ1 КАК Т2
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	Т1.Ч * 4 + Т2.Ч КАК Ч
ПОМЕСТИТЬ ВТ3
ИЗ
	ВТ2 КАК Т1,
	ВТ2 КАК Т2
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	Т1.Ч * 16 + Т2.Ч КАК Ч
ПОМЕСТИТЬ ВТ4
ИЗ
	ВТ3 КАК Т1,
	ВТ3 КАК Т2
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	Т1.Ч * 16 + Т2.Ч КАК Ч
ПОМЕСТИТЬ ВТ5
ИЗ
	ВТ4 КАК Т1,
	ВТ3 КАК Т2
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	ДОБАВИТЬКДАТЕ(&ДатаНач, ЧАС, ВТ5.Ч * 24 + 22) КАК НачалоНочи,
	ДОБАВИТЬКДАТЕ(&ДатаНач, ЧАС, (ВТ5.Ч + 1) * 24 + 6) КАК КонецНочи
ПОМЕСТИТЬ ЧасыНочи
ИЗ
	ВТ5 КАК ВТ5
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	Данные.Сотрудник КАК Сотрудник,
	Данные.ДатаВремяНачала,
	Данные.ДатаВремяОкончания,
	РАЗНОСТЬДАТ(Данные.ДатаВремяНачала, Данные.ДатаВремяОкончания, МИНУТА) КАК НочныеМинуты
ИЗ
	Данные КАК Данные
		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ЧасыНочи КАК ЧасыНочи
		ПО Данные.ДатаВремяНачала >= ЧасыНочи.НачалоНочи
			И Данные.ДатаВремяОкончания < ЧасыНочи.КонецНочи

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

ВЫБРАТЬ
	Данные.Сотрудник,
	Данные.ДатаВремяНачала,
	Данные.ДатаВремяОкончания,
	РАЗНОСТЬДАТ(Данные.ДатаВремяНачала, ЧасыНочи.КонецНочи, МИНУТА)
ИЗ
	Данные КАК Данные
		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ЧасыНочи КАК ЧасыНочи
		ПО Данные.ДатаВремяНачала >= ЧасыНочи.НачалоНочи
			И Данные.ДатаВремяНачала < ЧасыНочи.КонецНочи
			И Данные.ДатаВремяОкончания >= ЧасыНочи.КонецНочи

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

ВЫБРАТЬ
	Данные.Сотрудник,
	Данные.ДатаВремяНачала,
	Данные.ДатаВремяОкончания,
	РАЗНОСТЬДАТ(ЧасыНочи.НачалоНочи, Данные.ДатаВремяОкончания, МИНУТА)
ИЗ
	Данные КАК Данные
		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ЧасыНочи КАК ЧасыНочи
		ПО Данные.ДатаВремяОкончания >= ЧасыНочи.НачалоНочи
			И Данные.ДатаВремяОкончания < ЧасыНочи.КонецНочи
			И Данные.ДатаВремяНачала < ЧасыНочи.НачалоНочи

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

ВЫБРАТЬ
	Данные.Сотрудник,
	Данные.ДатаВремяНачала,
	Данные.ДатаВремяОкончания,
	РАЗНОСТЬДАТ(ЧасыНочи.НачалоНочи, ЧасыНочи.КонецНочи, МИНУТА)
ИЗ
	Данные КАК Данные
		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ЧасыНочи КАК ЧасыНочи
		ПО Данные.ДатаВремяНачала < ЧасыНочи.НачалоНочи
			И Данные.ДатаВремяОкончания >= ЧасыНочи.КонецНочи

УПОРЯДОЧИТЬ ПО
	Сотрудник
Показать
3. SlavaKron 06.06.18 22:21 Сейчас в теме
Этот вариант лучше:
ВЫБРАТЬ
	"Сотрудник1" КАК Сотрудник,
	ДАТАВРЕМЯ(2018, 4, 1, 18, 30, 0) КАК ДатаВремяНачала,
	ДАТАВРЕМЯ(2018, 4, 2, 2, 30, 0) КАК ДатаВремяОкончания
ПОМЕСТИТЬ Данные

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

ВЫБРАТЬ
	"Сотрудник2",
	ДАТАВРЕМЯ(2018, 4, 10, 8, 30, 0),
	ДАТАВРЕМЯ(2018, 4, 10, 17, 30, 0)

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

ВЫБРАТЬ
	"Сотрудник3",
	ДАТАВРЕМЯ(2018, 4, 12, 17, 30, 0),
	ДАТАВРЕМЯ(2018, 4, 13, 1, 30, 0)

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

ВЫБРАТЬ
	"Сотрудник4",
	ДАТАВРЕМЯ(2018, 4, 13, 22, 30, 0),
	ДАТАВРЕМЯ(2018, 4, 14, 1, 30, 0)

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

ВЫБРАТЬ
	"Сотрудник5",
	ДАТАВРЕМЯ(2018, 4, 16, 5, 30, 0),
	ДАТАВРЕМЯ(2018, 4, 16, 10, 30, 0)

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

ВЫБРАТЬ
	"Сотрудник6",
	ДАТАВРЕМЯ(2018, 4, 17, 20, 30, 0),
	ДАТАВРЕМЯ(2018, 4, 19, 10, 30, 0)
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	Данные.Сотрудник,
	Данные.ДатаВремяНачала,
	Данные.ДатаВремяОкончания,
	ВЫБОР
		КОГДА ЧАС(Данные.ДатаВремяНачала) < 6
			ТОГДА ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(Данные.ДатаВремяНачала, ДЕНЬ), ЧАС, -2)
		ИНАЧЕ ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(Данные.ДатаВремяНачала, ДЕНЬ), ЧАС, 22)
	КОНЕЦ КАК НачалоНочи,
	ВЫБОР
		КОГДА ЧАС(Данные.ДатаВремяОкончания) < 22
			ТОГДА ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(Данные.ДатаВремяОкончания, ДЕНЬ), ЧАС, 6)
		ИНАЧЕ ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(Данные.ДатаВремяОкончания, ДЕНЬ), ЧАС, 30)
	КОНЕЦ КАК КонецНочи
ПОМЕСТИТЬ НачалоИКонецНочи
ИЗ
	Данные КАК Данные
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	НачалоИКонецНочи.Сотрудник,
	НачалоИКонецНочи.ДатаВремяНачала,
	НачалоИКонецНочи.ДатаВремяОкончания,
	ВЫБОР
		КОГДА НачалоИКонецНочи.ДатаВремяНачала > НачалоИКонецНочи.НачалоНочи
			ТОГДА НачалоИКонецНочи.ДатаВремяНачала
		ИНАЧЕ НачалоИКонецНочи.НачалоНочи
	КОНЕЦ КАК НачалоНочнойРаботы,
	ВЫБОР
		КОГДА НачалоИКонецНочи.ДатаВремяОкончания < НачалоИКонецНочи.КонецНочи
			ТОГДА НачалоИКонецНочи.ДатаВремяОкончания
		ИНАЧЕ НачалоИКонецНочи.КонецНочи
	КОНЕЦ КАК КонецНочнойРаботы
ПОМЕСТИТЬ НочноеВремя
ИЗ
	НачалоИКонецНочи КАК НачалоИКонецНочи
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	НочноеВремя.Сотрудник,
	НочноеВремя.ДатаВремяНачала,
	НочноеВремя.ДатаВремяОкончания,
	ВЫБОР
		КОГДА РАЗНОСТЬДАТ(НочноеВремя.НачалоНочнойРаботы, НочноеВремя.КонецНочнойРаботы, МИНУТА) < 0
			ТОГДА 0
		КОГДА РАЗНОСТЬДАТ(НочноеВремя.НачалоНочнойРаботы, НочноеВремя.КонецНочнойРаботы, МИНУТА) <= 480
			ТОГДА РАЗНОСТЬДАТ(НочноеВремя.НачалоНочнойРаботы, НочноеВремя.КонецНочнойРаботы, МИНУТА)
		ИНАЧЕ РАЗНОСТЬДАТ(НочноеВремя.НачалоНочнойРаботы, НочноеВремя.КонецНочнойРаботы, МИНУТА) - (РАЗНОСТЬДАТ(НочноеВремя.НачалоНочнойРаботы, НочноеВремя.КонецНочнойРаботы, ДЕНЬ) - 1) * 960
	КОНЕЦ КАК НочныеМинуты
ИЗ
	НочноеВремя КАК НочноеВремя
Показать
ildarovich; +1 Ответить
4. ildarovich 7865 07.06.18 11:20 Сейчас в теме
(3) Этот вариант действительно лучше, так как искусственная таблица минут как в (2) в этой задаче ни к чему.
Но можно гораздо короче (ввод данных как в (2) и (3)):
ВЫБРАТЬ
	Сотрудник,
	ДатаВремяНачала,
	ДатаВремяОкончания,
	РАЗНОСТЬДАТ(ДОБАВИТЬКДАТЕ(ДатаВремяНачала, ЧАС, 2), ДОБАВИТЬКДАТЕ(ДатаВремяОкончания, ЧАС, 2), ДЕНЬ) * 480 - ВЫБОР
		КОГДА ЧАС(ДОБАВИТЬКДАТЕ(ДатаВремяНачала, ЧАС, 2)) > 7
			ТОГДА 480
		ИНАЧЕ ЧАС(ДОБАВИТЬКДАТЕ(ДатаВремяНачала, ЧАС, 2)) * 60 + МИНУТА(ДатаВремяНачала)
	КОНЕЦ + ВЫБОР
		КОГДА ЧАС(ДОБАВИТЬКДАТЕ(ДатаВремяОкончания, ЧАС, 2)) > 7
			ТОГДА 480
		ИНАЧЕ ЧАС(ДОБАВИТЬКДАТЕ(ДатаВремяОкончания, ЧАС, 2)) * 60 + МИНУТА(ДатаВремяОкончания)
	КОНЕЦ КАК НочныеМинуты
ИЗ
	Данные
Показать
Результаты те же.

Здесь две идеи:
1) Считать ночные минуты лучше "по екатеринбурскому времени" (с плюсом два часа), так как тогда ночное время будет с 0 до 8;
2) Ночные минуты интервала можно считать, отнимая ночные минуты до левого края интервала от ночных минут до правого края интервала. Та же идея применялась в решении задачи 71 из Минимализмов 3.

Можно заметить много повторных вычислений в выражении для ночных минут:
трижды повторяется
ДОБАВИТЬКДАТЕ(Данные.ДатаВремяНачала, ЧАС, 2)

и
ДОБАВИТЬКДАТЕ(Данные.ДатаВремяОкончания, ЧАС, 2)

по два раза
ЧАС(ДОБАВИТЬКДАТЕ(Данные.ДатаВремяНачала, ЧАС, 2)) 
и
ЧАС(ДОБАВИТЬКДАТЕ(Данные.ДатаВремяОкончания, ЧАС, 2)) 
В первом случае выручит вложенный запрос, во втором - вычисление агрегатного максимума (ВЫБОР по сути вычисляет максимум).

Но, сокращаясь по числу символов, запрос удлинняется в строках, поэтому лучше оставить так как есть.
5. Glebis 13 07.06.18 11:40 Сейчас в теме
Спасибо за ответы. Сейчас буду обдумывать запрос с "Екатеринбургским временем".
6. RustRR 07.06.18 11:57 Сейчас в теме
(5)
ВЫБРАТЬ
    "Сотрудник1" КАК Сотрудник,
    ДАТАВРЕМЯ(2018, 6, 1, 20, 0, 0) КАК ДатаВремяНачала,
    ДАТАВРЕМЯ(2018, 6, 1, 23, 0, 0) КАК ДатаВремяОкончания
ПОМЕСТИТЬ Данные

На этом примере возвращает -180. Полагаю, должно быть 60.
7. ildarovich 7865 07.06.18 13:05 Сейчас в теме
(6) в варианте (4) возвращается 60
Оставьте свое сообщение

Для получения уведомлений об ответах подключите телеграм бот:
Инфостарт бот