Subtract rows and create new columns with results - mysql

I have an issue with one SQL query in MySQL. My table looks like below:
Index User Date Speed
1 X 2018-01-01 10:00:00 23
1 X 2018-01-01 10:00:20 50
1 X 2018-01-02 10:00:00 40
1 Z 2018-01-01 10:00:00 20
1 Z 2018-01-02 10:00:00 40
1 Z 2018-01-03 10:00:00 50
and result should be like this:
Index User Date Speed Date_diff Speed_diff
1 X 2018-01-01 10:00:00 23
1 X 2018-01-01 10:00:20 50 20s 27
1 X 2018-01-01 10:02:00 40 1m40s -10
1 Z 2018-01-01 10:00:00 20 -2m -20
1 Z 2018-01-02 10:00:00 40 1d 20
1 Z 2018-01-03 10:00:00 50 1d 10
So basically I need to substract rows one after another and create a new columns one with results. I am starting an adventure with SQL and I am not sure how I could do this? Any idea?
I tried to do this using this https://dev.mysql.com/doc/refman/8.0/en/window-function-descriptions.html#function_lag but I think that my syntax is wrong
SELECT objid,
LAG(Date) OVER AS 'lag',
LEAD(Speed) OVER AS 'Lead',
date- LAG(date) OVER AS 'lag diff',
speed- LEAD(speed) OVER AS 'Lead diff',
FROM tabel;

Try something like:
SELECT Index, User, 'Date', Speed,
'Date' - LAG('Date') OVER w AS Date_diff,
Speed - LAG(Speed) OVER w AS Speed_diff
FROM table
WINDOW w AS (ORDER BY User, 'Date');

Only use single quotes for string and date values -- never for column names.
Your code also needs a windowing clause, and to adjust the date/time arithmetic. If you can represent the date/time difference as a time, then:
SELECT t.*,
secs_to_time(to_seconds(t.date) - LAG(to_seconds(t.date)) OVER (PARTITION BY user ORDER BY DATE)) AS date_diff
(t.speed - LAG(speed) OVER (PARTITION BY user ORDER BY DATE)) as speed_diff
FROM tabel t;

Related

Fifty two week high and low

We have a records table with the following data. We need to fill fifty-two week(or 365 days)high and low amounts in ft_high and ft_low columns? How can we accomplish this in MySQL?
Fifty-two-week data including the same date.
id user_id date amount ft_high ft_low
10 21 2020-10-11 1500 1800 950
11 22 2020-10-12 1950 2410 1738
12 21 2020-10-15 1150 1800 1500
----------------------------------------
----------------------------------------
99 21 2020-11-15 1950 1950 950
You can use window functions:
selet t.*,
min(amount) over (partition by user_id order by date range between interval 52 week preceding and current row) as ft_low,
max(amount) over (partition by user_id order by date range between interval 52 week preceding and current row) as ft_high,
from t;

Recursively calculate records within a time interval in mysql

I have a table where records will be getting inserted every 4 hours on a daily basis. If the record was not inserted for continuous 4 hours, I need to insert a log into another table. Below is the table schema.
Id DocPathid CreatedAt
1 1 2021-04-02 00:00:00
2 1 2021-04-02 04:00:00
3 1 2021-04-02 09:00:00
4 1 2021-04-02 12:00:00
5 1 2021-04-02 16:00:00
6 1 2021-04-02 20:00:00
7 1 2021-04-02 24:00:00
In the above case, there was no records inserted within a interval of 4hours (i.e. between 2021-04-02 04:00:00 & 2021-04-02 09:00:00). The query should return no. of failure count (in this case it is failed for 1 time).
Is there a way to achieve this in MySQL?
You can do something like this.
select count(1)
from (
select id, CreatedAt , timestampdiff(hour, CreatedAt
, lead(CreatedAt,1) over (partition by DocPathid order by CreatedAt) ) as hour
from Table1
) t
where hour >4
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=9b0c631145422dbccd2ea23f0a7d2011

Uneven automated buckets/bins in SQL

ads Table:
-one row per ad per day
date | ad_id | account_id | spend
2018-05-01 123 1101 100
2018-05-02 123 1101 125
2018-05-03 124 1101 150
2018-05-04 124 1101 150
2018-05-04 125 1105 150
2018-05-04 126 1105 150
2018-05-04 123 1101 150
2018-01-01 123 1101 150
I am trying to create a histogram to show the how much advertisers have spent in last 7 days.
I want the first bucket to be $10-999.99 and others to be $1000-1999.99,$2000-2999.99 etc but this I want to achieve through automation not by manually mentioning buckets through case function.
My current code does well in creating even automated buckets:
select CONCAT(1000*FLOOR(last_7_days_spend/1000), "-", 1000*FLOOR(last_7_days_spend/1000)+999.99) "spend($)" , count(*) "frequency"
from
(select account_id, sum(spend) "last_7_days_spend"
from fb_ads
where date between date_sub(curdate(), interval 7 day) and date_sub(curdate(), interval 1 day)
group by account_id) as abc
group by 1
order by 1;
and it returns this:
spend | frequency
0-999.99 2
2000-2999.99 1
But want to write some similar kind of query which should filter out records and start from $10-999.99 instead of $0.00-999.99.
Desired output:
spend | frequency
10-999.99 2
2000-2999.99 1
You'll need to use a CASE expression to define the first bucket, but you can automate the other buckets within that expression. Note that if you don't want a bucket for a spend of less than $10, you'll need to filter those values out:
SELECT
CASE WHEN last_7_days_spend < 1000 THEN '10-999.99'
ELSE CONCAT(1000*FLOOR(last_7_days_spend/1000), "-", 1000*FLOOR(last_7_days_spend/1000)+999.99)
END AS `spend($)`,
COUNT(*) AS `frequency`
FROM (
SELECT account_id, SUM(spend) AS `last_7_days_spend`
FROM fb_ads
WHERE date BETWEEN DATE_SUB(CURDATE(), INTERVAL 7 DAY) AND DATE_SUB(CURDATE(), INTERVAL 1 DAY)
GROUP BY account_id
) as abc
WHERE last_7_days_spend >= 10
GROUP BY 1
ORDER BY 1
Small demo on db-fiddle

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 ?

Select query that outputs counts of rows that are within a certain time period

My question is little bit more complicated than what the title implies but here it is:
I have a table with punch data formatted like this:
name time_in time_out location
1 2018-05-31 10:09:00 2018-05-31 16:06:00 1
3 2018-05-31 10:12:00 2018-05-31 17:03:00 1
I would like a select query that returns the total time in hours of people that are working during a 15min time frame for each location. Here is an example output for the two rows given:
time labor_hours location
2018-05-31 10:00:00 .15 1
2018-05-31 10:15:00 .50 1
2018-05-31 10:30:00 .50 1
2018-05-31 10:45:00 .50 1
2018-05-31 11:00:00 .50 1
...
2018-05-31 15:45:00 .50 1
2018-05-31 16:00:00 .35 1
2018-05-31 16:15:00 .25 1
2018-05-31 16:30:00 .25 1
2018-05-31 16:45:00 .25 1
2018-05-31 17:00:00 .10 1
Labor hours is total hours worked during a 15min time period in hours. So for example, the first row was calculated by looking at the first two rows and seeing that from 10:00:00 - 10:15:00 employee 1 and 2 worked for a total of 9 minutes. Since it's in hours 9/60 = .15.
I'm new to sql so I'm pretty lost on how to start with this.
If you are using MySQL 8.0, you can use the CTE feature as follows
WITH cte (timeStamp) AS
(
SELECT "2018-05-31 00:00:00"
UNION ALL
SELECT TIMESTAMPADD(MINUTE, 15, timeStamp)
WHERE timeStamp < 2018-06-01 00:00:00
)
SELECT timeStamp FROM cte;
SELECT cte.time,
sum(TIMESTAMPDIFF(MINUTE, punch.time,
TIMESTAMPAD(MINUTE, 15, cte.timeStamp))) as labour_hours,
punch.location
FROM cte LEFT OUTER JOIN punch ON punch.time >=cte.timeStamp
AND punch.time < TIMESTAMPADD(MINUTE, 15, cte.timeStamp)
GROUP BY punch.location, cte.timeStamp
If you are using an older version of MySQL, you need to create a stored procedure that generates the timestamps with 15 minute intervals.