Get time between two dates - sql

I am trying to get the time between two dates in two different rows.
dataset:
user
id
time
status
user3
4
2021-02-01 14:00:00
OUT
user1
2
2021-02-01 12:00:00
OUT
user2
1
2021-02-01 10:00:00
OUT
user1
2
2021-02-01 09:00:00
IN
user2
1
2021-02-01 08:00:00
IN
user3
4
2021-02-01 08:30:00
IN
What I am trying to obtain :
user
id
time
user3
4
07:30:00
user1
2
03:00:00
user2
1
02:00:00
The rows can be all mixed up since I dont know whenever a user will open (IN) or close (OUT) the job.
So far i tried to make a first query :
SELECT *
FROM table
WHERE DATE(date) BETWEEN '2021-02-01' AND '2021-02-02'
ORDER BY date DESC;
And then I was thinking looping on the result array, and make the math from there, but I was wondering if there is anyway to do this in SQL only.

You can use comditional aggregation as follows:
SELECT user, id,
Timediff( Max(case when status='OUT' then time end),
Min(case when status='IN' then time end) ) as diff
FROM table
WHERE DATE(date) BETWEEN '2021-02-01' AND '2021-02-02'
Group by user, id;

You can sort the result as out first then get the previous time from next row using Lead().
Though I am no expert in mariaDB but below query should work:
with userresult as(
SELECT *,lead(TIME,1)OVER(PARTITION BY id ORDER BY STATUS DESC) previoustime,ROW_NUMBER()over(partition by id order by status desc) rownum FROM USERS
where DATE(date) BETWEEN '2021-02-01' AND '2021-02-02'
)
select user,id,timediff(time,previoustime)time from userresult where rownum=1
If your mariaDB version is lower than 10.2 you can try simple sql like below:
select username,id,timediff(time,(select max(time) from users u where status='in'and u.id=us.id ))
from users us where status='out'

Try this simple solution:
SELECT T1.id, T1.time AS INTime, T1_1.time AS OUTTime
FROM T1
INNER JOIN T1 AS T1_1 ON (T1.id = T1_1.id AND T1_1.Status="OUT")
WHERE T1.Status="IN";

Related

INNER JOIN SQL with DateTime return multiple record

I have the following table:
Group RecDate oData
---------------------------------------
123 2022-03-20 02:00:00 F1xR
123 2022-03-21 02:30:00 F1xF
123 2022-03-22 05:00:00 F1xN
123 2022-03-15 04:00:00 F2xR
From the table above, I want to get the MAX date group by 2 char from oData field. Then I wrote a query like this:
SELECT a.Group, MAX(a.RecDate) RecDate, LEFT(a.oData, 2) oDataNo
INTO #t1
FROM TableData a
GROUP BY a.Group, LEFT(a.oData, 2)
SELECT * FROM #t1
Then, the result should be:
Group RecDate oDataNo
--------------------------------------------
123 2022-03-22 05:00:00 F1
123 2022-03-15 04:00:00 F2
From the result above (#t1), I want to join with the TableData to get the RIGHT character (1 digit) from oData field. So I INNER JOIN the #t1 with TableData. The JOIN field is RecDate. But it is strange that the result isn't what I want.
The query like:
SELECT RIGHT(a.oData,1) oDataStat, b.*
FROM TableData a
INNER JOIN #t1 b ON a.RecDate = b.RecDate
The wrong result like:
The result should be:
Group RecDate oDataNo oDataStat
-----------------------------------------------------------
123 2022-03-22 05:00:00 F1 N
123 2022-03-15 04:00:00 F2 R
Am I doing wrong approach?
Please advise. Really appreciated.
Thank you.
The query you provided returns the data you desire. However its cleaner to do it in a single query e.g.
WITH cte AS (
SELECT *
, RIGHT(a.oData,1) oDataStat
, ROW_NUMBER() OVER (PARTITION BY LEFT(a.oData, 2) ORDER BY RecDate DESC) rn
FROM TableData a
)
SELECT [Group], RecDate, oData, oDataStat
FROM cte
WHERE rn = 1
ORDER BY RecDate;
returns:
Group
RecDate
oData
oDataStat
123
2022-03-15 04:00:00
F2xR
R
123
2022-03-22 05:00:00
F1xN
N
Note: Your query as posted doesn't actually run due to not escaping [Group] - you should ensure everything you post has any errors removed first.

Counting subscriber numbers given events on SQL

I have a dataset on mysql in the following format, showing the history of events given some client IDs:
Base Data
Text of the dataset (subscriber_table):
user_id type created_at
A past_due 2021-03-27 10:15:56
A reactivate 2021-02-06 10:21:35
A past_due 2021-01-27 10:30:41
A new 2020-10-28 18:53:07
A cancel 2020-07-22 9:48:54
A reactivate 2020-07-22 9:48:53
A cancel 2020-07-15 2:53:05
A new 2020-06-20 20:24:18
B reactivate 2020-06-14 10:57:50
B past_due 2020-06-14 10:33:21
B new 2020-06-11 10:21:24
date_table:
full_date
2020-05-01
2020-06-01
2020-07-01
2020-08-01
2020-09-01
2020-10-01
2020-11-01
2020-12-01
2021-01-01
2021-02-01
2021-03-01
I have been struggling to come up with a query to count subscriber counts given a range of months, which are not necessary included in the event table either because the client is still subscribed or they cancelled and later resubscribed. The output I am looking for is this:
Output
date subscriber_count
2020-05-01 0
2020-06-01 2
2020-07-01 2
2020-08-01 1
2020-09-01 1
2020-10-01 2
2020-11-01 2
2020-12-01 2
2021-01-01 2
2021-02-01 2
2021-03-01 2
Reactivation and Past Due events do not change the subscription status of the client, however only the Cancel and New event do. If the client cancels in a month, they should still be counted as active for that month.
My initial approach was to get the latest entry given a month per subscriber ID and then join them to the premade date table, but when I have months missing I am unsure on how to fill them with the correct status. Maybe a lag function?
with last_record_per_month as (
select
date_trunc('month', created_at)::date order by created_at) as month_year ,
user_id ,
type,
created_at as created_at
from
subscriber_table
where
user_id in ('A', 'B')
order by
created_at desc
), final as (
select
month_year,
created_at,
type
from
last_record_per_month lrpm
right join (
select
date_trunc('month', full_date)::date as month_year
from
date_table
where
full_date between '2020-05-01' and '2021-03-31'
group by
1
order by
1
) dd
on lrpm.created_at = dd.month_year
and num = 1
order by
month_year
)
select
*
from
final
I do have a premade base table with every single date in many years to use as a joining table
Any help with this is GREATLY appreciated
Thanks!
The approach here is to have the subscriber rows with new connections as base and map them to the cancelled rows using a self join. Then have the date tables as base and aggregate them based on the number of users to get the result.
SELECT full_date, COUNT(DISTINCT user_id) FROM date_tbl
LEFT JOIN(
SELECT new.user_id,new.type,new.created_at created_at_new,
IFNULL(cancel.created_at,CURRENT_DATE) created_at_cancel
FROM subscriber new
LEFT JOIN subscriber cancel
ON new.user_id=cancel.user_id
AND new.type='new' AND cancel.type='cancel'
AND new.created_at<= cancel.created_at
WHERE new.type IN('new'))s
ON DATE_FORMAT(s.created_at_new, '%Y-%m')<=DATE_FORMAT(full_date, '%Y-%m')
AND DATE_FORMAT(s.created_at_cancel, '%Y-%m')>=DATE_FORMAT(full_date, '%Y-%m')
GROUP BY 1
Let me breakdown some sections
First up we need to have the subscriber table self joined based on user_id and then left table with rows as 'new' and the right one with 'cancel' new.type='new' AND cancel.type='cancel'
The new ones should always precede the canceled rows so adding this new.created_at<= cancel.created_at
Since we only care about the rows with new in the base table we filter out the rows in the WHERE clause new.type IN('new'). The result of the subquery would look something like this
We can then join this subquery with a Left join the date table such that the year and month of the created_at_new column is always less than equal to the full_date DATE_FORMAT(s.created_at_new, '%Y-%m')<=DATE_FORMAT(full_date, '%Y-%m') but greater than that of the canceled date.
Lastly we aggregate based on the full_date and consider the unique count of users
fiddle

subquery calculate days between dates

Sub query, SQL, Oracle
I'm new to sub queries and hoping to get some assistance. My thought was the sub query would run first and then the outer query would execute based on the sub query filter of trans_code = 'ABC'. The query works but it pulls all dates from all transaction codes, trans_code 'ABC' and 'DEF' ect.
The end goal is to calculate the number of days between dates.
The table structure is:
acct_num effective_date
1234 01/01/2020
1234 02/01/2020
1234 03/01/2020
1234 04/01/2021
I want to execute a query to look like this:
account Effective_Date Effective_Date_2 Days_Diff
1234 01/01/2020 02/01/2020 31
1234 02/01/2020 03/01/2020 29
1234 03/01/2020 04/01/2021 395
1234 04/01/2021 0
Query:
SELECT t3.acct_num,
t3.trans_code,
t3.effective_date,
MIN (t2.effective_date) AS effective_date2,
MIN (t2.effective_date) - t3.effective_date AS days_diff
FROM (SELECT t1.acct_num, t1.trans_code, t1.effective_date
FROM lawd.trans t1
WHERE t1.trans_code = 'ABC') t3
LEFT JOIN lawd.trans t2 ON t3.acct_num = t2.acct_num
WHERE t3.acct_num = '1234' AND t2.effective_date > t3.effective_date
GROUP BY t3.acct_num, t3.effective_date, t3.trans_code
ORDER BY t3.effective_date asc
TIA!
Use lead():
select t.*,
lead(effective_date) over (partition by acct_num order by effect_date) as next_efffective_date,
(lead(effective_date) - effective_date) as diff
from lawd.trans t

I need to calculate the time between dates in different lines. (PLSQL)

I have a table where I store all status changes and the time that it has been made. So, when I search the order number on the table of times I get all the dates of my changes, but what I realy want is the time (hours/minutes) that the order was in each status.
The table of time seems like this
ID_ORDER | Status | Date
1 Waiting 27/09/2017 12:00:00
1 Late 27/09/2017 14:00:00
1 In progress 28/09/2017 08:00:00
1 Validating 30/09/2017 14:00:00
1 Completed 30/09/2017 14:00:00
Thanks!
Use lead():
select t.*,
(lead(date) over (partition by id_order order by date) - date) as time_in_order
from t;

Get MAX count but keep the repeated calculated value if highest

I have the following table, I am using SQL Server 2008
BayNo FixDateTime FixType
1 04/05/2015 16:15:00 tyre change
1 12/05/2015 00:15:00 oil change
1 12/05/2015 08:15:00 engine tuning
1 04/05/2016 08:11:00 car tuning
2 13/05/2015 19:30:00 puncture
2 14/05/2015 08:00:00 light repair
2 15/05/2015 10:30:00 super op
2 20/05/2015 12:30:00 wiper change
2 12/05/2016 09:30:00 denting
2 12/05/2016 10:30:00 wiper repair
2 12/06/2016 10:30:00 exhaust repair
4 12/05/2016 05:30:00 stereo unlock
4 17/05/2016 15:05:00 door handle repair
on any given day need do find the highest number of fixes made on a given bay number, and if that calculated number is repeated then it should also appear in the resultset
so would like to see the result set as follows
BayNo FixDateTime noOfFixes
1 12/05/2015 00:15:00 2
2 12/05/2016 09:30:00 2
4 12/05/2016 05:30:00 1
4 17/05/2016 15:05:00 1
I manage to get the counts of each but struggling to get the max and keep the highest calculated repeated value. can someone help please
Use window functions.
Get the count for each day by bayno and also find the min fixdatetime for each day per bayno.
Then use dense_rank to compute the highest ranked row for each bayno based on the number of fixes.
Finally get the highest ranked rows.
select distinct bayno,minfixdatetime,no_of_fixes
from (
select bayno,minfixdatetime,no_of_fixes
,dense_rank() over(partition by bayno order by no_of_fixes desc) rnk
from (
select t.*,
count(*) over(partition by bayno,cast(fixdatetime as date)) no_of_fixes,
min(fixdatetime) over(partition by bayno,cast(fixdatetime as date)) minfixdatetime
from tablename t
) x
) y
where rnk = 1
Sample Demo
You are looking for rank() or dense_rank(). I would right the query like this:
select bayno, thedate, numFixes
from (select bayno, cast(fixdatetime) as date) as thedate,
count(*) as numFixes,
rank() over (partition by cast(fixdatetime as date) order by count(*) desc) as seqnum
from t
group by bayno, cast(fixdatetime as date)
) b
where seqnum = 1;
Note that this returns the date in question. The date does not have a time component.