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

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 |
+-----------------------+------------+------------+

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

How to find time average of two datetime rows in 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]

SQL: Sum timestamp intervals of the same day

I'm setting up a new SQL query to summarize records from a table of employee attendance. These records are downloaded from a fingerprint or RFID sensor and recorded on the same table. I want to get the amount of hours on the workplace.
Everything works fine if the employee comes in and leaves the workplace once at day. The device generates 2 records on the table and that's are not a problem. It's easy identify entrance and exit time.
But I don't realize how to solve if the person comes in, take a break (leaves workplace) and after that he comes in again until the exit time.
Assume that they are always even records (arrival and exit timestamps) on each interval. Also, an employee never checks in one day and leaves on the next day.
I have the following query. Remember: this only gets the minimum timestamp (arrival time) and the max timestamp (leave time).
SELECT Userid, Name, Date, Entrance, Exit, Hours FROM
(SELECT Userid AS user,
CONVERT(VARCHAR, CONVERT(TIME, min(Checktime))) AS Entrance,
CONVERT(VARCHAR, CONVERT(TIME, max(Checktime))) AS Exit,
CONVERT(VARCHAR, CONVERT(TIME, max(Checktime)-min(CheckTime))) AS Hours,
CONVERT(VARCHAR, CONVERT(DATE, CheckTime)) AS Fecha,
COUNT(*) AS Regs,
SUM(edited) AS edited FROM attendance
WHERE CONVERT(DATE, CheckTime) < CONVERT(DATE, GETDATE())
GROUP BY Userid, CONVERT(DATE, CheckTime)) AS Hs
INNER JOIN Userinfo
ON Userinfo.Userid = Hs.user
ORDER BY Date DESC, Name ASC;
For example, if the table has the following records:
id | Logid | Userid | CheckTime | edited
1 | 10 | 1 | 2019-06-18 8:00:00 | 0
2 | 11 | 1 | 2019-06-18 12:00:00 | 0
3 | 12 | 1 | 2019-06-18 15:00:00 | 0
4 | 13 | 1 | 2019-06-18 17:00:00 | 0
5 | 14 | 2 | 2019-06-18 8:00:00 | 0
6 | 15 | 2 | 2019-06-18 17:00:00 | 0
What I get:
Userid | Name | Date | Entrance | Exit | Hours | edited
1 | Gandalf | 2019-06-18 | 8:00:00 | 17:00:00 | 9:00:00 | 0
2 | Frodo | 2019-06-18 | 8:00:00 | 17:00:00 | 9:00:00 | 0
What I need:
Userid | Name | Date | Entrance | Exit | Hours | edited
1 | Gandalf | 2019-06-18 | 8:00:00 | 17:00:00 | 6:00:00 | 0
2 | Frodo | 2019-06-18 | 8:00:00 | 17:00:00 | 9:00:00 | 0
The total time was calculated from (12:00:00 - 8:00:00) + (17:00:00 - 15:00:00).
The columns "Entrance" and "Exit" on this case are not necessary at all.
Have you an idea how I can solve this? Thank you very much!
This assume you have pair enter/exit and handle multiple breaks.
SQL DEMO
with cte as (
SELECT *, ROW_NUMBER() OVER (PARTITION BY [Userid], cast ([CheckTime] as Date)
ORDER BY [CheckTime]) as rn
FROM Table1 t1
)
SELECT c1.[Userid],
cast (c1.[CheckTime] as Date) as the_day,
SUM (DATEDIFF (hh, c1.[CheckTime], c2.[CheckTime])) as total_hours
FROM cte c1
JOIN cte c2
ON c1.rn = c2.rn -1
AND c1.[Userid] = c2.[Userid]
AND c1.rn % 2 = 1
GROUP BY c1.[Userid],
cast (c1.[CheckTime] as Date) ;
OUTPUT
| Userid | the_day | total_hours |
|--------|------------|-------------|
| 1 | 2019-06-18 | 6 |
| 2 | 2019-06-18 | 9 |
NOTE:
General syntax for DATEDIFF:
DATEDIFF(datepart, start_date, end_date)
Just realize the function DATEDIFF is used to calculate the time interval between two date values and return it as an integer.
So if you have 08:00 and 09:30 using hh as datepart you still get 1h. Maybe is better use mi and divide by 60
Perfect! Juan Carlos's solution works great!
I'm posting this because I've edited some of his code to match the original post requirements.
The code is exactly the same. Only I've changed/added a few lines
with cte as (
SELECT *, ROW_NUMBER() OVER (PARTITION BY [Userid], cast ([CheckTime] as Date)
ORDER BY [CheckTime]) as rn
FROM Table1 t1
WHERE CAST(CheckTime AS DATE) = '2019-06-17' -- Filter by specific date
)
SELECT c1.[Userid],
cast (c1.[CheckTime] as Date) as the_day,
-- Return time as HH:MM
CONVERT(VARCHAR, SUM (DATEDIFF (SECOND , c1.[CheckTime], c2.[CheckTime]))/3600) + ':' + right('00' + CONVERT(VARCHAR, CONVERT(FLOAT, (SUM (DATEDIFF (SECOND , c1.[CheckTime], c2.[CheckTime]))/60) - ((SUM (DATEDIFF (SECOND , c1.[CheckTime], c2.[CheckTime]))/3600)*60))),2) as total_time
FROM cte c1
JOIN cte c2
ON c1.rn = c2.rn -1
AND c1.[Userid] = c2.[Userid]
AND c1.rn % 2 = 1
GROUP BY c1.[Userid],
cast (c1.[CheckTime] as Date);
This query returns:
| Userid | the_day | total_time |
|--------|------------|-------------|
| 1 | 2019-06-18 | 6:00 |
| 2 | 2019-06-18 | 9:00 |

Balance for each month by type

I have a table, in SQL-Server, with several records of input and output values with columns for type and date.
Something like that:
DATE |INPUT |OUTPUT |TYPE
2018-01-10 | 256.35| |A
2018-02-05 | | 35.00|B
2018-02-15 | 65.30| |A
2018-03-20 | 158.00| |B
2018-04-02 | | 63.32|B
2018-05-12 | | 128.12|A
2018-06-20 | | 7.35|B
I need help to make a query to returns the sum of inputs and outputs (as balance), per type, but it should return that sum at the end of each month, that is:
YEAR|MONTH|TYPE|BALANCE
2018| 1|A | 256.35
2018| 1|B | 0.00
2018| 2|A | 321.65
2018| 2|B | -35.00
2018| 3|A | 321.65
2018| 3|B | 123.00
2018| 4|A | 321.65
2018| 4|B | 59.68
2018| 5|A | 193.53
2018| 5|B | 59.68
2018| 6|A | 193.53
2018| 6|B | 52.33
2018| 7|A | 193.53
2018| 7|B | 52.33
Don't forget that the balance of each month is affected by the balance of the previous month, or in other words, the balance of each month is not only the movements of that month but of all the previous months also.
It should also be noted that it should include a record for each month of the year/type (up to the current date), even if a given month/type don't have movements, starting at the first month/year of the oldest movement and ending at actual date (in this case 2018 July).
Result achieved, there you go:
declare #min_month datetime=(select dateadd(month,datediff(month,0,min([DATE])),0) from _yourtable)
declare #max_month datetime=(select dateadd(month,datediff(month,0,max([DATE])),0) from _yourtable)
;WITH months(d) AS (
select #min_month
UNION ALL
SELECT dateadd(month,1,d) -- Recursion
FROM months
where dateadd(month,1,d)<=getdate()
)
select distinct
year(m.d) as YEAR,
month(m.d) as MONTH,
types.v as [TYPE]
,sum(isnull(t.[INPUT],0)-isnull(t.[OUTPUT],0)) over (partition by types.v order by m.d)
from months m
cross join (select distinct type from _yourtable)types(v)
left join _yourtable t on dateadd(month,datediff(month,0,t.[DATE]),0)=m.d and types.v=t.TYPE
order by m.d,type
option(maxrecursion 0)
You can use Lag function, below code might help:
select year(date), month(date), type
, sum(input-output) + isnull(lag(sum(input-output),1,0) over(order by year(date), month(date), type), 0)
from test group by year(date), month(date), type
Assuming your source data is in the structure you have initially provided (ie: this is not the result of another query), this is a fairly straightforward transformation using a table of dates and a running total via an ordered sum.
If you already have a dates table, you can remove the first 2 ctes in this script:
declare #t table(DateValue date,InputAmount decimal(8,2),OutputAmount decimal(8,2),ProdType nvarchar(1));
insert into #t values
('2018-01-10',256.35,null,'A')
,('2018-02-05',null, 35.00,'B')
,('2018-02-15', 65.30,null,'A')
,('2018-03-20',158.00,null,'B')
,('2018-04-02',null, 63.32,'B')
,('2018-05-12',null,128.12,'A')
,('2018-06-20',null, 7.35,'B')
;
-- Min date can just be min date in the source table, but the max date should be the month end of the max date in the source table0
declare #MinDate date = (select min(DateValue) from #t);
declare #MaxDate date = (select max(dateadd(day,-1,dateadd(month,datediff(month,0,DateValue)+1,0))) from #t);
with n(n) as (select * from (values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) t(t)) -- Using a tally table, built a table of dates
,d(d) as (select top(select datediff(day,#MinDate,#MaxDate)+1) dateadd(day,row_number() over (order by (select null))-1,#MinDate) from n n1,n n2,n n3, n n4)
,m as (select p.ProdType -- Then join to the source data to create a date value for each posible day for each product type
,d.d
,dateadd(day,-1,dateadd(month,datediff(month,0,d)+1,0)) as m -- And calculate a running total using a windowed aggregate
,sum(isnull(t.InputAmount,0) - isnull(t.OutputAmount,0)) over (partition by p.ProdType order by d.d) as RunningTotal
from d
cross join (select distinct ProdType
from #t
) as p
left join #t as t
on d.d = t.DateValue
and p.ProdType = t.ProdType
)
select m
,ProdType
,RunningTotal as Balance
from m
where m = d
order by m.d
,m.ProdType;
Output:
+-------------------------+----------+---------+
| m | ProdType | Balance |
+-------------------------+----------+---------+
| 2018-01-31 00:00:00.000 | A | 256.35 |
| 2018-01-31 00:00:00.000 | B | 0.00 |
| 2018-02-28 00:00:00.000 | A | 321.65 |
| 2018-02-28 00:00:00.000 | B | -35.00 |
| 2018-03-31 00:00:00.000 | A | 321.65 |
| 2018-03-31 00:00:00.000 | B | 123.00 |
| 2018-04-30 00:00:00.000 | A | 321.65 |
| 2018-04-30 00:00:00.000 | B | 59.68 |
| 2018-05-31 00:00:00.000 | A | 193.53 |
| 2018-05-31 00:00:00.000 | B | 59.68 |
| 2018-06-30 00:00:00.000 | A | 193.53 |
| 2018-06-30 00:00:00.000 | B | 52.33 |
+-------------------------+----------+---------+

Group By with Min and Max Date using T-SQL

I am trying to do some SQL on the data from the SF Bay Area Bike Share Kaggel Data
When I run this SQL:
/****** Select Bikes ******/
SELECT count(t.[id]) as TripCount
,t.[bike_id]
FROM [dbo].[trip] t
Group By t.[bike_id]
having count(t.[id]) < 25
Order By TripCount asc
I get this result:
+-----------+---------+
| TripCount | bike_id |
+-----------+---------+
| 6 | 876 |
| 18 | 323 |
| 20 | 565 |
| 24 | 476 |
| 24 | 697 |
+-----------+---------+
Where what I really want is this:
+-----------+---------+------------+------------+
| TripCount | bike_id | Min_date | Max_date |
+-----------+---------+------------+------------+
| 6 | 876 | 2014-05-22 | 2014-05-27 |
| 18 | 323 | 2013-08-30 | 2013-09-07 |
| 20 | 565 | 2013-08-29 | 2013-09-07 |
| 24 | 476 | 2013-08-29 | 2013-09-07 |
| 24 | 697 | 2013-10-15 | 2013-12-20 |
+-----------+---------+------------+------------+
where the Min Date & Max Date give respectively the first & last date the bike made a trip.
I can do this manually for each bike by running this:
SELECT
min(cast([start_date] as date)) as Min_Date
,max(cast([start_date] as date)) as Max_Date
,[bike_id]
FROM [dbo].[trip]
where bike_id = '697'
Group By bike_id
I tried this query:
/****** Script for SelectTopNRows command from SSMS ******/
SELECT
count (t.[id]) as TripCount
,min(cast(t.[start_date] as date)) as Min_Date
,max(cast(t.[start_date] as date)) as Max_Date
,[bike_id]
FROM [dbo].[trip] t
Group By count (t.[id]), bike_id
and obviously got the following error:
Cannot use an aggregate or a subquery in an expression used for the group by list of a GROUP BY clause.
Just not quite sure how to wrap my head around this...
You were close
SELECT
[bike_id]
,count (t.[id]) as TripCount
,min(cast(t.[start_date] as date)) as Min_Date
,max(cast(t.[start_date] as date)) as Max_Date
FROM [dbo].[trip] t
Group By bike_id