MySql how to compare this years month to last years month? - mysql

How do I find the department numbers who have produced more than 20% more sales in them this February (as in this year so would use YEAR(CURDATE()) rather than saying "2012") compared to last February as in the year before. The result table should have only one column which contains the department numbers that have produced 20% more sales.
If the febraury last year has no sales for "department 2" but sales for this febraury, then 0 * 1.2= 0 so the results would not show "department 2". If the february last year for "department 3" sold one item, and the february this year sold two items, this would be a 1.5 increase which is more than 1.2 so the results should show "department 3". If there were only 3 departments, at most, there should be only 3 rows.
Department 2 does not appear because there was 3 sales last february and one sale this february. Department 4 does not appear because there was no sale last february so in terms of maths 0 * 1.2 = 0 so Department 4 would not appear despite there being sales this febraury.
Thank you in advance, and hope this is detailed enough.

Try using having as below:
select departement_no
from department
group by departement_no
having sum(case date_format(date, '%m%Y')
when CONCAT('02',YEAR(CURDATE())) then 1 else 0 end)
>
1.2 * sum(case date_form(date, '%m%Y')
when CONCAT('02',YEAR(DATE_SUB(CURDATE(),INTERVAL 1 Year)))
then 1 else 0 end)
AND
sum(case date_form(date, '%m%Y')
when CONCAT('02',YEAR(DATE_SUB(CURDATE(),INTERVAL 1 Year)))
then 1 else 0 end) > 0

This uses MAKEDATE() function to craete the first day of a year and then adds the appropriate month intervals to calculate the start of the (February) month and the start date of the next month.
Replace ? with 1 for January, 2 for February, etc.:
SELECT
this_year.departement_no
FROM
( SELECT departement_no, COUNT(*) AS num_sales
FROM department
WHERE date >= MAKEDATE( YEAR(NOW()), 1) + INTERVAL (?-1) MONTH
AND date < MAKEDATE( YEAR(NOW()), 1) + INTERVAL (?) MONTH
GROUP BY departement_no
) this_year
LEFT JOIN
( SELECT departement_no, COUNT(*) AS num_sales
FROM department
WHERE date >= MAKEDATE( YEAR(NOW())-1, 1) + INTERVAL (?-1) MONTH
AND date < MAKEDATE( YEAR(NOW())-1, 1) + INTERVAL (?) MONTH
GROUP BY departement_no
) last_year
ON last_year.departement_no = this_year.departement_no
WHERE
this_year.num_sales > 1.2 * COALESCE(last_year.num_sales, 0) ;
If you want (as your rather strange requirements) to not show departments that have sales this February and had no sales last year, remove the COALESCE() function. You can also change the LEFT join to INNER join:
WHERE
this_year.num_sales > 1.2 * last_year.num_sales ;

Related

MySQL calculate percentage of two other calculated sums including a group by month

Say i have 3 tables with a model like this
The result i want to have now looks like this
I want to calculate turnovers and profits made by all employees per month and compare it to the last years SAME month and calculate the difference in percentage of the profits. It should include the last 12 months with the INTERVAL function.
select
bookings.b_emp_id as "Employee",
MONTH(bookings.b_date) as Month,
#turnover1 := sum(bookings.b_turnover) as '2017-turnover',
#turnover2 := (select sum(lx.b_turnover)
from bookings as lx
where lx.b_date = date_add(bookings.b_date, INTERVAL -1 YEAR)
GROUP BY
MONTH(bookings.b_date),
YEAR(bookings.b_date),
bookings.b_emp_id
) as '2016-turnover',
sum(b_profit) as '2017-profit',
#profit1 := (select sum(lx.umsatz_fees)
from bookings as lx
where lx.b_date = date_add(bookings.b_date,INTERVAL -1 YEAR)
GROUP BY
MONTH(bookings.b_date),
YEAR(bookings.b_date),
bookings.b_emp_id
) as '2016-profit'
from bookings
where bookings.b_date > '2017-01-01'
and bookings.b_emp_id = ´SA´
GROUP BY MONTH(bookings.b_date)
order by bookings.b_date desc
Use conditional aggregation. It is not clear if you want to look at the last 12 / 24 months, or at the months of 2017 and the same months in 2016. Neither do I understand how you want to calculate a percentage. I divide this year's profits by last year's in below query. Adjust this so it meets your needs.
select
b_emp_id,
month,
turnover_this_year,
profit_this_year,
turnover_last_year,
profit_last_year,
profit_this_year / profit_last_year * 100 as diff
from
(
select
b_emp_id,
month(b_date) as month,
sum(case when year(b_date) = year(curdate()) then b_turnover end) as turnover_this_year,
sum(case when year(b_date) = year(curdate()) then b_profit end) as profit_this_year,
sum(case when year(b_date) < year(curdate()) then b_turnover end) as turnover_last_year,
sum(case when year(b_date) < year(curdate()) then b_profit end) as profit_last_year
from bookings
where year(b_date) in (year(curdate()), year(curdate()) - 1)
and month(b_date) <= month(curdate())
group by b_emp_id, month(b_date)
) figures
order by b_emp_id, month;

mysql query on case when or data between specified date

for each relationship manager display all the customer and their total orders,who ordered more than 5 times in the last week or more than 10 times in the last 14 days,there are two tables
1.orders[date,rel. manager],
2.customer[cid,cname].
I am trying like this, thanks.
select o.RelationshipManager,c.Name,count(*) total_orders
case
when o.OrderedDate >curdate() -interval 7 day then count(*)
else
o.OrderedDate >curdate() -interval 14 day then count(*)
end
from customer c
join orders o on c.customerid=o.customerid;
SELECT o.RelationshipManager, c.Name
, COUNT(*) total_orders
, COUNT(CASE WHEN o.OrderedDate > curdate() - INTERVAL 7 DAY THEN 1 ELSE NULL END) AS oneWeekCount
, COUNT(CASE WHEN o.OrderedDate > curdate() - INTERVAL 14 DAY THEN 1 ELSE NULL END) AS twoWeekCount
FROM customer AS c
JOIN orders AS o ON c.customerid=o.customerid
-- WHERE o.OrderedDate > curdate() - INTERVAL 14 DAY
GROUP BY o.RelationshipManager, c.Name
HAVING oneWeekCount > 5 OR twoWeekCount > 10
;
COUNT only counts non-null values, the WHERE is optional but changes the results (it should reduce the number of records inspected, but makes total_orders the same as twoWeekCount); the HAVING filters the results after the aggregation/counting has been performed.
In my experience, it is very rare for an aggregate function to be appropriate inside a conditional; I'm not even 100% sure there is an appropriate scenario for such use.

Sales and transaction totals by part, grouped by last 24 months

SELECT det.partNum,
SUM(det.quantity) AS Demand1,
COUNT(det.partNum) AS Call1
FROM details det
JOIN invoice inv ON det.invoice_id = inv.id
WHERE inv.invoice_date
BETWEEN '2015-11-01 00:00:00'
AND '2015-11-31 23:59:59'
GROUP BY partNum
The above sql returns all part numbers, the total number sold (Demand), and the total number of transactions the parts were involved in (Call) for the current month.
What our vendor wants is this information for every part, but also grouped for each of the past 24 months. The csv they are requesting would look like the following (if only viewing the last 3 months):
Part# | Demand1 | Demand2 | Demand3 | Call1 | Call2 | Call3
123 | 0 | 2 | 0 | 0 | 1 | 0
345 | 6 | 3 | 4 | 1 | 2 | 3
Part# 123: 0 transactions this month (Call1) 0 quantity sold (Demand1)
1 transaction last month (Call2) 2 quantity sold (Demand2).
0 transactions two months ago (Call3) 0 quantity sold (Demand3).
Part# 345: 1 transaction this month (Call1) for qty sold of 6 (Demand1)
2 transactions last month (Call2) for qty sold of 3 (Demand2)
3 transactions two months ago (Call3) for qty sold of 4 (Demand3)
Realize that they want this extended out for the past 24 months. Demand1/Call1 are always the current month.
I used the WHERE/BETWEEN statement to show where the date is coming from and to demonstrate how I can get an accurate report of the parts for the current month.
What I can't figure out how to do is to fill Demand and Call for 24 months. And this is the format that the vendor expects the data to be in. This wasn't my choice. Any help in getting this working as expected would be greatly appreciated.
Thanks
EDIT
I removed the sql-server tag. Sorry about that. This is only MySQL.
Also, I'm adding my reply from below...
Looking into DATEDIFF, TIMESTAMPDIFF, and even PERIOD_DIFF. But none actually seem to return what I need. What needs to happen is the first demand column should search for the current month, day 1 (inclusive) through the next month, day 1 (exclusive). The next demand column should search the current month - one month, day 1 (inclusive) through next month - one month, day 1 (exclusive). And each subsequent column should search the same parameters, subtracting an additional month each column. I don't think that can be accomplished with precision simply using DATEDIFF.
I hope that makes sense.
And again, thanks for any help.
If I understood your problem correctly, you can do it like this:
SELECT
det.partNum,
SUM(case when inv.invoice_date >= dateadd(month, -3, #currMonth) and inv.invoice_date < dateadd(month, -2, #currMonth) then det.quantity else 0) AS Demand1,
SUM(case when inv.invoice_date >= dateadd(month, -2, #currMonth) and inv.invoice_date < dateadd(month, -1, #currMonth) then det.quantity else 0) AS Demand2,
...
FROM details det
JOIN invoice inv ON det.invoice_id = inv.id
WHERE
inv.invoice_date >= '2015-11-01 00:00:00' AND inv.invoice_date < '2015-12-01'
GROUP BY partNum
This uses a variable that has the start date of current month to make the SQL more simple. I also changed the where clause, you should really use >= + < with dates instead of between.
This might get you started with Pivot query.
;WITH cte AS
(
SELECT det.partNum,
SUM(det.quantity) AS DemandSum,
COUNT(det.partNum) AS CallCount,
DATEDIFF(MONTH,inv.invoice_date, GETDATE()) + 1 MonthDiff
FROM details det
JOIN invoice inv ON det.invoice_id = inv.id
GROUP BY det.partNum, DATEDIFF(MONTH,inv.invoice_date, GETDATE()) + 1
)
SELECT t.partNum,
[Demand1],[Demand2],[Demand3],[Demand4],[Demand5],[Demand6],[Demand7],[Demand8],[Demand9],[Demand10],[Demand11],[Demand12],
[Demand13],[Demand14],[Demand15],[Demand16],[Demand17],[Demand18],[Demand19],[Demand20],[Demand21],[Demand22],[Demand23],[Demand24],
[Call1],[Call2],[Call3],[Call4],[Call5],[Call6],[Call7],[Call8],[Call9],[Call10],[Call11],[Call12],
[Call13],[Call14],[Call15],[Call16],[Call17],[Call18],[Call19],[Call20],[Call21],[Call22],[Call23],[Call24]
FROM (SELECT DISTINCT partNum FROM cte) t
LEFT JOIN (
SELECT * FROM (
SELECT partNum, DemandSum, CONCAT('Demand',MonthDiff) ColName FROM cte
) c PIVOT (SUM(DemandSum) FOR ColName IN ([Demand1],[Demand2],[Demand3],[Demand4],[Demand5],[Demand6],[Demand7],[Demand8],[Demand9],[Demand10],[Demand11],[Demand12],
[Demand13],[Demand14],[Demand15],[Demand16],[Demand17],[Demand18],[Demand19],[Demand20],[Demand21],[Demand22],[Demand23],[Demand24])
) p
) ds ON ds.partNum = t.partNum
LEFT JOIN (
SELECT * FROM (
SELECT partNum, CallCount, CONCAT('Call',MonthDiff) ColName FROM cte
) c PIVOT (COUNT(CallCount) FOR ColName IN ([Call1],[Call2],[Call3],[Call4],[Call5],[Call6],[Call7],[Call8],[Call9],[Call10],[Call11],[Call12],
[Call13],[Call14],[Call15],[Call16],[Call17],[Call18],[Call19],[Call20],[Call21],[Call22],[Call23],[Call24])
) p
) cc ON cc.partNum = t.partNum
if that's too confusing, you can use the CASE method. I'd do it a little different than the other answer though..
SELECT
det.partNum,
SUM(case WHEN DATEDIFF(MONTH, inv.invoice_date, GETDATE()) = 0 then det.quantity else 0 end) AS Demand1,
SUM(case WHEN DATEDIFF(MONTH, inv.invoice_date, GETDATE()) = 1 then det.quantity else 0 end) AS Demand2,
SUM(case WHEN DATEDIFF(MONTH, inv.invoice_date, GETDATE()) = 2 then det.quantity else 0 end) AS Demand3,
COUNT(case WHEN DATEDIFF(MONTH, inv.invoice_date, GETDATE()) = 0 then det.partNum end) AS Call1,
COUNT(case WHEN DATEDIFF(MONTH, inv.invoice_date, GETDATE()) = 1 then det.partNum end) AS Call2,
COUNT(case WHEN DATEDIFF(MONTH, inv.invoice_date, GETDATE()) = 2 then det.partNum end) AS Call3
FROM
details det
JOIN invoice inv ON det.invoice_id = inv.id
GROUP BY
det.partNum
you can get the full script for all 24 months here.. SQL Fiddle

How to get the count of field of last 1 week ,last 1 month ,yesterday

I want to get count of customers city wise of today ,yesterday ,last week ,last month .
I want Result like
city today yesterday lastweek lastmnth
1 23 2 12 12
I have my table structure like the following
customers
c_id city_id c_name currentdate
1 1 Rama 2015-01-30 09:43:17
2 1 kavitha 2015-04-30 09:43:17
cities
city_id city_name
1 hyd
2 Wgl
I tried it with following .
select c.c_city, (select count(cr_id) as lastmonth from customer
where currentdate > DATE(NOW() - INTERVAL 30 DAY) )) from customers as c
left join cities as ci on c.city_id = ci.city_id group by c.city_id
Similarly to this:
SELECT city_id as city,
SUM(CASE WHEN LEFT(currentdate,10) = LEFT(NOW(),10) THEN 1 ELSE 0 END) as today,
SUM(CASE WHEN LEFT(currentdate,10) = LEFT(NOW()-INTERVAL 1 DAY,10) THEN 1 ELSE 0 END) as yesterday,
SUM(CASE WHEN currentdate > NOW()-INTERVAL 7 DAY THEN 1 ELSE 0 END) as lastweek,
SUM(CASE WHEN currentdate > NOW()-INTERVAL 30 DAY THEN 1 ELSE 0 END) as lastmnth
FROM customers GROUP BY city_id
For last week and last month, I'm assuming you mean 7 days back and 30 days back. If you however need data from previous week and from previous month (which doesn't make much sense to me in this context) you may need to rewrite the intervals.

mysql if data no exist for in order dates

SELECT
EXTRACT( DAY FROM date) AS day,
EXTRACT( MONTH FROM date) AS month, who, sum ( wsd ) AS total FROM weekly
WHERE season = '08'
AND status = 1
AND who = 'NAME SURNAME'
GROUP BY day, month
day month who total
12 8 NAME SURNAME 18
I am getting totals of "wsd" column for every person in "who" column per day. If one person doesn't have records in table for a day, we can not see that name in the results as expected.
But i want to see that records too, with date and name with "0" in total column.
How can i do it with mysql only?
For this case, you can move the conditional logic from the where to the aggregation -- assuming that you have data for someone on every day:
SELECT EXTRACT( DAY FROM date) AS day, EXTRACT( MONTH FROM date) AS month, who,
sum(case when status = 1 AND who = 'NAME SURNAME' then wsd else 0 end) AS total
FROM weekly
WHERE season = '08'
GROUP BY day, month;
Did you try Coalesce or Isnull for inserting default values(in your case 0) ?