How to find time average of two datetime rows in SQL? - sql

I have a table with datatime and person identity in which I wanted to find difference between time for each day and find the average in SQL.
The input table,
+---------------------+----------+--------+
| Datetime | Identity | Type |
+---------------------+----------+--------+
| 28/08/2019 4:00:00 | ABC | Entry |
| 28/08/2019 14:00:00 | ABC | Exit |
| 29/08/2019 6:00:00 | ABC | Entry |
| 29/08/2019 6:10:00 | ABC | Exit |
| 30/08/2019 8:00:00 | ABC | Entry |
| 30/08/2019 17:00:00 | ABC | Exit |
+---------------------+----------+--------+
Is it possible to create another table with the below data in SQL?
+------------+----------+-----------+
| Date | Identity | Time(Min) |
+------------+----------+-----------+
| 28/08/2019 | ABC | 600 |
| 29/08/2019 | ABC | 10 |
| 30/08/2019 | ABC | 540 |
+------------+----------+-----------+
Thank you in advance.

You can try below - it'll work for mysql
select cast(datetime as date),
TIMESTAMPDIFF(MINUTE,min(case when Type='Entry' then datetime end),
max(case when Type='Exit' then datetime end))
from tablename
group by cast(datetime as date)
OR for sql server -
select cast(datetime as date),
datediff(mi,min(case when Type='Entry' then datetime end),
max(case when Type='Exit' then datetime end))
from tablename
group by cast(datetime as date)

I think this should give you what you need, in SQL Server:
select cast([datetime] as date) as [Date]
, [Identity]
, datediff(minute, min([datetime]), max([datetime])) as [Time(Min)]
from table_name
group by cast([datetime] as date)
, [Identity]

Related

Break a date range into hours per day for each job

Yesterday I had asked for an efficient way to break a date range into hours per day and received an answer at the following link...
Is there an efficient way to break a date range into hours per day?
Now I need to go a step further and generate the same thing for each job in a list. I have a table with the following sample information...
+-------+-------------------------+-------------------------+
| JobID | StartDate | EndDate |
+-------+-------------------------+-------------------------+
| 1 | 2015-01-27 07:32:35.000 | 2015-01-28 14:39:35.000 |
| 2 | 2015-01-27 07:32:35.000 | 2015-01-29 16:39:35.000 |
| 3 | 2015-03-02 09:46:25.000 | 2015-03-05 17:24:15.000 |
+-------+-------------------------+-------------------------+
And I need to get a list like the following...
+-------+------------+-------+
| JobID | Date | Hours |
+-------+------------+-------+
| 1 | 2015-01-27 | 16.47 |
| 1 | 2015-01-28 | 14.65 |
| 2 | 2015-01-27 | 16.47 |
| 2 | 2015-01-28 | 24.00 |
| 2 | 2015-01-29 | 16.65 |
| 3 | 2015-03-02 | 14.23 |
| 3 | 2015-03-03 | 24.00 |
| 3 | 2015-03-04 | 24.00 |
| 3 | 2015-03-05 | 17.40 |
+-------+------------+-------+
Can the recursive CTE (from the link I included) be modified to include a JobID?
Thanks,
Carl
Here is what I came up with for a solution...
DECLARE #testTable TABLE (JobID INT, startdate DATETIME, enddate DATETIME);
INSERT INTO #testTable VALUES (1,'2015-01-27 07:32:35.000','2015-01-28 14:39:35.000');
INSERT INTO #testTable VALUES (2,'2015-01-27 07:32:35.000','2015-01-29 16:39:35.000');
INSERT INTO #testTable VALUES (3,'2015-03-02 09:46:25.000','2015-03-02 17:24:15.000');
WITH cte AS (
SELECT JobID,CAST(startdate AS DATE) startdate,DATEDIFF(minute, startdate, DATEADD(DAY, 1, CAST(startdate AS DATE) ) ) / 60.0 hours,enddate from #testTable
UNION ALL
SELECT JobID,DATEADD(DAY,1, startdate), DATEDIFF(minute, DATEADD(DAY,1, startdate), CASE WHEN DATEADD(DAY,2, startdate) > enddate
THEN enddate ELSE DATEADD(DAY,2, startdate) END) / 60.0, enddate
FROM cte
WHERE startdate <> CAST(enddate AS DATE)
)
SELECT * FROM cte
ORDER BY JobID, startdate

Date Difference Changes to 42000 while using LAG function in SQL Server

Question: Find out no follow-up appointments to the call within the following 7 days for a particular Patient
My query:
select *, DATEDIFF(DAY, (APPOINTMENT_DATE - LAG(APPOINTMENT_DATE)
over (ORDER BY PATIENT_ID)), APPOINTMENT_DATE) as DIFFERENCE from [dbo].
[Appointment Data]
Problems:
1.DIFFERENCE CHANGES to some crazy format because of datetime may be.
2.Is my query right? How do I find difference for each customer? I know I have to apply group by but I am little confused.
PLS HELP!
Dataset:
APPOINTMENT_DATE PATIENT_ID DIFFERENCE
2010-05-06 00:00:00.000 00051101 NULL
2010-04-11 00:00:00.000 00101005 40302
2010-05-06 00:00:00.000 00130521 40277
2010-02-07 00:00:00.000 00130521 40302
It seems that you have several mistakes in your query:
1) You should use column PATIENT_ID in partitioning and order by APPOINTMENT_DATE in LAG function
2) You have unnecessary subtraction in DATEDIFF function
So, your query should be something like:
select
*, datediff(dd, lag(APPOINTMENT_DATE) over (partition by PATIENT_ID order by APPOINTMENT_DATE), APPOINTMENT_DATE)
from
[dbo].[Appointment Data]
select *,
DATEDIFF(DAY, LAG(APPOINTMENT_DATE) over (ORDER BY PATIENT_ID), APPOINTMENT_DATE) as DIFFERENCE
from [dbo].[Appointment Data]
Result:
+-----------------------+------------+------------+
| APPOINTMENT_DATE | PATIENT_ID | DIFFERENCE |
+-----------------------+------------+------------+
| 5/6/2010 12:00:00 AM | 00051101 | null |
| 4/11/2010 12:00:00 AM | 00101005 | -25 |
| 5/6/2010 12:00:00 AM | 00130521 | 25 |
| 2/7/2010 12:00:00 AM | 00130521 | -88 |
+-----------------------+------------+------------+
If you switch the dates, the result will be different.
select *,
DATEDIFF(DAY, APPOINTMENT_DATE, LAG(APPOINTMENT_DATE) over (ORDER BY PATIENT_ID)) as DIFFERENCE
from [dbo].[Appointment Data]
Result:
+-----------------------+------------+------------+
| APPOINTMENT_DATE | PATIENT_ID | DIFFERENCE |
+-----------------------+------------+------------+
| 5/6/2010 12:00:00 AM | 00051101 | null |
| 4/11/2010 12:00:00 AM | 00101005 | 25 |
| 5/6/2010 12:00:00 AM | 00130521 | -25 |
| 2/7/2010 12:00:00 AM | 00130521 | 88 |
+-----------------------+------------+------------+

Show Date Range in Custom Column - Gaps and Islands

I have table that looks like this:
+------------+------+
| Date | Name |
+------------+------+
| 2017-01-07 | A |
| 2017-01-08 | A |
| 2017-01-09 | A |
| 2017-01-12 | A |
| 2017-01-07 | B |
| 2017-01-08 | B |
| 2017-01-09 | B |
+------------+------+
I would like to be able to turn it into the following:
+-------------------------+------+
| Date Range | Name |
+-------------------------+------+
| 2017-01-07 - 2017-01-09 | A |
| 2017-01-07 - 2017-01-09 | B |
| 2017-01-12 | A |
+-------------------------+------+
The code would find the minimum and maximum of consecutive dates only, group the results using the Name column and then list the minimum and maximum dates as a 'to and from' string in one column.
I'm having problems trying to list consecutive dates only. Note that the third entry above gets its own entry because it is not consecutive with the date range for 'A' in the earlier entry.
EDIT: Please note: This is specific to SQL Server 2008, which does not allow use of the LAG function.
EDIT 2:
The original answer supplied by McNets worked fine on SQL Server 2012. I've included it here as it's better if you have SQL Server 2012 onwards.
;WITH CalcDiffDays AS
(
SELECT Date, Name,
CONCAT (Name, CAST(DATEDIFF(DAY, LAG(Date, 1, Date - 1) OVER (PARTITION BY Name ORDER BY Name, Date), Date) AS VARCHAR(10))) AS NumDays
FROM #tmpTable
)
SELECT CONCAT(CONVERT(VARCHAR(20), MIN(Date), 102), ' - ', CONVERT(VARCHAR(20), MAX(Date), 102)) AS [Data Range], Name
FROM CalcDiffDays
GROUP BY NumDays, Name;
First I've added a row number to the whole table.
WITH RowN AS
(
SELECT Date, Name, ROW_NUMBER() OVER (ORDER BY Name, Date) RN
FROM #T
)
Then I've joined this table with itself just to calculate days between dates.
,CalcDiffDays AS
(
SELECT RowN.Date, RowN.Name,
ISLAND = RowN.Name +
CASE
WHEN RowN.RN > 1 AND RowN.Name = R2.Name THEN CAST(DATEDIFF(day, R2.Date, RowN.Date) AS VARCHAR(20))
ELSE '1'
END
FROM RowN
LEFT JOIN RowN R2 ON R2.RN = RowN.RN-1
)
GAPS. How many days between consecutive dates of the same name.
ISLANDS. By adding the name to the calculated days.
+---------------------+------+---------+
| Date | Name | NumDays |
+---------------------+------+---------+
| 07.01.2017 00:00:00 | A | A1 |
+---------------------+------+---------+
| 08.01.2017 00:00:00 | A | A1 |
+---------------------+------+---------+
| 09.01.2017 00:00:00 | A | A1 |
+---------------------+------+---------+
| 12.01.2017 00:00:00 | A | A3 |
+---------------------+------+---------+
| 07.01.2017 00:00:00 | B | B1 |
+---------------------+------+---------+
| 08.01.2017 00:00:00 | B | B1 |
+---------------------+------+---------+
| 09.01.2017 00:00:00 | B | B1 |
+---------------------+------+---------+
The second part: get the MIN and MAX Date of each island.
WITH RowN AS
(
SELECT Date, Name, ROW_NUMBER() OVER (ORDER BY Name, Date) RN
FROM #T
)
,CalcDiffDays AS
(
SELECT RowN.Date, RowN.Name,
ISLAND = RowN.Name +
CASE
WHEN RowN.RN > 1 AND RowN.Name = R2.Name THEN CAST(DATEDIFF(day, R2.Date, RowN.Date) AS VARCHAR(20))
ELSE '1'
END
FROM RowN
LEFT JOIN RowN R2 ON R2.RN = RowN.RN-1
)
SELECT CONVERT(VARCHAR(20), MIN(Date), 102) + ' - ' + CONVERT(VARCHAR(20), MAX(Date), 102) AS [Data Range], Name
FROM CalcDiffDays
GROUP BY ISLAND, Name
ORDER BY MIN(Date);
+-------------------------+------+
| Data Range | Name |
+-------------------------+------+
| 2017.01.07 - 2017.01.09 | A |
+-------------------------+------+
| 2017.01.07 - 2017.01.09 | B |
+-------------------------+------+
| 2017.01.12 - 2017.01.12 | A |
+-------------------------+------+
Can check it here: http://rextester.com/MHLEEJ50479

Need to calculate overtime and regular hours

I have 1 table as attendance
|AttendanceresultId | AccountID | Intime | Outtime | ShiftID |
| 1 | 1234 | 2016-06-21 06:56:00 | NULL | 1 |
| 2 | 1234 | NULL | 2016-06-21 17:02:00 | 1 |
| 3 | 1234 | 2016-06-22 06:56:00 | NULL | 1 |
| 4 | 1234 | NULL | 2016-06-22 17:02:00 | 1 |
| 5 | 1235 | 2016-06-21 22:55:00 | NULL | 3 |
| 6 | 1235 | NULL | 2016-06-22 06:00:00 | 3 |
| 7 | 1235 | 2016-06-22 22:55:00 | NULL | 3 |
| 8 | 1235 | NULL | 2016-06-23 07:00:00 | 3 |
Another table is shift table
| ShiftId | Starttime | Endtime |
| 1 | 07:00:00.00 | 16:00:00.00 |
| 3 | 23:00:00.00 | 06:00:00.00 |
I want to calculate the total number of working hours and over time hours for an employee lets say,
Expected Output,
| AccountID | NormalHours | OvertimeHours |
| 1234 | 18:08 Hrs | 02:04 Hrs |
| 1235 | 14:10 Hrs | 01:00 Hrs |
I am newbie in sql server can any one suggest anything
I broke this into four Parts to adjust for the lack of the lag function in 2008:
1) Common Table Expression to organize the records (so as not to count on the identity field)
2) Calculates the total minutes worked and worked for overtime without using the lag function
3) I then summarize those minutes - and split them into hours and minutes.
4) I then format them.
That said, there are quite a few possible places where I think real world data might create issues.
Updated version that does not use the lag function
WITH TimeRecords (AccountID, InOutTime, Intime, OutTime, Shift,
RowNumber) AS
(SELECT att.AccountID, COALESCE(att.Intime,att.OutTime),
att.Intime, att.OutTime, att.Shift,
ROW_NUMBER() OVER(ORDER BY att.AccountID,
COALESCE(att.InTime, att.OutTime) )
FROM dbo.attendance att)
,
ShiftTime AS (
SELECT trecout.AccountID ,
trecin.InTime,
trecout.OutTime,
-- sh.EndTime ,
-- Number of Minutes for the entire shift
CASE WHEN trecout.OutTime IS NOT NULL
THEN DATEDIFF(mi,
trecin.InTime,
trecout.OutTime)
ELSE 0
END AS TotalTime ,
-- Number of Minutes for overtime
CASE WHEN trecout.OutTime IS NOT NULL
THEN DATEDIFF(mi, sh.EndTime,
CAST(trecout.OutTime AS TIME))
ELSE 0
END AS OverTime
FROM TimeRecords trecout
JOIN TimeRecords trecin
ON trecout.RowNumber -1 = trecin.RowNumber
JOIN dbo.ShiftID sh
ON trecout.[Shift] = sh.ShiftID
)
,
-- Summarize the data
ShiftTimeSum
AS ( SELECT st.AccountID ,
FLOOR(SUM(st.TotalTime - st.OverTime) / 60) AS RegularHours ,
SUM(st.TotalTime - st.OverTime) % 60 AS RegularMinutes ,
SUM(st.OverTime) / 60 AS OverTimeHours ,
SUM(st.OverTime) % 60 AS OverTimeMinutes
FROM ShiftTime st
GROUP BY st.AccountID
)
-- Now format
SELECT sts.AccountID ,
CAST(sts.RegularHours AS VARCHAR(5)) + ':'
+ RIGHT(CAST(( 100 + sts.RegularMinutes ) AS CHAR(3)), 2) AS RegularTime ,
CAST(sts.OverTimeHours AS VARCHAR(5)) + ':'
+ RIGHT(CAST(( 100 + sts.OverTimeMinutes ) AS CHAR(3)), 2) AS OverTime
FROM ShiftTimeSum sts;

updates in month and day differences

I have a table as shown below:
Note: the MAX last_orderdate is 20131015 and the format is yyyymmdd.
I would like to show the final result looks like below:
Is there any query to help me in this as I have 200000 plus records.
Thank you very much for spending your time to look at my question.
For DATEDIFF() function
Try this:
UPDATE A
SET A.monthDiff = DATEDIFF(mm, CONVERT(DATE, A.orderDate, 112), B.lastOrderDate),
A.dayDiff = DATEDIFF(dd, CONVERT(DATE, A.orderDate, 112), B.lastOrderDate)
FROM tableA A, (SELECT MAX(CONVERT(DATE, orderDate, 112)) lastOrderDate FROM tableA) B
Check the SQL FIDDLE DEMO
OUTPUT
| ID | ORDERDATE | MONTHDIFF | DAYDIFF |
|----|-----------|-----------|---------|
| 1 | 20130105 | 9 | 283 |
| 2 | 20130205 | 8 | 252 |
| 3 | 20130305 | 7 | 224 |
| 4 | 20130909 | 1 | 36 |
| 5 | 20131001 | 0 | 14 |
| 6 | 20131015 | 0 | 0 |
try something like this:
declare #a date
set #a='20130105'
declare #b date
set #b='20131015'
select datediff(d,#a,#b) as date_diff,datediff(m,#a,#b) as month_diff
Try this.
select DATEDIFF(DAYOFYEAR,'20131015','20131125').
DAYOFYEAR represents count of days. Depeneds on your requirement, you can change to see month,day or year difference using DATEDIFF