Finding Max and Min Times - sql

I want to display max time and min time for a day in grid control using Visual Basic from a SQL Server database. My data currently looks like this:
UserID UserName Date Time
------------------------------------------
1 Shanks 30/1/2009 10:11:22
1 Shanks 30/1/2009 10:15:22
1 Shanks 30/1/2009 12:15:22
1 Shanks 30/1/2009 13:15:22
I need the output like this:
1 Shanks 30/1/2009 10:11:22 13:15:22
My table structure is:
UserID integer,
UserName varchar(20),
[Date] datetime,
[Time] datetime
How can I get that output from my data?

Is this what you're looking for?
SELECT UserID,
UserName,
Date,
MIN([Time]) AS MinTime,
MAX([Time]) AS MaxTime,
FROM Users
GROUP BY UserID, UserName, [Date]

SELECT
UserID,
UserName,
[Date],
MIN([Time]),
MAX([Time])
FROM
Table
GROUP BY
UserID,
UserName,
[Date]
Tested and working correctly with the following
DECLARE #Table TABLE (UserID INT,UserName VARCHAR(25),[Date] DATETIME,[Time] DATETIME)
INSERT INTO #Table VALUES(1, 'Shanks', '30 JAN 2009', '10:11:22');
INSERT INTO #Table VALUES(1, 'Shanks', '30 JAN 2009', '10:15:22');
INSERT INTO #Table VALUES(1, 'Shanks', '30 JAN 2009', '12:15:22');
INSERT INTO #Table VALUES(1, 'Shanks', '30 JAN 2009', '13:15:22');
INSERT INTO #Table VALUES(2, 'Shilpa', '3 JAN 2009', '10:11:22');
INSERT INTO #Table VALUES(2, 'Shilpa', '3 JAN 2009', '11:15:22');
INSERT INTO #Table VALUES(2, 'Shilpa', '3 JAN 2009', '12:15:22');
INSERT INTO #Table VALUES(2, 'Shilpa', '3 JAN 2009', '17:15:22');
SELECT
UserID,
UserName,
[Date],
MIN([Time]),
MAX([Time])
FROM
#Table
GROUP BY
UserID,
UserName,
[Date]
Results in
UserID UserName Date
----------- ------------------------- ----------------------- ----------------------- -----------------------
1 Shanks 2009-01-30 00:00:00.000 1900-01-01 10:11:22.000 1900-01-01 13:15:22.000
2 Shilpa 2009-01-03 00:00:00.000 1900-01-01 10:11:22.000 1900-01-01 17:15:22.000

Related

Converting GMT to EST in SQL Server

I have a table which stores records for Daylight saving and non-Daylight savings
CREATE TABLE #tmp
(
ID int,
IntervalName nvarchar(100),
StartDate datetime,
EndDate Datetime,
offset numeric(5, 2)
)
INSERT INTO #tmp
VALUES (1, 'EDT', '2022-03-13 07:00:00.000', '2022-11-06 06:00:00.000',-4.00)
INSERT INTO #tmp
VALUES (2, 'EST', '2022-11-06 06:00:00.000', '2023-03-12 07:00:00.000', -5.00)
I have my transactional tables which have Date column in the GMT timezone.
ID DatetimeGMT DatetimeLocal
---------------------------------------------------------
1 2022-11-05 07:00:00.000 2022-11-05 03:00:00.000
2 2022-11-10 06:00:00.000 2023-11-10 01:00:00.000
Now my DatetimeLocal column is calculating the offset hours based on the DatetimeGMT column.
The row with ID = 1 falls under offset -4 and the row with ID = 2 falls under offset -5.
Is there any suggestion how we can achieve this?
If you have the offsets all stored in a table like that, then you just need to JOIN to find the correct one.
eg
drop table if exists #tmp
drop table if exists #tran
go
CREATE TABLE #tmp
(
ID int,
IntervalName nvarchar(100),
StartDate datetime,
EndDate Datetime,
offset numeric(5, 2)
)
INSERT INTO #tmp
VALUES (1, 'EDT', '2022-03-13 07:00:00.000', '2022-11-06 06:00:00.000',-4.00)
INSERT INTO #tmp
VALUES (2, 'EST', '2022-11-06 06:00:00.000', '2023-03-12 07:00:00.000', -5.00)
create table #tran(id int primary key, DateTimeUTC datetime)
insert into #tran(id,DateTimeUTC) values (1,'2022-11-05 07:00:00.000'),(2,'2022-11-10 06:00:00.000')
select t.id, t.DateTimeUTC, dateadd(hour,offset,t.DateTimeUTC) DateTimeLocal, tz.offset
from #tran t
join #tmp tz
on t.DateTimeUTC >= tz.StartDate
and t.DateTimeUTC < tz.EndDate
outputs
id DateTimeUTC DateTimeLocal offset
----------- ----------------------- ----------------------- ---------------------------------------
1 2022-11-05 07:00:00.000 2022-11-05 03:00:00.000 -4.00
2 2022-11-10 06:00:00.000 2022-11-10 01:00:00.000 -5.00
SQL Server 2016 and later have the offsets built-in, so you can write queries like
select t.id,
t.DateTimeUTC,
cast(t.DateTimeUTC at time zone 'UTC' at time zone 'EASTERN STANDARD TIME' as datetime) DateTimeLocal
from #tran t

SQL Server copy muptiple rows of same table changing only date of a datetime column

I need to copy all rows of a table, changing only the date part of the Datetime. The query I've been testing in sqlfiddle, returns record count of 0, and obviously the #newDate + SameTime isn't part of the query, but I put it there so you know what I want to do.
CREATE TABLE Games
(
[Id] int IDENTITY(1, 1) PRIMARY KEY,
[GameTypeId] int,
[DateCreated] datetime,
[DateTime] datetime,
[Location] int
);
INSERT INTO Games ([GameTypeId], [DateCreated], [DateTime], [Location])
VALUES ('1', GETDATE(), '8/21/2021 8:00:00', '1'),
('2', GETDATE(), '8/21/2021 9:00:00', '1'),
('2', GETDATE(), '8/21/2021 10:00:00', '1');
The query I'm testing to copy the rows but is not working:
INSERT INTO Games (GameTypeId, DateCreated, DateTime, Location)
SELECT
GameTypeId, GETDATE(), #newDate + SameTime, Location
FROM
Games
WHERE
CONVERT(date, DateTime) = '8/21/2021'
The results from the initial insert:
Id GameTypeID DateCreated DateTime Location
--------------------------------------------------------------
1 1 8/15/2021 8/21/2021 8:00:00 1
2 1 8/15/2021 8/21/2021 9:00:00 1
3 1 8/15/2021 8/21/2021 10:00:00 1
And after the copy rows query, the results I want (but can't get). I want to copy everything to a new row, except the date only needs to change. The time stays the same:
4 1 8/20/2021 8/28/2021 8:00:00 1
5 1 8/20/2021 8/28/2021 9:00:00 1
6 1 8/20/2021 8/28/2021 10:00:00 1
You can use date arithmetic:
INSERT INTO Games (GameTypeId, DateCreated, DateTime, Location)
SELECT GameTypeId, DATEADD(day, 5, DateCreated),
DATEADD(day, 7, DateTime), location
FROM Games
WHERE CONVERT(date, DateTime) = '2021-08-21'

I want to split a row into multiple row based on month and year of the date in SQL Server

For example- I have below rows in my table:
id StartDate EndDate
101 1/03/2017 15/03/2017
102 27/03/2017 10/04/2017
103 25/12/2017 5/02/2018
I want the following output:
id month year
101 03 2017
102 03 2017
102 04 2017
103 12 2017
103 01 2018
103 02 2018
I have tried my best to find a solution but couldn't get through it. Any kind of help is always appreciated.
Thanks in advance.
If i correctly understood your problem then below query will work for you, this is giving the exact output required by you :
DECLARE #SAMPLE_DATA TABLE(id INT, StartDate DATETIME, EndDate DATETIME)
INSERT INTO #SAMPLE_DATA VALUES
(101, '03/1/2017', '03/15/2017'),
(102, '03/27/2017', '04/10/2017'),
(103, '12/25/2017', '02/5/2018')
;WITH SAMPLE_DATA
AS
(
SELECT ID,StartDate FROM #SAMPLE_DATA
UNION ALL
SELECT S1.id,DATEADD(D,1,S.STARTDATE) FROM SAMPLE_DATA S JOIN #SAMPLE_DATA S1 ON S.id=S1.id WHERE
DATEADD(D,1,S.STARTDATE)<=S1.EndDate
)
SELECT DISTINCT ID,MONTH(StartDate)[MONTH],YEAR(StartDate)[YEAR] FROM SAMPLE_DATA ORDER BY ID,YEAR,MONTH
Output of query :
-------------------
ID MONTH YEAR
-------------------
101 3 2017
102 3 2017
102 4 2017
103 12 2017
103 1 2018
103 2 2018
-------------------
You could try it with CTE
DECLARE #SampleDate AS TABLE (
Id int, StartDate date, EndDate date
)
INSERT INTO #SampleDate
(
Id,
StartDate,
EndDate
)
VALUES (101, '2017-03-01', '2017-03-15') ,
(102, '2017-03-27', '2017-04-10'),
(103, '2017-12-25', '2018-02-05')
DECLARE #MinDate date = '2017-01-01'
DECLARE #MaxDate date = '2020-01-01'
;WITH temp AS
(
SELECT #MinDate AS StartMonth, EOMOnth(#MinDate) AS EndMonth
UNION ALL
SELECT Dateadd(month, 1, t.StartMonth), Dateadd(month, 1, t.EndMonth) AS CurrentDate
FROM temp t
WHERE t.EndMonth < #MaxDate
)
SELECT DISTINCT sd.Id, DATEPART(Month,t.StartMonth) AS Month,
DATEPART(Year,t.StartMonth) AS Year
FROM temp t
INNER JOIN #SampleDate sd ON t.StartMonth BETWEEN sd.StartDate AND sd.EndDate
OR t.EndMonth BETWEEN sd.StartDate AND sd.EndDate
OPTION (MAXRECURSION 0)
Demo link: RexTester

SQL TSQL for Workers per Hour

I have a log with fingerprint timestamps as follows:
Usr TimeStamp
-------------------------
1 2015-07-01 08:01:00
2 2015-07-01 08:05:00
3 2015-07-01 08:07:00
1 2015-07-01 10:05:00
3 2015-07-01 11:00:00
1 2015-07-01 12:01:00
2 2015-07-01 13:03:00
2 2015-07-01 14:02:00
1 2015-07-01 16:03:00
2 2015-07-01 18:04:00
And I wish an output of workers per hour (rounding to nearest hour)
The theoretical output should be:
7:00 0
8:00 3
9:00 3
10:00 2
11:00 1
12:00 2
13:00 1
14:00 2
15:00 2
16:00 1
17:00 1
18:00 0
19:00 0
Can anyone think on how to approach this as SQL or if no other way, through TSQL?
Edit: The timestamps are logins and logouts of the different users. So at 8am 3 users logged in and the same 3 are still working at 9am. One of them leaves at 10am. etc
To start with you can use datepart to get hours for the days as following and then use group by user
SELECT DATEPART(HOUR, GETDATE());
SQL Fiddle
SELECT Convert(varchar(5),DATEPART(HOUR, timestamp)) + ':00' as time,
count(usr) as users
from tbl
group by DATEPART(HOUR, timestamp)
You need a datetime hour table to do this.
Note : This is just a example of showing how the query should work for one day. Replace the CTE with datetime hour table. In datetime hour table every date should start with 07:00:00 hour and end with 19:00:00 hour
When you want to do this for more than one day then you may have to include the Cast(dt.date_time AS DATE) in select and group by to differentiate the hour belong to which day
WITH datetime_table
AS (SELECT '2015-07-01 07:00:00' AS date_time
UNION ALL
SELECT '2015-07-01 08:00:00'
UNION ALL
SELECT '2015-07-01 09:00:00'
UNION ALL
SELECT '2015-07-01 10:00:00'
UNION ALL
SELECT '2015-07-01 11:00:00'
UNION ALL
SELECT '2015-07-01 12:00:00'
UNION ALL
SELECT '2015-07-01 13:00:00'
UNION ALL
SELECT '2015-07-01 14:00:00'
UNION ALL
SELECT '2015-07-01 15:00:00'
UNION ALL
SELECT '2015-07-01 16:00:00'
UNION ALL
SELECT '2015-07-01 17:00:00'
UNION ALL
SELECT '2015-07-01 18:00:00'
UNION ALL
SELECT '2015-07-01 19:00:00')
SELECT Datepart(hour, dt.date_time),
Hour_count=Count(t.id)
FROM datetime_table dt
LEFT OUTER JOIN Yourtable t
ON Cast(t.dates AS DATE) = Cast(dt.date_time AS DATE)
AND Datepart(hour, t.dates) =
Datepart(hour, dt.date_time)
GROUP BY Datepart(hour, dt.date_time)
SQLFIDDLE DEMO
You just need to group by hours and date. Check this below query and hope this helps you:
Create table #t1
(
usr int,
timelog datetime
)
Insert into #t1 values(1, '2015-07-01 08:01:00')
Insert into #t1 values(2, '2015-07-01 08:05:00')
Insert into #t1 values(3, '2015-07-01 08:07:00')
Insert into #t1 values(1, '2015-07-01 10:05:00')
Insert into #t1 values(3, '2015-07-01 11:00:00')
Insert into #t1 values(1, '2015-07-01 12:01:00')
Insert into #t1 values(2, '2015-07-01 13:03:00')
Insert into #t1 values(2, '2015-07-01 14:02:00')
Insert into #t1 values(1, '2015-07-01 16:03:00')
Insert into #t1 values(2, '2015-07-01 18:04:00')
Select cast(timelog as varchar(11)) as LogDate, Datepart(hour, timelog) as LogTime, count(usr) as UserCount from #t1
Group by Datepart(hour, timelog), cast(timelog as varchar(11))
The harder part is creating the zeros where data is missing. The usual approach is to generate a list of all possible "slots" and then do an outer join to the actual data. I'm assuming that you only want to run this for a single day at a time.
My approach, which is just an example, works because it does a cross join of two tables with 6 and 4 rows respectively and 6 times 4 is 24.
select f1.d * 6 + f0.d, coalesce(data.cnt, 0)
from
(
select 0 as d union all select 1 union all select 2 union all
select 3 union all select 4 union all select 5
) as f0,
(
select 0 as d union all select 1 union all
select 2 union all select 3
) as f1
left outer join
(
select
cast(datepart(hh, TimeStamp) as varchar(2)) + ':00' as hr,
count(*) as cnt
from LOG
group by datepart(hh, TimeStamp)
) as data
on data.hr = f1.d * 6 + f0.d
First you need to round up time to the closest hour
DATEADD(HOUR, DATEDIFF(HOUR, 0, DATEADD(MI, 30, TimeStamp)), 0)
As you see first we add 30 minutes to the original time (DATEADD(MI, 30, TimeStamp))
This approach will round up 08:04 to 08:00 or 07:58 to 8:00 too.
As I assume some workers can start working little bid early
SELECT DATEADD(HOUR, DATEDIFF(HOUR, 0, DATEADD(MI, 30, TimeStamp)), 0) As FingertipTime
FROM Fingertips
You can create a Computed column if you use rounded timestamp often
ALTER TABLE Fingertips ADD RoundedTimeStamp AS (DATEADD(HOUR, DATEDIFF(HOUR, 0, DATEADD(MI, 30, TimeStamp)), 0));
For comparing timestamps with constants of work hours you can find different methods. I will use a variable of type TABLE where i generate work hours for current day
Then using LEFT JOIN and GROUP BY we get quantity of timestamps
DECLARE #WorkHours TABLE(WorkHour DATETIME)
INSERT INTO #WorkHours (WorkHour) VALUES
('2015-07-01 07:00'),
('2015-07-01 08:00'),
('2015-07-01 09:00'),
('2015-07-01 10:00'),
('2015-07-01 11:00'),
('2015-07-01 12:00'),
('2015-07-01 13:00'),
('2015-07-01 14:00'),
('2015-07-01 15:00'),
('2015-07-01 16:00'),
('2015-07-01 17:00'),
('2015-07-01 18:00'),
('2015-07-01 19:00')
SELECT wh.Workhour
, COUNT(ft.TimeStamp) As Quantity
FROM #WorkHours wh
LEFT JOIN Fingertips ft ON ft.RoundedTimeStamp = wh.WorkHour
GROUP BY wh.WorkHour
Check this SQL Fiddle
Many separate parts that have to be glued together to get this done.
First rounding, this is easily done with obtaining the hour part of the date + 30 minutes. Then determine start and end records. If there are no fields to indicate this and assuming the first occurrence of a day is the login or start, you can use row_number and use the odd numbers as start records.
Then start and end have to be coupled, in sql server 2012 and higher this can be easily done with the lead function
To get the missing hours a sequence has to be created with all the hours. Several options for this (good link here), but I like the approach of using row_number on a table that is sure to contain enough rows (with a proper column for order by), such as sys.all_objects used in the link. That way hours 7 to 19 could be created as: select top 13 ROW_NUMBER() over (order by object_id) + 6 [Hour] from sys.all_objects
If there's only one date to check on, the query can simple left join on the hour of the timestamp fingerprints. If there are more dates, a second sequence could be created cross applied to the times to get all dates. Assuming the one date, final code would be:
declare #t table(Usr int, [timestamp] datetime)
insert #t values
(1 , '2015-07-01 08:01:00'),
(2 , '2015-07-01 08:05:00'),
(3 , '2015-07-01 08:07:00'),
(1 , '2015-07-01 10:05:00'),
(3 , '2015-07-01 11:00:00'),
(1 , '2015-07-01 12:01:00'),
(2 , '2015-07-01 13:03:00'),
(2 , '2015-07-01 14:02:00'),
(1 , '2015-07-01 16:03:00'),
(2 , '2015-07-01 18:04:00'),
(2 , '2015-07-01 18:04:00')
;with usrHours as
(
select Usr, datepart(hour, DATEADD(minute,30, times.timestamp)) [Hour] --convert all times to the rounded hour (rounding by adding 30 minutes)
, ROW_NUMBER() over (partition by usr order by [timestamp] ) rnr
from #t times --#t should be your logging table
), startend as --get next (end) hour by using lead
(
select Usr, [hour] StartHour , LEAD([Hour]) over (partition by usr order by rnr) NextHour ,rnr
from usrHours
),hours as --sequence of hours 7 to 19
(
select top 13 ROW_NUMBER() over (order by object_id) + 6 [Hour] from sys.all_objects
)
select cast([Hour] as varchar) + ':00' [Hour], COUNT(startend.usr) Users
from hours --sequence is leading
left join startend on hours.Hour between startend.StartHour and startend.NextHour
and rnr % 2 = 1 --every odd row number is a start time
group by Hours.hour
Here is my final working code:
create table tsts(id int, dates datetime)
insert tsts values
(1 , '2015-07-01 08:01:00'),
(2 , '2015-07-01 08:05:00'),
(3 , '2015-07-01 08:07:00'),
(1 , '2015-07-01 10:05:00'),
(3 , '2015-07-01 11:00:00'),
(1 , '2015-07-01 12:01:00'),
(2 , '2015-07-01 13:03:00'),
(2 , '2015-07-01 14:02:00'),
(1 , '2015-07-01 16:03:00'),
(2 , '2015-07-01 18:04:00')
select horas.hora, isnull(sum(math) over(order by horas.hora rows unbounded preceding),0) as Employees from
(
select 0 as hora union all
select 1 as hora union all
select 2 as hora union all
select 3 as hora union all
select 4 as hora union all
select 5 as hora union all
select 6 as hora union all
select 7 as hora union all
select 8 as hora union all
select 9 as hora union all
select 10 as hora union all
select 11 as hora union all
select 12 as hora union all
select 13 as hora union all
select 14 as hora union all
select 15 as hora union all
select 16 as hora union all
select 17 as hora union all
select 18 as hora union all
select 19 as hora union all
select 20 as hora union all
select 21 as hora union all
select 22 as hora union all
select 23
) as horas
left outer join
(
select hora, sum(math) as math from
(
select id, hora, iif(rowid%2 = 1,1,-1) math from
(
select row_number() over (partition by id order by id, dates) as rowid, id, datepart(hh,dateadd(mi, 30, dates)) as hora from tsts
) as Q1
) as Q2
group by hora
) as Q3
on horas.hora = Q3.hora
SQL Fiddle

SQL query to group by day and calculate elapsed time (aggregating rows)

I'm using SQL Server 2008 and looking how to create such a query:
I have a table:
Username nvarchar(50), LogDate datetime
Test1, 2012.01.01 00:00:00
Test2, 2012.01.01 00:00:02
Test1, 2012.01.01 00:00:05
Test3, 2012.01.01 00:00:06
Test1, 2012.01.02 00:01:01
Test2, 2012.01.02 00:02:50
Test1, 2012.01.02 00:01:01
Every few seconds users send updates to insert current datetime with his username in to the log table. I need to calculate how much hours users were "online" every day.
I assume query should sum DateDiff between two rows with the same username grouped by day.
I tried using rank, but haven't got what I want.
Thank you for your help in advance.
You can extract the date part of the DateTime and group by the UserName to use DateDiff on the MIN / MAX Values:
Table and Data Setup:
create table UserLog (Username nvarchar(50), LogDate DateTime);
insert into UserLog Values('Test1', '2012-01-01 00:00:00');
insert into UserLog Values('Test2', '2012-01-01 00:00:02');
insert into UserLog Values('Test1', '2012-01-01 00:00:05');
insert into UserLog Values('Test3', '2012-01-01 00:00:06');
insert into UserLog Values('Test3', '2012-01-01 00:01:26');
insert into UserLog Values('Test3', '2012-01-01 00:03:22');
insert into UserLog Values('Test3', '2012-01-01 00:05:42');
insert into UserLog Values('Test3', '2012-01-01 00:00:06');
insert into UserLog Values('Test1', '2012-01-02 00:01:01');
insert into UserLog Values('Test2', '2012-01-02 00:02:50');
insert into UserLog Values('Test1', '2012-01-02 00:01:01');
Then you can SELECT as follows:
select UserName, CAST(LogDate AS DATE) as BusinessDay,
MIN(LogDate) as FirstLogEntry, MAX(LogDate) as LastLogEntry,
DATEDIFF(second,MIN(LogDate), MAX(LogDate)) as ElapsedSeconds
FROM UserLog
GROUP BY Username, CAST(LogDate AS DATE)
This will yield the following results and you can calculate hours from the seconds. I showed seconds based on your sample data with test3 user expanded:
UserName BusinessDay FirstLogEntry LastLogEntry ElapsedSeconds
-------- ----------- ----------------------- ----------------------- --------------
Test1 2012-01-01 2012-01-01 00:00:00.000 2012-01-01 00:00:05.000 5
Test2 2012-01-01 2012-01-01 00:00:02.000 2012-01-01 00:00:02.000 0
Test3 2012-01-01 2012-01-01 00:00:06.000 2012-01-01 00:05:42.000 336
Test1 2012-01-02 2012-01-02 00:01:01.000 2012-01-02 00:01:01.000 0
Test2 2012-01-02 2012-01-02 00:02:50.000 2012-01-02 00:02:50.000 0