powered by simpleCommunicator - 2.0.51     © 2025 Programmizd 02
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / Вычислить отработанное время
25 сообщений из 25, страница 1 из 1
Вычислить отработанное время
    #40065427
Kaktyc007
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Добрый день, имеется таблица
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
CREATE TABLE [dbo].[УРВ] (
    [ИдУРВ]        INT            IDENTITY (1, 1) NOT NULL,
    [ИдСотрудника] INT            NULL,
    [ДатаВремя]    SMALLDATETIME  NULL,
    [Действие]     NVARCHAR (MAX) NULL,
    PRIMARY KEY CLUSTERED ([ИдУРВ] ASC),
    CONSTRAINT [FK_УРВ_Сотрудники] FOREIGN KEY ([ИдСотрудника]) REFERENCES [dbo].[Сотрудники] ([ИдСотрудника])
);


Каждый день сотрудники при входе и выходе с работы отмечаются, в [Действие] пишется "Начало рабочего дня" и "Конец рабочего дня".
Знаю есть DATEDIFF, но как определить время работы за один день, для определенного сотрудника знаю, но как составить таблицу со списком сотрудников и количеством отработанного времени за 1 день и за 1 неделю?
...
Рейтинг: 0 / 0
Вычислить отработанное время
    #40065502
SERG1257
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Если у вас все аккуратно - в каждый день только два действия "Начало рабочего дня" и "Конец рабочего дня" причем
"Начало рабочего дня" < "Конец рабочего дня"
тогда в лоб
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
select [ИдСотрудника], [ДатаВремя] as d_start, 
	(select max([ДатаВремя]) from [dbo].[УРВ] b
	  where a.[ИдСотрудника]=b.[ИдСотрудника]
		and b.[Действие]='Конец рабочего дня'
		and cast(a.[ДатаВремя] as date)<= b.[ДатаВремя]
		and b.[ДатаВремя]<dateadd(day,1,cast(a.[ДатаВремя] as date))
	) as d_end
from [dbo].[УРВ] a
where [Действие]='Начало рабочего дня'


Данный запрос будет правильно работать только на правильных данных.
В случае бардака в данных он вернет херню
...
Рейтинг: 0 / 0
Вычислить отработанное время
    #40065506
Kaktyc007
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
SERG1257, Спасибо! Только мне надо было найти разницу во времени, т.е сколько сотрудник провел времени на роботе.
Как быть если в [Действие] хранится не только начало и конец рабочего дня?
...
Рейтинг: 0 / 0
Вычислить отработанное время
    #40065507
Kaktyc007
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
SERG1257, Спасибо! Только мне надо было найти разницу во времени, т.е сколько сотрудник провел времени на роботе.
Как быть если в [Действие] хранится не только начало и конец рабочего дня?
...
Рейтинг: 0 / 0
Вычислить отработанное время
    #40065514
godsql
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Kaktyc007,
просто пример, отражающий последовательность действий для одного человека
Принимаем, что есть обязательное 1-е событие "вход", потом, 2-е, соответственно, "выход"
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
;with #t as(
select 1 as id, getdate() as dotime
union all
select id+1, dateadd(HH, 4, dotime) from #t
where id <20
), 
#t2 as (
select 
id, 
dotime, 
lead(dotime) over(order by (select 1/0)) as ndotime, 
datediff(hh, dotime, lead(dotime) over(order by (select 1/0)) ) as dtime   
from #t
),
#t3 as (
select id, dtime from  #t2
where id %2 <> 0
)
select sum(dtime) as worktime from #t3
...
Рейтинг: 0 / 0
Вычислить отработанное время
    #40065543
SERG1257
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Kaktyc007 Как быть если в [Действие] хранится не только начало и конец рабочего дня? Приведите пример, написав скрипты для заполнения данными таблицы [dbo].[УРВ]

Что должен показать отчет в случае некорректных данных (Начал работу в 23:00 вечера, закончил в два ночи)
Зашел на работу в 8:00 а выхода в 17:00 нет, следующая запись начало работы в 8 следующего дня
И наоборот имеем несколько записей "Начало рабочего дня" в 8:00, 9:00 и 10:00
Или несколько записей "Конец рабочего дня" в 17:00, 18:00 и 19:00

Вы можете быть свято уверенны, что такого не будет, потому что нибудет никогда, но ответ - что должен показать отчет для таких данных должен быть приготовлен.
...
Рейтинг: 0 / 0
Вычислить отработанное время
    #40065565
Kaktyc007
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
SERG1257
Приведите пример, написав скрипты для заполнения данными таблицы [dbo].[УРВ]


Код: sql
1.
2.
3.
insert into УРВ (ИдСотрудника, ДатаВремя, Действие) values ('6', '2021-04-25 08:00:00', N'Начало рабочего дня')
insert into УРВ (ИдСотрудника, ДатаВремя, Действие) values ('6', '2021-04-25 12:00:00', N'Обед')
insert into УРВ (ИдСотрудника, ДатаВремя, Действие) values ('6', '2021-04-25 17:00:00', N'Конец рабочего дня')



SERG1257
Что должен показать отчет в случае некорректных данных (Начал работу в 23:00 вечера, закончил в два ночи)
Зашел на работу в 8:00 а выхода в 17:00 нет, следующая запись начало работы в 8 следующего дня
И наоборот имеем несколько записей "Начало рабочего дня" в 8:00, 9:00 и 10:00
Или несколько записей "Конец рабочего дня" в 17:00, 18:00 и 19:00

В первом случае 3 часа. А в остальных случаях не допускать этого.
...
Рейтинг: 0 / 0
Вычислить отработанное время
    #40065569
Kaktyc007
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Вот пример для вычисления длительности рабочего дня в минутах для одного сотрудника с одной записью в таблице, если добавить еще одно появления на работе, уже ругается: "Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression."
Код: sql
1.
2.
SELECT ИдСотрудника, DATEDIFF (MINUTE, (select ДатаВремя from УРВ where Действие=N'Начало рабочего дня' and ИдСотрудника=5 ), (select ДатаВремя from УРВ where Действие=N'Конец рабочего дня' and ИдСотрудника=5)) 
from УРВ where ИдСотрудника=5 and Действие=N'Начало рабочего дня'
...
Рейтинг: 0 / 0
Вычислить отработанное время
    #40065576
aleks222
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Kaktyc007
Вот пример для вычисления длительности рабочего дня в минутах для одного сотрудника с одной записью в таблице, если добавить еще одно появления на работе, уже ругается: "Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression."
Код: sql
1.
2.
SELECT ИдСотрудника, DATEDIFF (MINUTE, (select ДатаВремя from УРВ where Действие=N'Начало рабочего дня' and ИдСотрудника=5 ), (select ДатаВремя from УРВ where Действие=N'Конец рабочего дня' and ИдСотрудника=5)) 
from УРВ where ИдСотрудника=5 and Действие=N'Начало рабочего дня'



Вот в чем проблема "настоящих программиздов"?
Арифметику они не учили - вот в чем их проблема.

1. Если в табличке "все хорошо", т.е. каждому началу соответствует каждый конец

2.
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
declare @now datetime = getdate();
SELECT ИдСотрудника
          , ВремяРаботыМинуты = sum( case Действие when N'Начало рабочего дня' then - datediff(minute, ДатаВремя, @now)
                                                   when N'Конец рабочего дня' then + datediff(minute, ДатаВремя, @now)
                                                  else 0
                                     end
                                  )
from УРВ 
group by ИдСотрудника



3. Если же там "не фсе хорошо" - надо сначала решить эту проблему...
...
Рейтинг: 0 / 0
Вычислить отработанное время
    #40065578
Kaktyc007
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
aleks222
Kaktyc007
Вот пример для вычисления длительности рабочего дня в минутах для одного сотрудника с одной записью в таблице, если добавить еще одно появления на работе, уже ругается: "Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression."
Код: sql
1.
2.
SELECT ИдСотрудника, DATEDIFF (MINUTE, (select ДатаВремя from УРВ where Действие=N'Начало рабочего дня' and ИдСотрудника=5 ), (select ДатаВремя from УРВ where Действие=N'Конец рабочего дня' and ИдСотрудника=5)) 
from УРВ where ИдСотрудника=5 and Действие=N'Начало рабочего дня'



Вот в чем проблема "настоящих программиздов"?
Арифметику они не учили - вот в чем их проблема.

1. Если в табличке "все хорошо", т.е. каждому началу соответствует каждый конец

2.
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
declare @now datetime = getdate();
SELECT ИдСотрудника
          , ВремяРаботыМинуты = sum( case Действие when N'Начало рабочего дня' then - datediff(minute, ДатаВремя, @now)
                                                   when N'Конец рабочего дня' then + datediff(minute, ДатаВремя, @now)
                                                  else 0
                                     end
                                  )
from УРВ 
group by ИдСотрудника



3. Если же там "не фсе хорошо" - надо сначала решить эту проблему...

Спасибо, но зачем брать текущее системное время? И как вывести отработанное время за неделю, а не за все время?
...
Рейтинг: 0 / 0
Вычислить отработанное время
    #40065580
aleks222
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Kaktyc007
aleks222
пропущено...


Вот в чем проблема "настоящих программиздов"?
Арифметику они не учили - вот в чем их проблема.

1. Если в табличке "все хорошо", т.е. каждому началу соответствует каждый конец

2.
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
declare @now datetime = getdate();
SELECT ИдСотрудника
          , ВремяРаботыМинуты = sum( case Действие when N'Начало рабочего дня' then - datediff(minute, ДатаВремя, @now)
                                                   when N'Конец рабочего дня' then + datediff(minute, ДатаВремя, @now)
                                                  else 0
                                     end
                                  )
from УРВ 
group by ИдСотрудника



3. Если же там "не фсе хорошо" - надо сначала решить эту проблему...

Спасибо, но 1) зачем брать текущее системное время? И 2)как вывести отработанное время за неделю, а не за все время?


1. Ну... арифметика - полезная наука. Учи.
2. Ваще-то, оно выводит "за все время, что есть в таблице". Т.е. если в таблице оставить неделю - будет за неделю. "Оставить" = наложить нужный фильтр или добавить группировку по неделям.
...
Рейтинг: 0 / 0
Вычислить отработанное время
    #40065584
Kaktyc007
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
aleks222, а арифметика тут причем? если datediff находит разницу между записанным и текущим временем
...
Рейтинг: 0 / 0
Вычислить отработанное время
    #40065588
godsql
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Kaktyc007
aleks222, а арифметика тут причем? если datediff находит разницу между записанным и текущим временем

Если вычесть Разницу между "концом" и "текущим" из Разницы между "началом" и "текущим" - это и будет разница между "началом" и "концом".
в-а = (1000+в) - (1000+а)
...
Рейтинг: 0 / 0
Вычислить отработанное время
    #40065594
Kaktyc007
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
godsql
Kaktyc007
aleks222, а арифметика тут причем? если datediff находит разницу между записанным и текущим временем

Если вычесть Разницу между "концом" и "текущим" из Разницы между "началом" и "текущим" - это и будет разница между "началом" и "концом".
в-а = (1000+в) - (1000+а)

почему нельзя сразу вычесть разницу между концом и началом?
...
Рейтинг: 0 / 0
Вычислить отработанное время
    #40065601
Kaktyc007
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Как объединить эти два запроса в один?
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
select [ИдСотрудника], [ДатаВремя] as d_start, 
	(select max([ДатаВремя]) from [dbo].[УРВ] b
	  where a.[ИдСотрудника]=b.[ИдСотрудника]
		and b.[Действие]='Конец рабочего дня'
		and cast(a.[ДатаВремя] as date)<= b.[ДатаВремя]
		and b.[ДатаВремя]<dateadd(day,1,cast(a.[ДатаВремя] as date))
	) as d_end
from [dbo].[УРВ] a
where [Действие]='Начало рабочего дня'



Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
declare @now datetime = getdate();
SELECT ИдСотрудника
          , ВремяРаботыМинуты = sum( case Действие when N'Начало рабочего дня' then - datediff(minute, ДатаВремя, @now)
                                                   when N'Конец рабочего дня' then + datediff(minute, ДатаВремя, @now)
                                                  else 0
                                     end
                                  )
from УРВ 
group by ИдСотрудника
...
Рейтинг: 0 / 0
Вычислить отработанное время
    #40065633
SERG1257
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Kaktyc007 "Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression."правильно ругается. Выбирайте одно значение

Kaktyc007 В первом случае 3 часа.То бишь эта ситуация вполне законна.
Kaktyc007 А в остальных случаях не допускать этого. Про не допускать это триггеры и акты (отчет о кривых данных)
Определяем бизнес правило - датой конца работы считается ближайшая дата от начала работы (в тот же или последующие дни)
Предполагаем что кривых данных в таблице нет - мы выловили их ранее.
Пишем подзапрос возвращающий ближайшую дату конца работы

Код: sql
1.
2.
3.
4.
5.
(select min([ДатаВремя]) from [dbo].[УРВ] b
	  where a.[ИдСотрудника]=b.[ИдСотрудника]
		and b.[Действие]='Конец рабочего дня'
		and a.[ДатаВремя]<b.[ДатаВремя]
) as d_end


обратите внимание на агрегатную функцию min - специально чтобы не было ошибки выше
Также можно сделать top 1

Код: sql
1.
2.
3.
4.
5.
6.
(select top 1 [ДатаВремя] from [dbo].[УРВ] b
	  where a.[ИдСотрудника]=b.[ИдСотрудника]
		and b.[Действие]='Конец рабочего дня'
		and a.[ДатаВремя]<b.[ДатаВремя]
order by [ДатаВремя] 
) as d_end



У настоящих программистов появится соблазн засунуть этот подзапрос в скалярную функцию, не делайте этого - это убъет производительность

Дальше фильтр по [dbo].[УРВ] на день или неделю, и сумму (в часах или минутах) разницы между d_start и d_end
Код: sql
1.
2.
3.
4.
5.
select [ИдСотрудника],a.[ДатаВремя] as d_start, sum(datediff(MINUTE,d_start,d_end))
from [dbo].[УРВ] a
where [начало отчетного периода]<=a.[ДатаВремя] and a.[ДатаВремя]<[конец отчетного периода]
and a.[Действие]='Начало рабочего дня'
group by [ИдСотрудника]


Запрос специально неправильный (без подстановки d_start и d_end) чтобы служба медом не казалась
...
Рейтинг: 0 / 0
Вычислить отработанное время
    #40065635
aleks222
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
SERG1257

У настоящих программистов появится соблазн засунуть этот подзапрос в скалярную функцию, не делайте этого - это убъет производительность


Настоящие программизды не пользуют подзапросы без крайней нужды.
В данном, конкретном случае никаких подзапросов не надо.
От слова совсем.
...
Рейтинг: 0 / 0
Вычислить отработанное время
    #40066331
Wlr-l
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Решение от алекса безусловно сильное, как и совет "арифметика - полезная наука. Учи."
Я решил последовать совету, чтобы освежить свои знания арифметики.

Очевидно, что в предложенном решении:
1. Для всех событий их ДатаВремя всегда будет предшествовать @now.
2. Точка 'Начало рабочего дня' будет находиться на оси времени левее точки 'Конец рабочего дня'.
3. От точки 'Начало рабочего дня' до точки @now пройдет больше времени, чем
от точки 'Конец рабочего дня' до точки @now.

Следовательно, значение -datediff(minute, ДатаВремя, @now) = -(@now - 'Начало рабочего дня') будет отрицательным и определять знак суммы.

Т.е. запрос вернет отрицательное значение.

Первой мыслью было взять @now меньше любого ДатаВремя, чтобы получить положительный результат!
Но ведь дан хороший совет, проверь арифметикой!

В этом случае
1. Для всех событий их ДатаВремя всегда будет позже @now.
2. Точка 'Начало рабочего дня' будет находиться на оси времени левее точки 'Конец рабочего дня'.
3. От точки @now до точки 'Конец рабочего дня' пройдет больше времени, чем
от точки @now до точки 'Начало рабочего дня'.

Теперь знак суммы будет определять выражение (@now - 'Конец рабочего дня'), которое так же будет отрицательным.

Т.е. и в этом случае запрос вернет отрицательное значение.

Здесь я вспомнил об абсолютном значении, но был же дан хороший совет!

Можно просто изменить порядок дат в datediff, но еще проще поменять знаки в операторе case.
...
Рейтинг: 0 / 0
Вычислить отработанное время
    #40066436
aleks222
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Wlr-l

1. Для всех событий их ДатаВремя всегда будет предшествовать @now.

Это абсолютно пофиг.
Можно использовать любую фиксированную точку на шкале времени.
Арифметику ты тоже не доучил.
...
Рейтинг: 0 / 0
Вычислить отработанное время
    #40066494
Wlr-l
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
aleks222
Wlr-l

1. Для всех событий их ДатаВремя всегда будет предшествовать @now.

Это абсолютно пофиг.
Можно использовать любую фиксированную точку на шкале времени.
Арифметику ты тоже не доучил.


1. Так я сразу сказал, что благодаря твоему совету я решил вспомнить то, что недоучил в школе.

2. Действительно, можно использовать любую точку на шкале времени.
У тебя она описана так: declare @now datetime = getdate();

3. В твоем запросе получается, что если алекс пришел на работу в 08, а ушел в 10,
то он проработал -2 часа. Далее, если часовая ставка 1 руб., то он заработал -2 рубля.
Т.е. алекс, проработав в организации с 08 до 10, остался должен этой организации.

4. Я сказал, что твое решение сильное, по сравнению с другими предложенными решениями,
и предложил его чуть-чуть подправить, чтобы время работы было положительным числом.

5. Кроме арифметики есть еще и логика.
...
Рейтинг: 0 / 0
Вычислить отработанное время
    #40066534
982183
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Та система доступа, которую видел я, не позволит войти второй раз по одной и той же карточке.
Как и два раза выйти.
...
Рейтинг: 0 / 0
Вычислить отработанное время
    #40066538
aleks222
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Wlr-l

3. В твоем запросе получается, что если алекс пришел на работу в 08, а ушел в 10,
то он проработал -2 часа. Далее, если часовая ставка 1 руб., то он заработал -2 рубля.
Т.е. алекс, проработав в организации с 08 до 10, остался должен этой организации.

Не придирайся.
Это минус из кармана работодателя.
...
Рейтинг: 0 / 0
Вычислить отработанное время
    #40066638
Wlr-l
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Да я и не придираюсь.

Вспомнилась фраза классика МЛ: "Учиться арифметике настоящим образом".
Вот я и учусь арифметике уже третий раз (сам, с дочкой, теперь с внучкой).

Операция вычитания - это НЕ коммутативная операция .
Собственно, как и сложение чисел с разными знаками.

И эта не коммутативность может сыграть злую шутку.
...
Рейтинг: 0 / 0
Вычислить отработанное время
    #40066652
msLex
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Wlr-l
Собственно, как и сложение чисел с разными знаками.


Сложение чисел с разными знаками (и вообще всех действительных чисел), таки, коммутативная операция.
...
Рейтинг: 0 / 0
Вычислить отработанное время
    #40066717
Wlr-l
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Да, это так, конечно, сложение чисел коммутативно.

Я хотел сказать, что в рассматриваемом запросе имеет значение, где стоят унарные минус и плюс, хотя происходит суммирование разностей двух точек времени, которые (разности) в зависимости от выбранной опорной точки могут быть как положительными, так и отрицательными.
Если они стоят так, как в оригинальном варианте, то мы получим отрицательное значение отработанного времени.
Поменяв их местами, получим положительное значение отработанного времени.

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

Поэтому я скажу спасибо алексу за это его решение задачи ТС!
...
Рейтинг: 0 / 0
25 сообщений из 25, страница 1 из 1
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / Вычислить отработанное время
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]