conversion of time into minute count - sql

Ok so I need to convert a value from a table into an absolute minute count.
What I mean by that is this.
I currently have data like this
ID day time
1 1 00:04
2 1 01:08
3 2 00:08
4 2 02:04
I want it to convert to total count of minutes, not resettting back to zero for each day. so it would be
ID day time
1 1 04
2 1 68
3 2 1448
4 2 1564
Currently the time data is varchar(5) and not in date time. I have tried
DATEDIFF(MINUTE, DATEADD(DAY, DATEDIFF(DAY, 0,dispatchday), 0), dispatchday)
and that returned nothing but zero's

SQL Server I presume? This should take care of it. It doesn't make a difference what kind of field time is in, as long as it contains a valid time value.
select (day-1) * 1440 + datediff(minute, '00:00:00', time) as result from yourtable

Related

How do I calculate the difference between two timestamps?

How to calculate the difference in minutes between timestamps TIMEIN and TIMEOUT? My table (only 3 rows):
DATE
TIMEIN
TIMEOUT
2020-05-06
14:00
15:00
2020-05-06
14:45
15:55
2020-05-07
09:00
10:45
My SQL doesn't output what I want:
SELECT (T.DATE + T.TIMEIN - T.DATE + T.TIMEOUT) AS `Duration`
FROM Transport T;
Output:
Duration
29
29
19
It should be :
Duration
60
70
105
I tried this without using the date, however that lead to an output of -1 for all rows.
Assuming that TIMEOUT is always greater than TIMEIN you can subtract the unix epochs of the 2 values and divide by 60 to get the number of minutes:
SELECT (strftime('%s', TIMEOUT) - strftime('%s', TIMEIN)) / 60 AS Duration
FROM Transport;
See the demo.
date/time is not like integers so they didn't add/subtract like integers. So convert them in a timestamp before subtraction.

Extract 30 minutes from timestamp and group it by 30 mins time interval -PGSQL

In PostgreSQL I am extracting hour from the timestamp using below query.
select count(*) as logged_users, EXTRACT(hour from login_time::timestamp) as Hour
from loginhistory
where login_time::date = '2021-04-21'
group by Hour order by Hour;
And the output is as follows
logged_users | hour
--------------+------
27 | 7
82 | 8
229 | 9
1620 | 10
1264 | 11
1990 | 12
1027 | 13
1273 | 14
1794 | 15
1733 | 16
878 | 17
126 | 18
21 | 19
5 | 20
3 | 21
1 | 22
I want the same output for same SQL for 30 mins. Please suggest
SELECT to_timestamp((extract(epoch FROM login_time::timestamp)::bigint / 1800) * 1800)::timestamp AS interval_30_min
, count(*) AS logged_users
FROM loginhistory
WHERE login_time::date = '2021-04-21' -- inefficient!
GROUP BY 1
ORDER BY 1;
Extracting the epoch gets the number of seconds since the epoch. Integer division truncates. Multiplying back effectively rounds down, achieving the same as date_trunc() for arbitrary time intervals.
1800 because 30 minutes contain 1800 seconds.
Detailed explanation:
Truncate timestamp to arbitrary intervals
The cast to timestamp makes me wonder about the actual data type of login_time? If it's timestamptz, the cast depends on your current time zone setting and sets you up for surprises if that setting changes. See:
How do I match an entire day to a datetime field?
Subtract hours from the now() function
Ignoring time zones altogether in Rails and PostgreSQL
Depending on the actual data type, and exact definition of your date boundaries, there is a more efficient way to phrase your WHERE clause.
You can change the column on which you're aggregating to use the minute too:
select
count(*) as logged_users,
CONCAT(EXTRACT(hour from login_time::timestamp), '-', CASE WHEN EXTRACT(minute from login_time::timestamp) < 30 THEN 0 ELSE 30 END) as HalfHour
from loginhistory
where login_time::date = '2021-04-21'
group by HalfHour
order by HalfHour;

Calculate overlap time in seconds for groups in SQL

I have a bunch of timestamps grouped by ID and type in the sample data shown below.
I would like to find overlapped time between start_time and end_time columns in seconds for each group of ID and between each lead and follower combinations. I would like to show the overlap time only for the first record of each group which will always be the "lead" type.
For example, for the ID 1, the follower's start and end times in row 3 overlap with the lead's in row 1 for 193 seconds (from 09:00:00 to 09:03:13). the follower's times in row 3 also overlap with the lead's in row 2 for 133 seconds (09:01:00 to 2020-05-07 09:03:13). That's a total of 326 seconds (193+133)
I used the partition clause to rank rows by ID and type and order them by start_time as a start.
How do I get the overlap column?
row# ID type start_time end_time rank. overlap
1 1 lead 2020-05-07 09:00:00 2020-05-07 09:03:34 1 326
2 1 lead 2020-05-07 09:01:00 2020-05-07 09:03:13 2
3 1 follower 2020-05-07 08:59:00 2020-05-07 09:03:13 1
4 2 lead 2020-05-07 11:23:00 2020-05-07 11:33:00 1 540
4 2 follower 2020-05-07 11:27:00 2020-05-07 11:32:00 1
5 3 lead 2020-05-07 14:45:00 2020-05-07 15:00:00 1 305
6 3 follower 2020-05-07 14:44:00 2020-05-07 14:44:45 1
7 3 follower 2020-05-07 14:50:00 2020-05-07 14:55:05 2
In your example, the times completely cover the total duration. If this is always true, you can use the following logic:
select id,
(sum(datediff(second, start_time, end_time) -
datediff(second, min(start_time), max(end_time)
) as overlap
from t
group by id;
To add this as an additional column, then either use window functions or join in the result from the above query.
If the overall time has gaps, then the problem is quite a bit more complicated. I would suggest that you ask a new question and set up a db fiddle for the problem.
Tried this a couple of way and got it to work.
I first joined 2 tables with individual records for each type, 'lead' and 'follower' and created a case statement to calculate max start time for each lead and follower start time combination and min end time for each lead and follower end time combination. Stored this in a temp table.
CASE
WHEN lead_table.start_time > follower_table.start_time THEN lead_table.start_time
WHEN lead_table.start_time < follower_table.start_time THEN patient_table.start_time_local
ELSE 0
END as overlap_start_time,
CASE
WHEN follower_table.end_time < lead_table.end_time THEN follower_table.end_time
WHEN follower_table.end_time > lead_table.end_time THEN lead_table.end_time
ELSE 0
END as overlap_end_time
Then created an outer query to lookup the temp table just created to find the difference between start time and end time for each lead and follower combination in seconds
select temp_table.id,
temp_table.overlap_start_time,
temp_table.overlap_end_time,
DATEDIFF_BIG(second,
temp_table.overlap_start_time,
temp_table.overlap_end_time) as overlap_time FROM temp_table

Showing results of a SELECT statement including every 30-min range of the day, filling 0´s when no records exist

I have a table arrivals like this:
HHMM Car
---- ---
0001 01
0001 02
0001 03
0002 04
...
0029 20
0029 21
0030 22
...
0059 56
I need to know how many cars arrived at each range of 30 minutes.
I wrote a query like this:
WITH
PREVIOUS_QUERIES AS
(
-- in fact, "PREVIOUS_QUERIES" represent a sequence of queries where I get
-- the timestamp HHMM and convert it in a range where HOURS = HH and
-- MINUTES_START can be 0 (if MM<30) or 30 (if MM>=30).
),
INTERVALS AS
(
SELECT
TO_CHAR(HOURS,'FM00')||':'||TO_CHAR(MINUTES_START,'FM00')||' - '
||TO_CHAR(HOURS,'FM00')||':'||TO_CHAR(MINUTES_START +29,'FM00') AS INTERVAL,
CAR
FROM
PREVIOUS_QUERIES
)
SELECT
INTERVAL,
COUNT (DISTINCT (CAR)),
FROM INTERVALS
GROUP BY INTERVAL
ORDER BY INTERVAL
;
My query produces the following results.
Interval Cars
------------- ----
00:00 - 00:29 21
00:30 - 00:59 35
01:00 - 01:29 41
02:30 - 02:59 5
03:00 - 03:29 12
03:30 - 03:59 13
...
That means, if there are no arrivals in some interval, my query doesn´t show a line with Cars=0. I need these rows in my results:
01:30 - 01:59 0
02:00 - 02:29 0
How could I add these rows? Can it be done with a change on my query, or
should this query be completely rewritten?
What I imagine is that I should generate the 48 ranges of 30 minutes from '00:00-00:29' to '23:30-23:59' and then use them as a parameter for SELECT, but I don´t know how to do it.
You could just select those 48 values into another CTE, something like 'cteIntervals', and then adjust the final query to be something like:
SELECT
I.Interval
, NVL(Q.Cars, 0) Cars
FROM
cteIntervals I
LEFT JOIN
(
-- Your current query
) Q
ON I.Interval = Q.Interval
This has the effect of creating a template for the final query to fit into.
For a slightly more dynamic solution you could look into producing cteIntervals using a recursive CTE, or you could store the values in a table, etc.

GROUP BY several hours

I have a table where our product records its activity log. The product starts working at 23:00 every day and usually works one or two hours. This means that once a batch started at 23:00, it finishes about 1:00am next day.
Now, I need to take statistics on how many posts are registered per batch but cannot figure out a script that would allow me achiving this. So far I have following SQL code:
SELECT COUNT(*), DATEPART(DAY,registrationtime),DATEPART(HOUR,registrationtime)
FROM RegistrationMessageLogEntry
WHERE registrationtime > '2014-09-01 20:00'
GROUP BY DATEPART(DAY, registrationtime), DATEPART(HOUR,registrationtime)
ORDER BY DATEPART(DAY, registrationtime), DATEPART(HOUR,registrationtime)
which results in following
count day hour
....
1189 9 23
8611 10 0
2754 10 23
6462 11 0
1885 11 23
I.e. I want the number for 9th 23:00 grouped with the number for 10th 00:00, 10th 23:00 with 11th 00:00 and so on. How could I do it?
You can do it very easily. Use DATEADD to add an hour to the original registrationtime. If you do so, all the registrationtimes will be moved to the same day, and you can simply group by the day part.
You could also do it in a more complicated way using CASE WHEN, but it's overkill on the view of this easy solution.
I had to do something similar a few days ago. I had fixed timespans for work shifts to group by where one of them could start on one day at 10pm and end the next morning at 6am.
What I did was:
Define a "shift date", which was simply the day with zero timestamp when the shift started for every entry in the table. I was able to do so by checking whether the timestamp of the entry was between 0am and 6am. In that case I took only the date of this DATEADD(dd, -1, entryDate), which returned the previous day for all entries between 0am and 6am.
I also added an ID for the shift. 0 for the first one (6am to 2pm), 1 for the second one (2pm to 10pm) and 3 for the last one (10pm to 6am).
I was then able to group over the shift date and shift IDs.
Example:
Consider the following source entries:
Timestamp SomeData
=============================
2014-09-01 06:01:00 5
2014-09-01 14:01:00 6
2014-09-02 02:00:00 7
Step one extended the table as follows:
Timestamp SomeData ShiftDay
====================================================
2014-09-01 06:01:00 5 2014-09-01 00:00:00
2014-09-01 14:01:00 6 2014-09-01 00:00:00
2014-09-02 02:00:00 7 2014-09-01 00:00:00
Step two extended the table as follows:
Timestamp SomeData ShiftDay ShiftID
==============================================================
2014-09-01 06:01:00 5 2014-09-01 00:00:00 0
2014-09-01 14:01:00 6 2014-09-01 00:00:00 1
2014-09-02 02:00:00 7 2014-09-01 00:00:00 2
If you add one hour to registrationtime, you will be able to group by the date part:
GROUP BY
CAST(DATEADD(HOUR, 1, registrationtime) AS date)
If the starting hour must be reflected accurately in the output (as 9, 23, 10, 23 rather than as 10, 0, 11, 0), you could obtain it as MIN(registrationtime) in the SELECT clause:
SELECT
count = COUNT(*),
day = DATEPART(DAY, MIN(registrationtime)),
hour = DATEPART(HOUR, MIN(registrationtime))
Finally, in case you are not aware, you can reference columns by their aliases in ORDER BY:
ORDER BY
day,
hour
just so that you do not have to repeat the expressions.
The below query will give you what you are expecting..
;WITH CTE AS
(
SELECT COUNT(*) Count, DATEPART(DAY,registrationtime) Day,DATEPART(HOUR,registrationtime) Hour,
RANK() over (partition by DATEPART(HOUR,registrationtime) order by DATEPART(DAY,registrationtime),DATEPART(HOUR,registrationtime)) Batch_ID
FROM RegistrationMessageLogEntry
WHERE registrationtime > '2014-09-01 20:00'
GROUP BY DATEPART(DAY, registrationtime), DATEPART(HOUR,registrationtime)
)
SELECT SUM(COUNT) Count,Batch_ID
FROM CTE
GROUP BY Batch_ID
ORDER BY Batch_ID
You can write a CASE statement as below
CASE WHEN DATEPART(HOUR,registrationtime) = 23
THEN DATEPART(DAY,registrationtime)+1
END,
CASE WHEN DATEPART(HOUR,registrationtime) = 23
THEN 0
END