SQLite: Sum of differences between two dates group by every date - sql

I have a SQLite database with start and stop datetimes
With the following SQL query I get the difference hours between start and stop:
SELECT starttime, stoptime, cast((strftime('%s',stoptime)-strftime('%s',starttime)) AS real)/60/60 AS diffHours FROM tracktime;
I need a SQL query, which delivers the sum of multiple timestamps, grouped by every day (also whole dates between timestamps).
The result should be something like this:
2018-08-01: 12 hours
2018-08-02: 24 hours
2018-08-03: 12 hours
2018-08-04: 0 hours
2018-08-05: 1 hours
2018-08-06: 14 hours
2018-08-07: 8 hours

You can try this, use CTE RECURSIVE make a calendar table for every date start time and end time, and do some calculation.
Schema (SQLite v3.18)
CREATE TABLE tracktime(
id int,
starttime timestamp,
stoptime timestamp
);
insert into tracktime values
(11,'2018-08-01 12:00:00','2018-08-03 12:00:00');
insert into tracktime values
(12,'2018-09-05 18:00:00','2018-09-05 19:00:00');
Query #1
WITH RECURSIVE cte AS (
select id,starttime,date(starttime,'+1 day') totime,stoptime
from tracktime
UNION ALL
SELECT id,
date(starttime,'+1 day'),
date(totime,'+1 day'),
stoptime
FROM cte
WHERE date(starttime,'+1 day') < stoptime
)
SELECT strftime('%Y-%m-%d', starttime),(strftime('%s',CASE
WHEN totime > stoptime THEN stoptime
ELSE totime
END) -strftime('%s',starttime))/3600 diffHour
FROM cte;
| strftime('%Y-%m-%d', starttime) | diffHour |
| ------------------------------- | -------- |
| 2018-08-01 | 12 |
| 2018-09-05 | 1 |
| 2018-08-02 | 24 |
| 2018-08-03 | 12 |
View on DB Fiddle

Related

grouping the table data(id,machine_id,telemetry_time,riskscore,current) by plant shift time and considering date as shift startime

I have a table with timestamp and someother metrics like riskscore and current of a machine.Here plant shift starts # 08:00 am and ends # next day 08:00am.
i want to group the data by day(shift: 08:00am to nextday 08:00am) of timetamp and label it as shift start date.i have a 6months of data.)
expected output:
machine | date | avg_riskscore | avg_current
2 | 2020-12-02 | 25.5 | 10
here this record is group of data between '2020-12-02 08:00:00' and '2020-12-03 08:00:00' and should insert with date '2020-12-02'
here i need to aggregate the 6 months of data like this.
DB Fiddle
You can just offset the timestamp by 8 hours, then truncate to date and aggregate. Based on your fiddle, that would be:
select
equipment_id,
(telemetry_time - interval '8 hour')::date as date,
avg(riskscore) as avg_riskscore,
avg(i_rms) as avg_i_rms
from telemetry_test
group by equipment_id, date

Get a rolling count of timestamps in SQL

I have a table (in an Oracle DB) that looks something like what is shown below with about 4000 records. This is just an example of how the table is designed. The timestamps range for several years.
| Time | Action |
| 9/25/2019 4:24:32 PM | Yes |
| 9/25/2019 4:28:56 PM | No |
| 9/28/2019 7:48:16 PM | Yes |
| .... | .... |
I want to be able to get a count of timestamps that occur on a rolling 15 minute interval. My main goal is to identify the maximum number of timestamps that appear for any 15 minute interval. I would like this done by looking at each timestamp and getting a count of timestamps that appear within 15 minutes of that timestamp.
My goal would to have something like
| Interval | Count |
| 9/25/2019 4:24:00 PM - 9/25/2019 4:39:00 | 2 |
| 9/25/2019 4:25:00 PM - 9/25/2019 4:40:00 | 2 |
| ..... | ..... |
| 9/25/2019 4:39:00 PM - 9/25/2019 4:54:00 | 0 |
I am not sure how I would be able to do this, if at all. Any ideas or advice would be much appreciated.
If you want any 15 minute interval in the data, then you can use:
select t.*,
count(*) over (order by timestamp
range between interval '15' minute preceding and current row
) as cnt_15
from t;
If you want the maximum, then use rank() on this:
select t.*
from (select t.*, rank() over (order by cnt_15 desc) as seqnum
from (select t.*,
count(*) over (order by timestamp
range between interval '15' minute preceding and current row
) as cnt_15
from t
) t
) t
where seqnum = 1;
This doesn't produce exactly the results you specify in the query. But it does answer the question:
I want to be able to get a count of timestamps that occur on a rolling 15 minute interval. My main goal is to identify the maximum number of timestamps that appear for any 15 minute interval.
You could enumerate the minutes with a recursive query, then bring the table with a left join:
with recursive cte (start_dt, max_dt) as (
select trunc(min(time), 'mi'), max(time) from mytable
union all
select start_dt + interval '1' minute, max_dt from cte where start_dt < max_dt
)
select
c.start_dt,
c.start_dt + interval '15' minute end_dt,
count(t.time) cnt
from cte c
left join mytable t
on t.time >= c.start_dt
and t.time < c.start_dt + interval '15' minute
group by c.start_dt

Postgresql Where Specific Time On Date

I've table structure like this:
---------------------------
No | Data | create_time
---------------------------
1 | Data1 | 2020-04-28 00:01:30
2 | Data2 | 2020-04-28 13:04:00
3 | Data3 | 2020-04-27 01:01:30
4 | Data4 | 2020-04-27 14:04:00
How to query But with condition: Date 27 April Until 28 April And Time 00:00 Until 12:00
What I've tried so far:
SELECT * FROM mytable WHERE ((date(create_time) >= '2020-04-27' AND date(2020-04-27) <= '2020-04-28'
AND TO_CHAR(create_time,'HH24:MI:SS') BETWEEN '00:00:00' AND '12:00:00'))
And
SELECT * FROM mytable WHERE ((date(create_time) >= '2020-04-27' AND date(2020-04-27) <= '2020-04-28'
AND TO_CHAR(create_time,'HH24:MI:SS') =>'00:00:00' AND TO_CHAR(create_time,'HH24:MI:SS') <= '12:00:00'))
What I want to achieve is, I want to get the data from that date but the time is only on 00:00:00 until 12:00:00 (24 Hours Format)
But it's still not working, The Date is correct but the time that I want is not.
Note: this answer assumes that create_time is correctly defined as timestamp.
You can combine conditions on the date and time part:
select *
from mytable
where create_time::date between date '2020-04-27'and date '2020-04-28'
and create_time::time between time '00:00' and time '12:00'
Alternatively you can use a range condition without casting the column:
select *
from mytable
where create_time >= date '2020-04-27'
and create_time < date '2020-04-29'
and create_time::time between time '00:00' and time '12:00'
That can use an index on create_time.
If you don't want to include times at precisely 12:00, you need to change the "time" condition as well:
and create_time::time >= time '00:00'
and create_time::time < time '12:00'
Online example
Try the following and here is the demo.
with cte as
(
select
*,
cast (create_time::timestamp as time) as hour
from times
)
select
no,
data
from cte
where date(create_time) >= '2020-04-27'
and date(create_time) <= '2020-04-28'
and hour between '00:00:00' and '12:00:00'
Output:
| No| Data |
*----------*
|1 | Data1 |
|3 | Data3 |

Reporting on time information using start and end time

Is it possible to create a report that sums hours for a day grouped by an Id using a start and end time stamp?
I need to be able to split time that spans days and take part of that time and sum to the correct date group.
NOTE: The date ids are to a date dimension table.
------------------------------------------------------------------------------
TaskId | StartDateId | EndDateId | StartTime | EndTime
------------------------------------------------------------------------------
2 | 20190317 | 20190318 | 2019-03-17 16:30:00 | 2019-03-18 09:00:00
------------------------------------------------------------------------------
1 | 20190318 | 20190318 | 2019-03-18 09:00:00 | 2019-03-18 16:30:00
------------------------------------------------------------------------------
2 | 20190318 | 20190319 | 2019-03-18 16:30:00 | 2019-03-19 09:00:00
------------------------------------------------------------------------------
So based on this, the desired report output would be:
-------------------------
Date | Task | Hours
-------------------------
2019-03-17 | 2 | 7.5
-------------------------
2019-03-18 | 1 | 7.5
-------------------------
2019-03-18 | 2 | 16.5
-------------------------
...
The only working solution I have managed to implement is splitting records so that no record spans multiple days. I was hoping to find a report query solution, rather than an ETL base based solution.
I have tried to simulate your problem here: https://rextester.com/DEV45608 and I hope it helps you :) (The CTE GetDates can be replaced by your date dimension)
DECLARE #minDate DATE
DECLARE #maxDate DATE
CREATE TABLE Tasktime
(
Task_id INT,
Start_time DATETIME,
End_time DATETIME
);
INSERT INTO Tasktime VALUES
(2,'2019-03-17 16:30:00','2019-03-18 09:00:00'),
(1,'2019-03-18 09:00:00','2019-03-18 16:30:00'),
(2,'2019-03-18 16:30:00','2019-03-19 09:00:00');
SELECT #mindate = MIN(Start_time) FROM Tasktime;
SELECT #maxdate = MAX(End_time) FROM Tasktime;
;WITH GetDates AS
(
SELECT 1 AS counter, #minDate as Date
UNION ALL
SELECT counter + 1, DATEADD(day,counter,#minDate)
from GetDates
WHERE DATEADD(day, counter, #minDate) <= #maxDate
)
SELECT counter, Date INTO #tmp FROM GetDates;
SELECT
g.Date,
t.Task_id,
SUM(
CASE WHEN CAST(t.Start_time AS DATE) = CAST(t.End_time AS DATE) THEN
DATEDIFF(second, t.Start_time, t.End_time) / 3600.0
WHEN CAST(t.Start_time AS DATE) = g.Date THEN
DATEDIFF(second, t.Start_time, CAST(DATEADD(day,1,g.Date) AS DATETIME)) / 3600.0
WHEN CAST(t.End_time AS DATE) = g.Date THEN
DATEDIFF(second, CAST(g.Date AS DATETIME), t.End_time) / 3600.0
ELSE
24.0
END) AS hours_on_the_day_for_the_task
from
#tmp g
INNER JOIN
Tasktime t
ON
g.Date BETWEEN CAST(t.Start_time AS DATE) AND CAST(t.End_time AS DATE)
GROUP BY g.Date, t.Task_id
The Desired Date can be joined to the date dimension and return the "calendar date" and you can show that date in the report.
As for the HOURS.. when you are retrieving your dataset in SQL, just do this.. it is as simple as:
cast(datediff(MINUTE,'2019-03-18 16:30:00','2019-03-19 09:00:00') /60.0 as decimal(13,1)) as 'Hours'
So in your case it would be
cast(datediff(MINUTE,sometable.startdate,sometable.enddate) /60.0 as decimal(13,1)) as 'Hours'
Just doing a HOUR will return the whole hour.. and dividing by 60 will return a whole number. Hence the /60.0 and the cast

Summing counts based on overlapping intervals in postgres

I want to sum the column for every two minute interval (so it would be the sum of 1,2 and 2,3 and 3,4, etc...), but I'm not exactly sure how to go about doing that.
My data looks something like:
minute | source | count
2018-01-01 10:00 | a | 7
2018-01-01 10:01 | a | 5
2018-01-01 10:02 | a | 10
2018-01-01 10:00 | b | 20
2018-01-01 10:05 | a | 12
What I want
(e.g. row1+row2, row2+3, row3, row4, row5)
minute | source | count
2018-01-01 10:00 | a | 12
2018-01-01 10:01 | a | 15
2018-01-01 10:02 | a | 10
2018-01-01 10:00 | b | 20
2018-01-01 10:05 | a | 12
You can use a correlated subquery selecting the sum of the counts for the records in the interval sharing the source (I guess that the source must match is an requirement. If not, just remove the comparison in the WHERE clause.).
SELECT "t1"."minute",
"t1"."source",
(SELECT sum("t2"."count")
FROM "elbat" "t2"
WHERE "t2"."source" = "t1"."source"
AND "t2"."minute" >= "t1"."minute"
AND "t2"."minute" <= "t1"."minute" + INTERVAL '1 MINUTE') "count"
FROM "elbat" "t1";
SQL Fiddle
the post above assumes all the timestamps are to the minute. if you want to check for every 2 minutes throughout the day you can use the generate_series function. the issue with including the beginning minute and ending time in each interval will be b having 2 rows in the results.
ie.
select begintime,
endtime,
source,
sum(count)
from mytable
inner join (
select begintime, endtime
from (
select lag(time, 1) over (order by time) as begintime,
time as endtime
from (
select *
from generate_series('2018-01-01 00:00:00', '2018-01-02 00:00:00', interval '2 minutes') time
) q
) q2
where begintime is not null
) times on minute between begintime and endtime
group by begintime, endtime, source
order by begintime, endtime, source
you can change the 'minute between begintime and endtime' to 'minute > begintime and minute <= endtime' if you don't want that overlap