date : group by month from day 2 to day 1 next month - mysql

I have query like this :
SELECT EXTRACT(MONTH FROM d.mydate) AS synmonth, SUM(apcp) AS apcptot
FROM t_synop_data2 d
WHERE d.mydate
BETWEEN '2011-01-01' AND '2011-12-31'
AND d.idx_synop = '06712'
GROUP BY synmonth
This query adds all rain (apcp) in a month like this :
1 32.8 => from 2011.01.01 to 2011.01.31
2 27.2 => from 2011.02.01 to 2011.02.28
3 21.0
4 21.8
5 88.5
6 131.4
7 118.6
8 57.1
9 80.9
10 84.6
11 1.1
12 143.5 => from 2011.12.01 to 2011.12.31
That's what I want, but with a little difference.
This difference is that i have to adds apcp from day 2 in the month to day 1 next month and then return a result like above.
1 132.8 => from 2011.01.02 to 2011.02.01
2 27.2 => from 2011.02.02 to 2011.03.01
3 21.0
4 21.8
5 88.5
6 131.4
7 118.6
8 57.1
9 80.9
10 84.6
11 1.1
12 143.5 => from 2011.12.02 to 2012.01.01
I tried something with add_date(), extract() or date_format() but without result.
Thank you for your answer
Vince

Here is the query :
SELECT EXTRACT(MONTH FROM ADDDATE(d.mydate,-1) ) AS synmonth
, SUM(apcp) AS apcptot
FROM t_synop_data2 AS d
WHERE ADDDATE(d.mydate,-1) BETWEEN '2011-01-01' AND '2012-12-31'
AND d.idx_synop = '06712'
GROUP BY synmonth
You can check the result by adding two columns like this:
SELECT EXTRACT(MONTH FROM ADDDATE(d.mydate,-1) ) AS synmonth
, SUM(apcp) AS apcptot
, MIN(d.mydate) AS date_min
, MAX(d.mydate) AS date_max
FROM t_synop_data2 AS d
WHERE ADDDATE(d.mydate,-1) BETWEEN '2011-01-01' AND '2012-12-31'
AND d.idx_synop = '06712'
GROUP BY synmonth

You can group by EXTRACT(MONTH FROM d.mydate - INTERVAL 1 DAY)
SELECT EXTRACT(MONTH FROM d.mydate) AS synmonth, SUM(apcp) AS apcptot
FROM t_synop_data2 d
WHERE d.mydate
BETWEEN '2011-01-01' AND '2011-12-31'
AND d.idx_synop = '06712'
GROUP BY EXTRACT(MONTH FROM d.mydate - INTERVAL 1 DAY)

Related

How to retrieve data from previous 4 weeks (mySQL)

I am trying to write a query to get the last 4 weeks (Mon-Sun) of data. I want every week of data to be stored with an individual and shared table.
every week data store based on name if same name repeated on single week amt should sum and if multiple name it should be show data individual, To see an example of what I am looking for, I have included the desired input and output below.
this is my table
date
amt
name
2022-04-29
5
a
2022-04-28
10
b
2022-04-25
11
a
2022-04-23
15
b
2022-04-21
20
b
2022-04-16
20
a
2022-04-11
10
a
2022-04-10
5
b
2022-04-05
5
b
i want output like this
date
sum(amt)
name
2022-04-25 to 2020-04-29
16
a
2022-04-25 to 2020-04-29
10
b
2022-04-18 to 2022-04-24
35
b
2022-04-11 to 2022-04-17
30
a
2022-04-04 to 2022-04-10
10
b
I would appreciate any pointers or 'best-practises' which I should employ to achieve this task.
You can try to use DATE_ADD with WEEKDAY get week first day and end day.
SELECT
CASE WHEN
weekofyear(`date`) = weekofyear(NOW())
THEN 'current week'
ELSE
CONCAT(date_format(DATE_ADD(`date`, interval - WEEKDAY(`date`) day), '%Y-%m-%d'),' to ',date_format(DATE_ADD(DATE_ADD(`date`, interval -WEEKDAY(`date`) day), interval 6 day), '%Y-%m-%d'))
END 'date',
SUM(amt)
FROM T
GROUP BY
CASE WHEN
weekofyear(`date`) = weekofyear(NOW())
THEN 'current week'
ELSE
CONCAT(date_format(DATE_ADD(`date`, interval - WEEKDAY(`date`) day), '%Y-%m-%d'),' to ',date_format(DATE_ADD(DATE_ADD(`date`, interval -WEEKDAY(`date`) day), interval 6 day), '%Y-%m-%d'))
END
sqlfiddle
EDIT
I saw you edit your question, you can just add name in group by
SELECT
CONCAT(date_format(DATE_ADD(`date`, interval - WEEKDAY(`date`) day), '%Y-%m-%d'),' to ',date_format(DATE_ADD(DATE_ADD(`date`, interval -WEEKDAY(`date`) day), interval 6 day), '%Y-%m-%d')) 'date',
SUM(amt),
name
FROM T
GROUP BY
CONCAT(date_format(DATE_ADD(`date`, interval - WEEKDAY(`date`) day), '%Y-%m-%d'),' to ',date_format(DATE_ADD(DATE_ADD(`date`, interval -WEEKDAY(`date`) day), interval 6 day), '%Y-%m-%d')),
name
ORDER BY 1 desc
sqlfiddle
This is in SQL Server, and just a mess about. Hopefully it can be of some help.
with cteWeekStarts
as
(
select
n,dateadd(week,-n,DATEADD(week, DATEDIFF(week, -1, getdate()), -1)) as START_DATE
from
(values (1),(2),(3),(4)) as t(n)
), cteStartDatesAndEndDates
as
(
select *,dateadd(day,-1,lead(c.start_date) over (order by c.n desc)) as END_DATE
from cteWeekStarts as c
)
,cteSalesSumByDate
as
(
select s.SalesDate,sum(s.salesvalue) as sum_amt from
tblSales as s
group by s.SalesDate
)
select c3.n as WeekNum,c3.START_DATE,isnull(c3.END_DATE,
dateadd(day,6,c3.start_date)) as END_DATE,
(select sum(c2.sum_amt) from cteSalesSumByDate as c2 where c2.SalesDate
between c3.START_DATE and c3.END_DATE) as AMT
from cteStartDatesAndEndDates as c3
order by c3.n desc

Using count(*) .. Over(*) in mysql

My data looks like the following,
requestedDate Status
2020-04-21 APPROVED
2020-04-23 APPROVED
2020-04-27 PENDING
2020-05-21 PENDING
2020-06-01 APPROVED
I would like to extarct a report that looks like the following where the count is by status and month.
Status StatusCount Month MonthCount CountTotal
APPROVED 2 APR 3 5
PENDING 1 MAY 1 5
APPROVED 1 JUN 1 5
My sql looks like the following,
select distinct
status,
count(status) over (partition by status) as total_by_status,
CASE
WHEN Month(requestedDate) = 1 THEN 'JAN'
WHEN Month(requestedDate) = 2 THEN 'FEB'
WHEN Month(requestedDate) = 3 THEN 'MAR'
WHEN Month(requestedDate) = 4 THEN 'APR'
WHEN Month(requestedDate) = 5 THEN 'MAY'
WHEN Month(requestedDate) = 6 THEN 'JUN'
WHEN Month(requestedDate) = 7 THEN 'JUL'
WHEN Month(requestedDate) = 8 THEN 'AUG'
WHEN Month(requestedDate) = 9 THEN 'SEP'
WHEN Month(requestedDate) = 10 THEN 'OCT'
WHEN Month(requestedDate) = 11 THEN 'NOV'
WHEN Month(requestedDate) = 12 THEN 'DEC'
END AS myMONTH,
count(Month(requestedDate)) over (partition by Month(requestedDate)) as total_by_month,
count(*) over () as Totals
from Reports
where
requestedDate between DATE_SUB(CURDATE(), INTERVAL 120 DAY) and date(CURDATE())
order by 1;
The output for that looks like,
status total_by_status myMONTH total_by_month Totals
APPROVED 3 APR 3 5
APPROVED 3 JUN 1 5
PENDING 2 APR 3 5
PENDING 2 MAY 1 5
dbfiddle
First you need a valid aggregation query. Then you can use window functions on top of it (here, you would typically compute window sums of the counts).
I would write this as:
select
status,
count(*) status_count,
date_format(requestedDate, '%b') requested_month
sum(count(*)) over(partition by year(requestedDate), month(requestedDate)) month_count,
sum(count(*)) over() total_count
from reports
where requestedDate between current_date - interval 120 day and current_date
group by status, year(requestedDate), month(requestedDate), date_format(requestedDate, '%b')
Since it is just for last 120 days (last years same month wouldnt occur) so we can also use distinct instead of group by), something like below:
select distinct status,
count(*) over (partition by status) as total_by_status,
date_format(requestedDate, '%b') mymonth,
count(Month(requestedDate)) over (partition by Month(requestedDate)) as total_by_month,
count(*) over () as total_by_month
from reports
where requestedDate between current_date - interval 120 day and current_date
order by status, mymonth
Demo

Bigquery mysql to adjust dates for active weekdays

In a previous question the answer was given to perform a query which would get revenue from the last year in a new column. This worked perfectly, but now I have a follow-up question. (please review this link to have a look at the previous question)
The query used to get this data (With thanks to Mikhail):
#standardSQL
SELECT
a.date, a.location, a.revenue,
DATE_SUB(a.date, INTERVAL 1 YEAR) date_last_year,
IFNULL(b.revenue, 0) revenue_last_year
FROM `project.dataset.table` a
LEFT JOIN `project.dataset.table` b
ON a.location = b.location
AND DATE_SUB(a.date, INTERVAL 1 YEAR) = b.date
The simplified outcome looks as follows (limited to 1 location):
date revenue revenue_last_year
2019-01-31 1471,2577 2185,406
2019-01-30 1291,1111 4723,7439
2019-01-29 2178,6532 2263,5283
2019-01-28 1531,8021 0
2019-01-26 1578,1247 2446,6234
2019-01-25 1299,644 1522,4537
2019-01-24 788,2669 1979,104
2019-01-23 787,441 4117,7927
2019-01-22 2437,2951 1876,2479
2019-01-21 1071,0476 0
2019-01-19 2291,0456 2289,8657
The follow up question relates to working with weekdays from last year. As you can see the revenue last year has '0' values. That's because it's a day that location A was closed. However, to make an accurate comparison on a day by day basis we need to locate the revenue for the day this revenue was open.
For more information, have a look at this table below to see what it looks like when we take the last 10 days of Jan this year and append two columns with the revenues of last year found manually:
date revenue revenue_last_year date revenue
2019-01-31 1471,2577 2185,406 2018-01-31 2185,406
2019-01-30 1291,1111 4723,7439 2018-01-30 4723,7439
2019-01-29 2178,6532 2263,5283 2018-01-29 2263,5283
2019-01-28 1531,8021 0 2018-01-27 2843,6616
2019-01-26 1578,1247 2446,6234 2018-01-26 2446,6234
2019-01-25 1299,644 1522,4537 2018-01-25 1522,4537
2019-01-24 788,2669 1979,104 2018-01-24 1979,104
2019-01-23 787,441 4117,7927 2018-01-23 4117,7927
2019-01-22 2437,2951 1876,2479 2018-01-22 1876,2479
2019-01-21 1071,0476 0 2018-01-20 2561,4086
2019-01-19 2291,0456 2289,8657 2018-01-19 2289,8657
Please note the differences in dates.
What would be a good way to solve this? Would it be necessary to adjust to a query for weekdays and how would you approach this?
Below is for BigQuery Standard SQL
#standardSQL
SELECT
a.date, a.location, ANY_VALUE(a.revenue) revenue,
ARRAY_AGG(
STRUCT(b.date AS date_last_year, b.revenue AS revenue_last_year)
ORDER BY b.date DESC LIMIT 1
)[OFFSET(0)].*
FROM `project.dataset.table` a
CROSS JOIN `project.dataset.table` b
WHERE a.location = b.location
AND b.date BETWEEN DATE_SUB(DATE_SUB(a.date, INTERVAL 1 YEAR), INTERVAL 7 DAY) AND DATE_SUB(a.date, INTERVAL 1 YEAR)
GROUP BY a.date, a.location
You can test, play with above using dummy/sample data (I used same as in my answer for your previous question) as in below example
#standardSQL
WITH `project.dataset.table` AS (
SELECT DATE '2018-02-20' `date`, 'A' location, 1 revenue UNION ALL
SELECT '2018-02-21', 'A', 3 UNION ALL
SELECT '2019-02-20', 'A', 5 UNION ALL
SELECT '2019-02-21', 'A', 7 UNION ALL
SELECT '2019-02-22', 'A', 9 UNION ALL
SELECT '2018-02-20', 'B', 2 UNION ALL
SELECT '2018-02-22', 'B', 4 UNION ALL
SELECT '2019-02-20', 'B', 6 UNION ALL
SELECT '2019-02-21', 'B', 8 UNION ALL
SELECT '2019-02-22', 'B', 10
)
SELECT
a.date, a.location, ANY_VALUE(a.revenue) revenue,
ARRAY_AGG(
STRUCT(b.date AS date_last_year, b.revenue AS revenue_last_year)
ORDER BY b.date DESC LIMIT 1
)[OFFSET(0)].*
FROM `project.dataset.table` a
CROSS JOIN `project.dataset.table` b
WHERE a.location = b.location
AND b.date BETWEEN DATE_SUB(DATE_SUB(a.date, INTERVAL 1 YEAR), INTERVAL 7 DAY) AND DATE_SUB(a.date, INTERVAL 1 YEAR)
GROUP BY a.date, a.location
-- ORDER BY a.date, a.location
with result
Row date location revenue date_last_year revenue_last_year
1 2019-02-20 A 5 2018-02-20 1
2 2019-02-20 B 6 2018-02-20 2
3 2019-02-21 A 7 2018-02-21 3
4 2019-02-21 B 8 2018-02-20 2
5 2019-02-22 A 9 2018-02-21 3
6 2019-02-22 B 10 2018-02-22 4
Note the differences in dates :o)

No results from SQL Query

I am trying to count the number of nights guests stay within a range. So less than 7 nights, between 7 and 14 nights and lastly more than 14 nights.
I would like the output to look something like this:
Range | Count
----------------------
<= 7 Nights | 3
8 - 14 Nights | 2
15 Nights + | 1
I am using MYSQL and have attempted the following two solutions, but no results seem to ever appear.
SELECT
CASE
WHEN booking_num_nights <= 7 THEN '<= 7 Nights'
WHEN booking_num_nights > 7 and booking_num_nights <= 14 THEN '8 - 14 Nights'
WHEN booking_num_nights > 14 THEN '15 Nights +'
END AS range, count(*) AS count_num
FROM BOOKING
WHERE booking_property_id = :id
GROUP BY range
The other attempt was as follows:
SELECT booking_num_nights as range, count(*) as count_num
FROM (SELECT
CASE
WHEN booking_num_nights <= 7 THEN '<= 7 Nights'
WHEN booking_num_nights > 7 and booking_num_nights <= 14 THEN '8 - 14 Nights'
WHEN booking_num_nights > 14 THEN '15 Nights +'
END AS range
FROM BOOKING)
WHERE booking_property_id = :id
GROUP BY range
range is a reserved word. Either use a different name or escape it:
SELECT
CASE
WHEN booking_num_nights <= 7 THEN '<= 7 Nights'
WHEN booking_num_nights > 7 and booking_num_nights <= 14 THEN '8 - 14 Nights'
WHEN booking_num_nights > 14 THEN '15 Nights +'
END AS `range`, count(*) AS count_num
FROM BOOKING
WHERE booking_property_id = :id
GROUP BY `range`
DEMO

get average value depending on month?

My table:
rating date
4 12/02/2013
3 12/02/2013
2.5 12/01/2013
3 12/01/2013
4.5 21/11/2012
5 10/11/2012
If I give input as 3 the last three months (02,01,12), average of rating result should come
I tried by using GROUP BY but I get this result:
rating month
3.5 02
2.75 01
For the 12th month no rating so no output.....
My desired result:
rating month
3.5 02
2.75 01
0 12
The problem is that you want to return months that do not exist. If you do not have a calendar table with dates, then you will want to use something like the following:
select d.mth Month,
coalesce(avg(t.rating), 0) Rating
from
(
select 1 mth union all
select 2 mth union all
select 3 mth union all
select 4 mth union all
select 5 mth union all
select 6 mth union all
select 7 mth union all
select 8 mth union all
select 9 mth union all
select 10 mth union all
select 11 mth union all
select 12 mth
) d
left join yourtable t
on d.mth = month(t.date)
where d.mth in (1, 2, 12)
group by d.mth
See SQL Fiddle with Demo
SELECT coalesce(avg(rating), 0.0) avg_rating, req_month
FROM yourTable
RIGHT JOIN
(SELECT month(now()) AS req_month
UNION
SELECT month(now() - INTERVAL 1 MONTH) AS req_month
UNION
SELECT month(now() - INTERVAL 2 MONTH) AS req_month) tmpView
ON month(yourTable.date) = tmpView.req_month
WHERE yourTable.date > ( (curdate() - INTERVAL day(curdate()) - 1 DAY) - INTERVAL 2 MONTH)
OR ratings.datetime IS NULL
GROUP BY month(yourTable.date);