How to Sum week hours in MYSQL Table? - mysql

I have these rows in my Database Table.
ID Name Date working_hours
29 A 2020-04-07 14.45
29 A 2020-04-08 10.30
29 A 2020-04-06 06.30
29 A 2020-04-10 10.15
30 B 2020-04-09 09.15
30 B 2020-04-07 09.00
30 B 2020-04-08 09.00
30 B 2020-04-10 04.45
30 B 2020-04-06 07.45
30 B 2020-04-10 04.45
When I execute below Query
SELECT ID,Name, date, working_hours,sum(working_hours) as 'total_week_hours'
from tblstafftasks
WHERE date between
date_add('2020-04-10', interval -WEEKDAY('2020-04-10') day) and
date_add(date_add('2020-04-10', interval -WEEKDAY('2020-04-10')-1 day), interval 6 day)
group by ID
I Get below OUTPUT
ID Name Date working_hours total_week_hours
29 A 2020-04-10 10.15 41.2
30 B 2020-04-10 04.45 43.5
But I want output to look like below
ID Name Date working_hours total_week_hours
29 A 2020-04-07 14.45 41.2
29 A 2020-04-08 10.30 41.2
29 A 2020-04-06 06.30 41.2
29 A 2020-04-10 10.15 41.2
30 B 2020-04-09 09.15 43.5
30 B 2020-04-07 09.00 43.5
30 B 2020-04-08 09.00 43.5
30 B 2020-04-10 04.45 43.5
30 B 2020-04-06 07.45 43.5
30 B 2020-04-10 04.45 43.5
Any help will be highly appreciated.

Join the original table with a subquery that gets the weekly totals.
SELECT t1.ID, t1.Name, t1.date, t1.working_hours, t2.total_week_hours
FROM tblstafftasks AS t1
JOIN (
SELECT ID, SUM(working_hours) AS total_week_hours
WHERE date between
date_add('2020-04-10', interval -WEEKDAY('2020-04-10') day) and
date_add(date_add('2020-04-10', interval -WEEKDAY('2020-04-10')-1 day), interval 6 day
GROUP BY ID)
) AS t2 ON t1.ID = t2.ID
WHERE t1.date between
date_add('2020-04-10', interval -WEEKDAY('2020-04-10') day) and
date_add(date_add('2020-04-10', interval -WEEKDAY('2020-04-10')-1 day), interval 6 day)

Related

SQL Arrange blocks of rows in Order By

Say I have an original output which looks like this (sorted purely by StartTime):
Name
OrderNo
OpNo
StartTime
Length
A
17345
op10
27/4/22 08:00
04:00:00
B
12727
op 20
27/4/22 11:00
02:00:00
A
18001
op10
27/4/22 13:20
01:00:00
B
17345
op 20
28/4/22 10:17
06:00:00
B
18001
op 20
29/4/22 13:00
04:00:00
C
17345
op 30
3/5/22 16:20
08:00:00
C
18001
op 30
5/5/22 13:00
09:00:00
How would it be possible to order the rows by OrderNo, then by OpNo, and then by the StartTime of the lowest OpNo in each OrderNo? So it would look like this:
Name
OrderNo
OpNo
StartTime
Length
A
17345
op10
27/4/22 08:00
04:00:00
B
17345
op 20
28/4/22 10:17
06:00:00
C
17345
op 30
3/5/22 16:20
08:00:00
B
12727
op 20
27/4/22 11:00
02:00:00
A
18001
op10
27/4/22 13:20
01:00:00
B
18001
op 20
29/4/22 13:00
04:00:00
C
18001
op 30
5/5/22 13:00
09:00:00
I understand it's easy enough to do the Order By OrderNo, OpNo to get the rows sorted into 'blocks' of each OrderNo with asc (or desc) OpNo. But the problem I'm then having is how do I order each of these 'blocks' by the StartTime of the lowest OpNo in each of these 'blocks'?
Hopefully I've made sense?
Using first_value
select *
from (
select *, first_value(StartTime) over(partition by OrderNo order by OpNo) blockStart
from mytable
) t
order by blockStart, OrderNo, OpNo

Creating custom week number in sql

I have a table with ID and Date columns.
Table A:
ID
Date
24
2019-10-29
24
2019-10-30
24
2019-10-31
.....
....
I need to add a column called "Week_Num" such that:
If there is a new "ID" and the first date begins on a Tuesday (eg: 29th Oct,2019 in ID 24 is Tuesday) then the week will start with 1
Week will always end on Saturday irrespective of whether 7 days are over or not with only one exception (see point 3)
If there is a new "ID" and the first date begins before Tuesday (eg: 25th Oct,2020 in ID 25 is Sunday) then the week will start with 0 and once it reaches Tuesday the week_num will become 1.
Expected Output:
ID
Date
Day_Of_Week
Week_Num
24
2019-10-29
Tue
1
24
2019-10-30
Wed
1
24
2019-10-31
Thur
1
24
2019-11-01
Fri
1
24
2019-11-02
Sat
1
24
2019-11-03
Sun
2
24
2019-11-04
Mon
2
24
2019-11-05
Tues
2
24
2019-11-06
Wed
2
24
2019-11-07
Thurs
2
24
2019-11-08
Fri
2
24
2019-11-09
Sat
2
24
..........
.
..
24
2020-03-14
.
..
25
2020-10-25
Sun
0
25
2020-10-26
Mon
0
25
2020-10-27
Tue
1
25
2020-10-28
Wed
1
25
2020-10-29
Thur
1
25
2020-10-30
Fri
1
25
2020-10-31
Sat
1
What I have so far:
select
distinct ID,min(Date) over (partition by ID order by date) as firstTuesdayOfSeason
from
TableA
group by ID,Date
having datepart(weekday,Date)=3
which gets the first Tuesday for every new ID and gives the following output:
ID
firstTuesdayOfSeason
24
2019-10-29
25
2020-10-27
I was thinking of joining this table with the Table A (the one with ID, Date as columns) but I don't know how to implement the weird Saturday logic.
This might work for you.
Find min-date, offset to Tuesday for each ID, and calculate using date-diff for each row against the min-date
select a.ID, a.Date, weekday(a.Date), greatest(0, 1 + floor((datediff(a.Date, b.since) - b.off) / 7)) week_num
from TableA a
join (
select ID, min(Date) As since, (case when weekday(min(Date)) > 1 then 8 - weekday(min(Date)) else 1 - weekday(min(Date)) end) off
from TableA
group by ID
) b ON a.ID = b.ID
order by a.ID, a.Date
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=657c96465338a398ced73bc256ff92c0

MariaDb/MySQL sliding sum over 24 hours

My table have fields that represent starting and ending working period as datetime.
I need to find related entries that match a total of 14hours min over a sliding period of 24 hours.
I think window function will (maybe) save me, but MariadDB (i use) doesn't implement yet Range time intervals in window function.
here is some example data:
id starting_hour ending_hour
-- ------------------- -------------------
1 2018-09-02 06:00:00 2018-09-02 08:30:00
2 2018-09-03 08:30:00 2018-09-03 10:00:00
4 2018-09-03 11:00:00 2018-09-03 15:00:00
5 2018-09-02 15:30:00 2018-09-02 16:00:00
6 2018-09-02 16:15:00 2018-09-02 17:00:00
7 2018-09-20 00:00:00 2018-09-20 08:00:00
8 2018-09-19 10:00:00 2018-09-19 12:00:00
9 2018-09-19 12:00:00 2018-09-19 16:00:00
10 2018-10-08 12:00:00 2018-10-08 14:00:00
11 2018-10-29 09:00:00 2018-10-29 10:00:00
So how to find rows where in a 24 hours window their sum a more or equal to 14 hours.
thanks
Edit:
SELECT
id,
starting_hour,
ending_hour,
TIMEDIFF (ending_hour, starting_hour) AS duree,
(
SELECT SUM(TIMEDIFF(LEAST(ending_hour, DATE_ADD(a.starting_hour, INTERVAL 24 HOUR)), starting_hour)) / 10000
FROM `table` b
WHERE b.starting_hour BETWEEN a.starting_hour AND DATE_ADD(a.starting_hour, INTERVAL 24 HOUR)
) AS duration
FROM
`table` a
HAVING duration >= 14
ORDER BY starting_hour ASC
;
This returns Id 8 but i want the whole period. (eg: Id 8, Id 9 and Id 7)
EDIT2:
The expected results are ranges of working time where they are in a window of 24 hours and where their sum are more or equal to 14 hours.
EDIT 3:
In fact under MySQL 8 this seems to work.
SELECT * FROM (
SELECT
*,
SEC_TO_TIME(SUM(TIME_TO_SEC(TIMEDIFF(hs.`ending_hour`, hs.`starting_hour`))) OVER (ORDER BY hs.starting_hour RANGE BETWEEN INTERVAL '12' HOUR PRECEDING AND INTERVAL '12' HOUR following)) AS tot
FROM
table hs
WHERE hs.`starting_hour` > DATE_SUB(NOW(), INTERVAL 50 DAY) AND hs.`ending_hour` <= NOW()
ORDER BY hs.`starting_hour` ASC
) t1
HAVING tot >= '14:00:00'
;
Is there a way to do it under MariaDB 10.2 without window function ? Or without window range function ?

Mysql Select Query group by 7 Days Interval

I'm trying to group my data by 7 days interval.
for example.
I have a data which is you can find it below.
count startDate finish_date
1247 2017-03-09 08:43:18 2017-03-09 16:05:34
1681 2017-03-10 08:30:13 2017-03-10 16:31:55
1464 2017-03-11 08:36:50 2017-03-11 16:42:03
1343 2017-03-12 08:26:57 2017-03-12 16:39:58
1333 2017-03-13 08:35:34 2017-03-13 16:26:18
1215 2017-03-14 08:36:58 2017-03-14 16:13:20
1817 2017-03-16 08:24:49 2017-03-16 17:18:19
1675 2017-03-17 08:22:30 2017-03-17 16:36:58
1546 2017-03-18 08:33:52 2017-03-18 16:51:52
1443 2017-03-20 08:11:00 2017-03-20 16:26:38
1481 2017-03-21 08:26:04 2017-03-21 16:57:30
1574 2017-03-23 08:19:07 2017-03-23 16:12:46
1270 2017-03-24 08:25:25 2017-03-24 16:37:59
1765 2017-03-25 08:22:58 2017-03-25 16:44:24
1200 2017-03-26 08:37:47 2017-03-26 14:59:51
1479 2017-03-27 08:17:50 2017-03-27 15:18:32
And I wanted to group them by 7 days interval.
I tried this. for it.
select count(*), min(locationDate) as startDate, max(locationDate) as finish_date from location where tagCode = 24901 and xLocation >= 278 and xLocation <= 354 and yLocation >= 239 and yLocation <= 426 and locationDate
>= DATE_SUB('2017-03-01 00:00:01',INTERVAL 7 day) and locationDate <= '2017-03-27 23:59:59' group by DATEDIFF(locationDate, '2017-03-01 00:00:01') div 7
And data is like.
count startDate finish_date
8283 2017-03-09 08:43:18 2017-03-14 16:13:20
7962 2017-03-16 08:24:49 2017-03-21 16:57:30
7291 2017-03-23 08:19:07 2017-03-27 15:22:05
Problem is Second Week it must start from 2017-03-15 and third week need to start 2017-03-22 but because of there is no data in on days its not starting how can I fix it ?
As I asked you in my comment, I think the result you wrote would be good with the input you provided, but it wouldn't be with a different input (like having 2017-03-15 but not 2017-03-16).
A solution could be to write the query kind of like this
select sum(count) as count, min(location_date), max(location_date)
from (
select t1.location_date,
t1.count,
date_sub(location_date, interval (datediff(t1.location_date, t2.min_date) % 7) day) week_start
from location t1
cross join
(select min(location_date) as min_date from location) t2
where t1.tagCode = 24901 and
t1.xLocation between 278 and 354 and
t1.yLocation between 239 and 426 and
t1.locationDate >= DATE_SUB('2017-03-01 00:00:01',INTERVAL 7 day) and
t1.locationDate <= '2017-03-27 23:59:59'
) t3
group by week_start
I tested a simplified version of this on a simplified version of your input, there might be typos...
Edit
To display both interval starting date and ending date, try with this
select sum(count) as count, week_start, week_end
from (
select t1.count,
date_sub(location_date, interval (datediff(t1.location_date, t2.min_date) % 7) day) week_start,
date_sub(location_date, interval (datediff(t1.location_date, t2.min_date) % 7) - 6 day) week_end
from location t1
cross join
(select min(location_date) as min_date from location) t2
where t1.tagCode = 24901 and
t1.xLocation between 278 and 354 and
t1.yLocation between 239 and 426 and
t1.locationDate >= DATE_SUB('2017-03-01 00:00:01',INTERVAL 7 day) and
t1.locationDate <= '2017-03-27 23:59:59'
) t3
group by week_start, week_end
I just use GROUP BY DATE_FORMAT:
SELECT someTimeStamp,SUM(amount) AS Total FROM sometable WHERE 1 GROUP BY DATE_FORMAT(someTimeStamp,"%Y%v")
I think you can do that :
you need to change the result of your query from this :
1 1247 2017-03-09 08:43:18 2017-03-09 16:05:34
2 1681 2017-03-10 08:30:13 2017-03-10 16:31:55
3 1464 2017-03-11 08:36:50 2017-03-11 16:42:03
4 1343 2017-03-12 08:26:57 2017-03-12 16:39:58
5 1333 2017-03-13 08:35:34 2017-03-13 16:26:18
6 1215 2017-03-14 08:36:58 2017-03-14 16:13:20
7 1817 2017-03-16 08:24:49 2017-03-16 17:18:19
8 1675 2017-03-17 08:22:30 2017-03-17 16:36:58
9 1546 2017-03-18 08:33:52 2017-03-18 16:51:52
10 1443 2017-03-20 08:11:00 2017-03-20 16:26:38
11 1481 2017-03-21 08:26:04 2017-03-21 16:57:30
12 1574 2017-03-23 08:19:07 2017-03-23 16:12:46
13 1270 2017-03-24 08:25:25 2017-03-24 16:37:59
14 1765 2017-03-25 08:22:58 2017-03-25 16:44:24
15 1200 2017-03-26 08:37:47 2017-03-26 14:59:51
16 1479 2017-03-27 08:17:50 2017-03-27 15:18:32
to This using the logic of computing the number of days between the max date and the min date of the first line :
-- max date of the row min date of the first row
select FLOOR(datediff('2017-03-12 16:05:34', '2017-03-09 08:43:18')/7);
select FLOOR( datediff('2017-03-16 17:18:19', '2017-03-09 08:43:18')/7);
-- what is important that you always compute the max date - the min date of the first row the same row like in your example is : '2017-03-09 08:43:18'
select FLOOR( datediff(max_date, '2017-03-09 08:43:18')/7);
rec_sum min_date max_date day_diff
1 1247 2017-03-09 08:43:18 2017-03-09 16:05:34 0
2 1681 2017-03-10 08:30:13 2017-03-10 16:31:55 0
3 1464 2017-03-11 08:36:50 2017-03-11 16:42:03 0
4 1343 2017-03-12 08:26:57 2017-03-12 16:39:58 0
5 1333 2017-03-13 08:35:34 2017-03-13 16:26:18 0
6 1215 2017-03-14 08:36:58 2017-03-14 16:13:20 0
7 1817 2017-03-16 08:24:49 2017-03-16 17:18:19 1
8 1675 2017-03-17 08:22:30 2017-03-17 16:36:58 1
9 1546 2017-03-18 08:33:52 2017-03-18 16:51:52 1
10 1443 2017-03-20 08:11:00 2017-03-20 16:26:38 1
11 1481 2017-03-21 08:26:04 2017-03-21 16:57:30 1
12 1574 2017-03-23 08:19:07 2017-03-23 16:12:46 2
13 1270 2017-03-24 08:25:25 2017-03-24 16:37:59 2
14 1765 2017-03-25 08:22:58 2017-03-25 16:44:24 2
15 1200 2017-03-26 08:37:47 2017-03-26 14:59:51 2
16 1479 2017-03-27 08:17:50 2017-03-27 15:18:32 2
-- now you can group the new result by the new field the result of division.
select
sum(rec_sum) ,
min(min_date),
max(max_date)
from (query result in the previous list)
group by day_diff
i know it's a little bit hard but i think you can do it the hard way is the day_diff computing .

Counted value grouped by date range

From a Call Detail Record (CDR) table, I need to compute the maximum channels that have been simultaneously busy in a specified time range. The table has one column for the end of the call time and another column for the call duration.
CallID DateTime Duration
1 2014-04-28 11:37:54 35
2 2014-04-28 11:37:53 82
3 2014-04-28 11:37:42 53
4 2014-04-28 11:37:37 159
5 2014-04-28 11:37:13 49
6 2014-04-28 11:37:02 267
7 2014-04-28 11:37:00 267
8 2014-04-28 11:36:54 49
9 2014-04-28 11:36:20 266
I would need a report for getting a response like this (time range in hours for example):
DateRange NumBusyChannels
2014-04-28 11:00-12:00 9
2014-04-28 10:00-11:00 5
EDIT: Sample table to test Miky response.
CallID DateTime Duration
014 2013-07-23 08:42:42 3
015 2013-07-23 08:42:42 3
019 2013-07-23 08:42:42 3
012 2013-07-23 08:10:00 3
013 2013-07-23 08:10:00 3
016 2013-07-23 08:10:00 3
017 2013-07-23 08:10:00 3
018 2013-07-23 08:10:00 3
You might start with a query like this which should give you the number of overlapping calls for each call.
SELECT COuter.*,
COALESCE(COverlap.Cnt,0) as NumChannelsInUse
FROM Cdr COuter
LEFT OUTER JOIN(
SELECT C1.CallId,
COUNT(*) as Cnt
FROM Cdr C1
INNER JOIN Cdr C2
ON (C2.DateTime >= C1.DateTime
AND
C2.DateTime <= DATE_ADD(C1.DateTime, INTERVAL C1.Duration SECOND))
OR
(DATE_ADD(C2.DateTime, INTERVAL C2.Duration SECOND) >= C1.DateTime
AND
DATE_ADD(C2.DateTime, INTERVAL C2.Duration SECOND) <= DATE_ADD(C1.DateTime, INTERVAL C1.Duration SECOND))
OR
(C1.DateTime >= C2.DateTime
AND
C1.DateTime <= DATE_ADD(C2.DateTime, INTERVAL C2.Duration SECOND) )
OR
(DATE_ADD(C1.DateTime, INTERVAL C1.Duration SECOND) >= C2.DateTime
AND
DATE_ADD(C1.DateTime, INTERVAL C1.Duration SECOND) < DATE_ADD(C2.DateTime, INTERVAL C2.Duration SECOND))
GROUP BY C1.CallId) COverlap
ON COuter.CallId = COverlap.CallId
You would then have to further manipulate the query to partition the results by hour and then to select the maximum for each hour..