How to do Week over Week Increase in SQL [closed] - mysql

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
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 write a query to find out: on each day, the total spend and the week-over-week change since 1st January.
So, week over week should show 8th day's total spending - 1st days total spending. I can achieve that lag window function but what I am not sure what to do if the first day is not mentioned in the date column. Let's say there's no spending on the first day of may then the answer would go wrong if I had used lag function. Is there a way that I could write a query that would look for the total spending through dates rather than lag function? and if on the first day I have no spending, I could get 1200-0=1200 is the WOW change. Also, I can't create a dates table that I can join the ads table on.
I have written this much so far:
select dates, sum(spend) "total_spend_each_day",
from fb_ads as f
where dates>= '2018-01-01'
group by dates
order by 1;
Desired Output:
date | total_spend_each_day | Week_over_week_change
2018-05-01 500 Null
2018-05-02 600 Null
2018-05-03 700 Null
2018-05-04 800 Null
2018-05-05 900 Null
2018-05-06 1000 Null
2018-01-07 1100 Null
2018-01-08 1200 700

Just use lag(). Assuming you have at least one record per day:
select dates, sum(spend) as total_spend_each_day,
sum(spend) - lag(sum(spend), 7) over (order by dates) as diff
from fb_ads as f
where dates >= '2018-01-01'
group by dates
order by 1;
If you don't have data for each day, then just use a window frame with range():
select dates, sum(spend) as total_spend_each_day,
(sum(spend) -
max(sum(spend)) over (order by dates range between interval 7 day and interval 7 day)
) as diff
from fb_ads as f
where dates >= '2018-01-01'
group by dates
order by 1;

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;

Subtract rows and create new columns with results

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;

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

Records count per day, including 0 values

I'm trying to get a query that will show number of visits per day for the last 7 days. Query that I come up with works but it has limitation I do not know how to get rid of.
Imagine, it is August 4th, 2019. Our table visits keeps timestamps of users visits to a website:
ID | timestamp
1 | 2019-08-03
2 | 2019-08-03
3 | 2019-08-02
4 | 2019-07-31
5 | 2019-07-31
6 | 2019-07-31
7 | 2019-07-31
8 | 2019-07-30
9 | 2019-07-30
10 | 2019-07-28
Objective: get number of visits to a website per day for the last 7 days. So the result should be something like:
DATE | NumberOfVisitis
2018-08-04 | 0
2018-08-03 | 2
2018-08-02 | 1
2018-08-01 | 0
2018-07-31 | 4
2018-07-30 | 1
2018-07-29 | 0
My query includes only dates registered in DB (it excludes days with no visits). This makes sense as query is data dependent, instead of calendar.
SELECT DATE_FORMAT(`timestamp`, "%Y%m/%d") AS Date, COUNT(`id`) AS
NumberOfVisitis FROM `visits` WHERE `timestamp` >= DATE_ADD(NOW(),
INTERVAL -7 DAY) GROUP BY DAY(`timestamp`) ORDER BY `timestamp` DESC
Can you please let me know how can I modify my query to include days with no visits in the query result?
MySQL lacks anything like Postgres's generate_series so we have to fake it.
Simplest thing to do is to make a table with a bunch of numbers in it. This will be useful for generating lots of things.
create table numbers ( number serial );
insert into numbers () values (), (), (), (), (), (), ();
From that we can generate a list of the last 7 days.
select date_sub(date(now()), interval number-1 day) as date
from numbers
order by number
limit 7
Then using that as a CTE (or a subquery) we left join it with visits. A left join means all dates will be present.
with dates as (
select date_sub(date(now()), interval number-1 day) as date
from numbers
order by number
limit 7
)
select date, coalesce(sum(id), 0)
from dates
left join visits on date = timestamp
group by date
order by date

SQL Order by 2 conditions

Let's say that I have a database, that looks like that:
price date hour
12.00 2018-12-11 5
13.00 2018-12-04 2
14.00 2018-12-06 1
15.00 2018-12-11 1
16.00 2018-12-04 6
17.00 2018-12-06 10
I need to order by date and if days are the same after hour, so results should be:
price date hour
13.00 2018-12-04 2
16.00 2018-12-04 6
14.00 2018-12-06 1
17.00 2018-12-06 10
15.00 2018-12-11 1
12.00 2018-12-11 5
I tried to write a simple query, but it couldn't take into account 2 conditions, one after another:
SELECT price, date, hour
FROM table
WHERE date BETWEEN '2018-12-04' AND '2018-12-11'
ORDER BY date, hour
Could anyone help with this issue ?
Thanks All !
The only real issue I can think of would be if hour were stored as a string. If so, use implicit conversion:
SELECT price, date, hour
FROM table
WHERE date BETWEEN '2018-12-04' AND '2018-12-11'
ORDER BY date, hour + 0;
A simple ORDER BY will suffice:
select * from my_table order by `date`, hour;
Please note the date is usually a reserved word, so its usage is discouraged, and needs to be quoted.