MYSQL Query group by custom Month date? - mysql

I wrote a query that returns monthly sales.
SELECT
count(O.orderid) as Number_of_Orders,
concat (MonthName(FROM_UNIXTIME(O.`date`)),' - ',year(FROM_UNIXTIME(O.date))) as Ordered_Month,
sum(O.total) as TotalAmount,
Month(FROM_UNIXTIME(O.`date`)) as Month_of_Year,
year(FROM_UNIXTIME(O.date)) as Sale_Year
FROM orders O
group by Month_of_Year, Sale_Year
order by Sale_Year DESC,Month_of_Year DESC
I would like to make it group for a custom date like
instead of 1st to 1st, it should group for 10th -10th of every month.
Not sure how to group it that way!

because you are dealing with a time "shift", you'll have to do that math in your equation to "fake it out". Something like
SELECT
count(O.orderid) as Number_of_Orders,
concat(
MonthName( Date_Sub( FROM_UNIXTIME(O.`date`), INTERVAL 10 DAY )),
' - ',
Year( Date_Sub( FROM_UNIXTIME(O.date), INTERVAL 10 DAY) )
) as Ordered_Month,
sum(O.total) as TotalAmount,
Month( Date_Sub( FROM_UNIXTIME(O.`date`), INTERVAL 10 DAY )) as Month_of_Year,
Year( Date_Sub( FROM_UNIXTIME(O.date), INTERVAL 10 DAY )) as Sale_Year
FROM
orders O
group by
Month_of_Year,
Sale_Year
order by
Sale_Year DESC,
Month_of_Year DESC
So, in essence, you are taking the dates ex: March 11-31 + April 1-10 and subtracting "10 days" from them... so for the query, they will look like March 1-31, and April 11-30 will appear like April 1-20 + May, etc for rest of each year...

Not tested.
group by Month_of_Year, ceil(day(o.`date`)/10), Sale_Year
This is a better idea in order to avoid having 4 groups but just 3
select
month(my_date) as your_month,
year(my_date) as your_year,
case
when day(my_date) <= 10 then 1
when day(my_date) between 11 and 20 then 2
else 3 end as decade,
count(*) as total
from table
group by
your_month,your_year,decade
Adapt it to your needs.

Related

How to count only business days (Monday to Friday) per month between two dates in MySQL 5.7?

I have the following table called vacations, where the employee id is displayed along with the start and end date of their vacations:
employee
start
end
1001
26/10/21
22/11/21
What I am looking for is to visualize the number of vacation days that each employee had, but separating them by month and without non-working days (Saturdays and Sundays).
For example, if you wanted to view the vacations for employee 1001, the following result should be displayed:
days
month
4
10
16
11
I have the following query that I have worked with:
SELECT id_employee,
EXTRACT(YEAR_MONTH FROM t.Date) as YearMonth,
COUNT(1) as Days
FROM (SELECT v.id_employee,
DATE_ADD(v.start, interval s.seq - 1 DAY) AS Date
FROM vacations v
CROSS JOIN seq_1_to_100 s
WHERE DATE_ADD(v.start, interval s.seq - 1 DAY) <= v.end
ORDER BY v.id_employee, , v.start, s.seq
) t
GROUP BY id_employee,
EXTRACT(YEAR_MONTH FROM t.Date)
With this query I separate the days between a range of two dates with their respective month, but how could I adapt it to stop considering Saturdays and Sundays? I'm working with MySQL 5.7 in phpMyAdmin
instead of count sum the compaarison of weekday function, which give what day it is .
But you should always save fates n a valid mysql manner 2021-10-28
SELECT id_employee,
EXTRACT(YEAR_MONTH FROM t.Date) as YearMonth,
SUM(WEEKDAY(`Date`) < 5) as Days
FROM (SELECT v.id_employee,
DATE_ADD(v.start, interval s.seq - 1 DAY) AS Date
FROM vacations v
CROSS JOIN seq_1_to_100 s
WHERE DATE_ADD(v.start, interval s.seq - 1 DAY) <= v.end
ORDER BY v.id_employee, v.start, s.seq
) t
GROUP BY id_employee,
EXTRACT(YEAR_MONTH FROM t.Date)

How to display tree results count by each month?

I am trying to count achieve 3 results in one query:
Count all results where ‘app_creationdate’ = ('month') from current row
Count all results where ‘app_start’ = ('month') from current row
Count all results where ‘app_creationsdate’ < ‘app_start’ and ‘app_start’ = ('month') from current row
My Table:
app_id | app_creationdate(timestamp) | app_start(datetime)
00001 | 2014-11-17 19:39:04 | 2014-11-18 09:30:00
SELECT
DATE_FORMAT( app_creationsdate, '%m' ) AS 'month',
COUNT( app_id ) AS 'new',
(SELECT COUNT( app_id )
FROM appointments WHERE MONTH(app_start) = MONTH(NOW())) AS 'act',
(SELECT COUNT( app_id )
FROM appointments WHERE MONTH(app_creationsdate) < MONTH(app_start)) AS 'prev'
FROM appointments
WHERE app_owner = 2 AND app_creationsdate > DATE_SUB(now(), INTERVAL 12 MONTH)
GROUP BY DATE_FORMAT( app_creationsdate, '%Y%m' )
This may be closer to what you want. I'm still a bit confused about the prev scenario, so I did my best. I use EXTRACT(YEAR_MONTH FROM ...) to get the year and month, without the day, of each date so that we can do monthly comparisons. That's probably what you were trying to do with the DATE_FORMAT business.
SELECT DATE_FORMAT( app_creationsdate, '%m' ) AS 'month',
COUNT( app_id ) AS 'new',
-- get all other appointments that start in this month
(SELECT COUNT( act.app_id )
FROM appointments AS act
WHERE EXTRACT(YEAR_MONTH FROM act.app_start) = EXTRACT(YEAR_MONTH FROM appointments.app_creationsdate)) AS 'act',
-- get all appointments that were created before they started (???) and that started before this month
(SELECT COUNT( prev.app_id )
FROM appointments AS prev
WHERE EXTRACT(YEAR_MONTH FROM prev.app_creationsdate) < EXTRACT(YEAR_MONTH FROM appointments.app_creationsdate)
AND EXTRACT(YEAR_MONTH FROM prev.app_start) = EXTRACT(YEAR_MONTH FROM appointments.app_creationsdate)) AS 'prev'
FROM appointments
WHERE app_owner = 2
AND app_creationsdate > DATE_SUB(now(), INTERVAL 12 MONTH)
GROUP BY EXTRACT(YEAR_MONTH FROM app_creationsdate)
I am not entirely sure what you are trying to accomplish but I do notice one thing:
DATE_FORMAT(a2.app_start, '%m' ) = DATE_FORMAT('month', '%m' )
This: DATE_FORMAT('month', '%m' )...evaluates to NULL so equating that with anything is never going to work (I don't think anyway, but I'm new to MySQL... :) ).

MySQL select unique months when discounts are valid

I have a MySQL database containing discounts. A simplified version looks like this:
id | start (UNIX timestamp) | end (UNIX timestamp)
45 | 1384693200 | 1398992400
68 | 1386018000 | 1386277200
263 | 1388530800 | 1391209200
A discount can last a few days, a few months, or even a few years. I'm looking for a way to select a unique list of months where (future) discounts are valid.
If there is:
a discount which starts in november 2013 and ends in april 2014
a discount which starts in december 2013 and ends in the same month
a discount which starts in january 2014 and ends one month later
a discount which starts in june 2014 and ends the same month
The output should be:
- December (2013)
- January (2014)
- February (2014)
- March (2014)
- April (2014)
- June (2014)
November 2013 is not shown because it is in the past. May 2014 is not shown because there is no discount in that month.
Can somebody help?
Thanks in advance!
Create a table containing a sequence of numbers from 0 to a number of month you could ever require, and join this table to your table.
This is example how to get a list of years+months separately for each id
SELECT id,
year( start + interval x month ) year,
month( start + interval x month ) month
FROM
numbers n
JOIN
(
SELECT id,
from_unixtime( start ) start,
from_unixtime( end ) end
FROM Table1
) q
ON n.x <= period_diff( date_format( q.end, '%Y%m' ),date_format( q.start, '%Y%m' ))
ORDER BY id, year, month ;
Demo --> http://www.sqlfiddle.com/#!9/d7cfc/4
If you want to combine years+months for all id, skip id column and use GROUP BY
SELECT year( start + interval x month ) year,
month( start + interval x month ) month
FROM
numbers n
JOIN
(
SELECT id,
from_unixtime( start ) start,
from_unixtime( end ) end
FROM Table1
) q
ON n.x <= period_diff( date_format( q.end, '%Y%m' ),date_format( q.start, '%Y%m' ))
GROUP BY year, month
ORDER BY year, month ;
If you want to skip past years and months, add WHERE year >= current year AND month >= current month, this is a trivial change. Also add another WHERE end < current-unix-time in the subquery to filter out unwanted past rows.

showing previous and current month data in table using mysql

I am trying to show three different figures of the same column In a mysql query, I would like to keep one month static: April, so it would be a case like this I want to show The current month, the previous month and the static month of the year I'm working with, in this case let us stick with 2012
Example
Tablename:payment
id , pay_date, amount
1 2012-02-12 1000
2 2012-03-11 780
3 2012-04-15 890
4 2012-05-12 1200
5 2012-06-12 1890
6 2012-07-12 1350
7 2012-08-12 1450
So what I want to do is show the column amount for the month of April as I said I want to keep that row static: 890, the current month lets say the current month is August:1450 and the previous month amount which would be July:1350: so the final result would be something like this:
april_amount current_month_amount previous_month_amount
890 1450 1350
However I'm stuck here:
select amount as april_amount
from payment
where monthname(pay_date) LIKE 'April'
and year(pay_date) LIKE 2012
I hope the question is written clear enough, and thanks alot for the help much appreciated.
If the results can be rows instead of columns:
SELECT MONTHNAME(pay_date), amount FROM payment
WHERE pay_date BETWEEN '2012-04-01'
AND '2012-04-30'
OR pay_date BETWEEN CURRENT_DATE
- INTERVAL DAYOFMONTH(CURRENT_DATE) - 1 DAY
AND LAST_DAY(CURRENT_DATE)
OR pay_date BETWEEN CURRENT_DATE
- INTERVAL DAYOFMONTH(CURRENT_DATE) - 1 DAY
- INTERVAL 1 MONTH
AND LAST_DAY(CURRENT_DATE - INTERVAL 1 MONTH)
See it on sqlfiddle.
I might be way off here. But try:
select top 1
p.amount, c.amount, n.amount
from payment c
inner join payment p ON p.pay_date < c.pay_date
inner join payment n ON n.pay_date > c.pay_date
where monthname(c.paydate) LIKE 'April'
and year(c.pay_date) LIKE 2012
order by p.pay_date DESC, n.pay_date ASC
EDIT, I didnt read your question properly. I was going for previous, current, and next month. 1 minute and I'll try again.
select top 1
p.amount AS april_amount, c.amount AS current_month_amount, n.amount AS previous_month_amount
from payment c
inner join payment p ON monthname(p.pay_date) = 'April' AND year(p.pay_date) = 2012
inner join payment n ON n.pay_date > c.pay_date
where monthname(c.paydate) = monthname(curdate())
and year(c.pay_date) = year(curdate())
order by n.pay_date ASC
This assumes there is only 1 entry per month.
Ok, so i haven't written in mysql for a while. here is what worked for your example data:
select
p.amount AS april_amount, c.amount AS current_month_amount, n.amount AS previous_month_amount
from payment AS c
inner join payment AS p ON monthname(p.pay_date) LIKE 'April' AND year(p.pay_date) LIKE 2012
inner join payment AS n ON n.pay_date < c.pay_date
where monthname(c.pay_date) LIKE monthname(curdate())
and year(c.pay_date) LIKE year(curdate())
order by n.pay_date DESC
limit 1
the previous month table joined is counterintuitively named n, but this works. I verified it in a WAMP install.
To handle aggregates per month you can use subselects. Performance may suffer on very large tables (millions of rows or more).
SELECT SUM( a.amount ) AS april_amount,
(
SELECT SUM( c.amount )
FROM payment c
WHERE MONTH( c.pay_date ) = MONTH( CURDATE( ) )
) AS current_month_amount,
(
SELECT SUM( p.amount )
FROM payment p
WHERE MONTH( p.pay_date ) = MONTH( CURDATE( ) - INTERVAL 1
MONTH )
) AS previous_month_amount
FROM payment a
WHERE MONTHNAME( a.pay_date ) = 'April'
AND YEAR( a.pay_date ) =2012

MySQL group by week

I have a large number of records with a transaction datetime field going back several years. I would like to do a comparative analysis between the same timespan this year and last. How can I group by week over a 3 month range?
I'm running into problems using the YEARWEEK and WEEK functions because of the day the year 2012 starts of versus the day 2011 starts on.
Given that I have records with datetimes everyday from Jan 1st to the current day, and records with the same datetimes from the prior year, how can I group by week so the output is sums with dates like: 01/01/2011, 01/08/2011, 01/15/2011, etc., and 01/01/2012, 01/08/2012, 01/15/2012, etc.?
My query so far is as follows:
SELECT
DATE_FORMAT(A.transaction_date, '%Y-%m-%d') as date,
ROUND(sum(A.quantity), 3) AS quantity,
ROUND(sum(A.total_amount), 3) AS amount,
A.product_code,
D.fuel_type_code,
D.fuel_type_name,
C.customer_code,
C.customer_name
FROM
cl_transactions AS A
INNER JOIN
card AS B ON A.card_number=B.card_number
INNER JOIN
customer AS C ON B.customer_code=C.customer_code
INNER JOIN
fuel_type AS D ON A.fuel_type=D.fuel_type_code
WHERE
((A.transaction_date >= DATE_FORMAT(NOW() - INTERVAL 3 MONTH, '%Y-%m-01')) OR (A.transaction_date - INTERVAL 1 YEAR >= DATE_FORMAT(NOW() - INTERVAL 15 MONTH, '%Y-%m-01') AND A.transaction_date <= NOW() - INTERVAL 1 YEAR))
GROUP BY
A.transaction_date, fuel_type_code;
I would essentially like something that achieves the following pseudo-query:
GROUP BY
STARTING FROM THE OLDEST DATE (A.transaction_date + INTERVAL 6 DAY)
I started with an inner query using sqlvariables to build out from/to ranges for this year and last year of each respective start of year/month/day (ex: 2012-01-01 and 2011-01-01 respectively). From that, I'm also pre-formatting the date for final output so you have ONE master date basis for display reflecting that of whatever the "this year" week would be.
From that, I do a join to the transaction table where the transaction date is BETWEEN the respective start of current week and start of next week. Since date/time stamps include hour minute, 2012-01-01 by itself is implied as 12:00:00am (midnight) of the day. and between will go UP TO 7 days later 12:00:00 am. And that date will become the start date of the following week.
So, by joining on the date being between EITHER last yr or this yr time period, its the same group qualification. So the field selection does a ROUND( SUM( IF() )) per respective last year or this year. if the incoming transaction date is LESS than the current year's week start, then it must be a record from prior year, otherwise its for the current year. So, respectively, add the value itself, or zero as it applies.
So now, you have the group by. The week that it qualified for was already prepared from the inner query via "ThisYearWeekOf" formatted column, regardless of the otherwise computed "YEARWEEK()" or "WEEK()". The date ranges took care of that qualification for us.
Finally, I added the fuel-type as a join and included that as the group by. You have to group by all non-aggregate columns for proper SQL, although MySQL lets you get by by just grabbing the first entry for the given group if it is NOT so specified in group by.
To close, I DID include the information for the customer as you didn't have it in the group by and did not appear to be applicable... it would just arbitrarily grab one. However, I've added it to the group by, so now your records will show at the per customer level, per product and fuel type, how much sales and quantity between this year and last.
SELECT
JustWeekRange.ThisYearWeekOf,
CTrans.product_code,
FT.fuel_type_code,
FT.fuel_type_name,
C.customer_code,
C.customer_name,
ROUND( SUM( IF( CTrans.transaction_date < JustWeekRange.ThisYrWeekStart, CTrans.Quantity, 0 )), 3) as LastYrQty,
ROUND( SUM( IF( CTrans.transaction_date < JustWeekRange.ThisYrWeekStart, CTrans.total_amount, 0 )), 3) as LastYrAmt,
ROUND( SUM( IF( CTrans.transaction_date < JustWeekRange.ThisYrWeekStart, 0, CTrans.Quantity )), 3) as ThisYrQty,
ROUND( SUM( IF( CTrans.transaction_date < JustWeekRange.ThisYrWeekStart, 0, CTrans.total_amount )), 3) as ThisYrAmt,
FROM
( SELECT
DATE_FORMAT(#ThisYearDate, '%Y-%m-%d') as ThisYearWeekOf,
#LastYearDate as LastYrWeekStart,
#ThisYearDate as ThisYrWeekStart,
#LastYearDate := date_add( #LastYearDate, interval 7 day ) LastYrStartOfNextWeek,
#ThisYearDate := date_add( #ThisYearDate, interval 7 day ) ThisYrStartOfNextWeek
FROM
(select #ThisYearDate := '2012-01-01',
#LastYearDate := '2011-01-01' ) sqlvars,
cl_transactions justForLimit
HAVING
ThisYrWeekStart < '2012-04-01'
LIMIT 15 ) JustWeekRange
JOIN cl_transactions AS CTrans
ON CTrans.transaction_date BETWEEN
JustWeekRange.LastYrWeekStart AND JustWeekRange.LastYrStartOfNextWeek
OR CTrans.transaction_date BETWEEN
JustWeekRange.ThisYrWeekStart AND JustWeekRange.ThisYrStartOfNextWeek
JOIN fuel_type FT
ON CTrans.fuel_type = FT.fuel_type_code
JOIN card
ON CTrans.card_number = card.card_number
JOIN customer AS C
ON card.customer_code = C.customer_code
GROUP BY
JustWeekRange.ThisYearWeekOf,
CTrans.product_code,
FT.fuel_type_code,
FT.fuel_type_name,
C.customer_code,
C.customer_name