mysql - sums per month and year - mysql

i have an mysql table with the following structure:
tblapp
app_id app_date app_price app_price_paid payment_id receipt_id expenses_enabled
1 01/01/2000 100 50 1 1 1
1 11/01/2000 10 20 3 2 0
1 21/01/2000 40 40 1 4 0
1 30/01/2000 30 30 2 1 1
I would like to have as columns
YEAR DETAILS Jan Feb ... Dec TOTALS
and rows
1. Price = sum app_price where expenses_enabled=1
2. PricePaid = sum app_price_paid where expenses_enabled=1
3. No = sum app_price where expenses_enabled=1 and receipt_id=1
3. Yes = sum app_price where expenses_enabled=1 and receipt_id=0
Desire output:
Year Details Jan Feb ... Dec Totals
2020 Price 130 0 ... 0 130
2020 Price_Paid 80 0 ... 0 80
2020 No 80 0 ... 0 80
2020 Yes 0 0 ... 0 0
Is it possible.

Try this one:
select
y.year,
d.details,
sum(case when month(app_date) = 1 then val else 0 end) month_01,
sum(case when month(app_date) = 2 then val else 0 end) month_02,
sum(case when month(app_date) = 3 then val else 0 end) month_03,
sum(case when month(app_date) = 4 then val else 0 end) month_04,
sum(case when month(app_date) = 5 then val else 0 end) month_05,
sum(case when month(app_date) = 6 then val else 0 end) month_06,
sum(case when month(app_date) = 7 then val else 0 end) month_07,
sum(case when month(app_date) = 8 then val else 0 end) month_08,
sum(case when month(app_date) = 9 then val else 0 end) month_09,
sum(case when month(app_date) = 10 then val else 0 end) month_10,
sum(case when month(app_date) = 11 then val else 0 end) month_11,
sum(case when month(app_date) = 12 then val else 0 end) month_12,
sum(case when month(app_date) > 0 then val else 0 end) total
FROM (
SELECT 'Price' details UNION ALL
SELECT 'PricePaid' details UNION ALL
SELECT 'No' details UNION ALL
SELECT 'Yes' details
) d cross join (
SELECT distinct year(app_date) yr
FROM tblapp
) y
left join (
select app_date, COALESCE(app_price, 0) val, 'Price' details from tblapp
WHERE expenses_enabled = 1
union all
select app_date, COALESCE(app_price_paid, 0) val, 'PricePaid' details from tblapp
WHERE expenses_enabled = 1
union all
select app_date, COALESCE(app_price, 0) val, 'No' details from tblapp
WHERE expenses_enabled = 1 and receipt_id = 1
union all
select app_date, COALESCE(app_price, 0) val, 'Yes' details from tblapp
WHERE expenses_enabled = 1 and receipt_id = 0
) t on year(t.app_date) = y.year and t.details = d.details
group by y.year, d.details
order by y.year desc;

Related

NULL values populating when trying to calculate a percentage in MYSQL

SELECT
SUM(CASE WHEN Reason = 'Forced' then 1 Else 0 end) Total_Number_Forced_Outage_Events,
COUNT(*) - 'Total_Number_Forced_Outage_Events' AS Total_Number_Outage_Events,
ROUND(('Total_Number_Forced_Outage_Events' / 'Total_Number_Outage_Events') * 100) AS Forced_Outage_Percentage,
YEAR(Start_Time) as Year
FROM AEMR
WHERE Status = 'Approved'
GROUP BY
Reason,
YEAR(Start_Time)
ORDER BY
YEAR(Start_Time),
Reason
OUTPUT
Total_Number_Forced_Outage_Events
Total_Number_Outage_Events
Forced_Outage_Percentage
Year
0
181
NULL
2016
1264
1264
NULL
2016
0
106
NULL
2016
0
380
NULL
2016
0
127
NULL
2017
1622
1622
NULL
2017
0
102
NULL
2017
0
320
NULL
2017
You can use coalesce() to replace NULL values with 0:
SELECT
SUM(CASE WHEN Reason = 'Forced' then 1 Else 0 end) Total_Number_Forced_Outage_Events,
COUNT(*) - Coalesce('Total_Number_Forced_Outage_Events',0) AS Total_Number_Outage_Events,
ROUND((Coalesce('Total_Number_Forced_Outage_Events',0) / Coalesce('Total_Number_Outage_Events',0)) * 100) AS Forced_Outage_Percentage,YEAR(Start_Time) as Year
FROM AEMR
WHERE Status = 'Approved'
GROUP BY
Reason,
YEAR(Start_Time)
ORDER BY
YEAR(Start_Time), Reason
You used column aliases in same select list I don't know which database allow it so I am changing the query:
SELECT
SUM(CASE WHEN Reason = 'Forced' then 1 Else 0 end) Total_Number_Forced_Outage_Events,
COUNT(*) - SUM(CASE WHEN Reason = 'Forced' then 1 Else 0 end) AS Total_Number_Outage_Events,
ROUND((SUM(CASE WHEN Reason = 'Forced' then 1 Else 0 end) / (COUNT(*) - SUM(CASE WHEN Reason = 'Forced' then 1 Else 0 end))) * 100,0) AS Forced_Outage_Percentage,YEAR(Start_Time) as Year
FROM AEMR
WHERE Status = 'Approved'
GROUP BY
Reason,
YEAR(Start_Time)
ORDER BY
YEAR(Start_Time), Reason
SELECT
SUM(CASE WHEN Reason = 'Forced' THEN 1 ELSE 0 END) Total_Number_Forced_Outage_Events
,Count(*) as Total_Number_Outage_Events
,CAST((CAST(SUM(CASE WHEN Reason = 'Forced' THEN 1 ELSE 0 END)AS DECIMAL(18,2))/CAST(Count(*)
AS DECIMAL(18,2)))*100 AS DECIMAL(18,2)) as Forced_Outage_Percentage
,Year(Start_Time) as Year
FROM
AEMR
WHERE Status = 'Approved'
GROUP BY
Year(Start_Time)

How to convert this MySQL query to Eloquent?

I am trying to convert a raw SQL query into eloquent and I am struggling with the join part as it gets quite complex.
I have got as far as the joins but I am looking at the documentation and struggling to work out how to convert the rest.
Here is the raw query
SELECT
SUM(CASE WHEN t2.field_2 = 1 THEN 1 ELSE 0 END) AS 'lead',
SUM(CASE WHEN t2.field_2 = 2 THEN 1 ELSE 0 END) AS 'in_review',
SUM(CASE WHEN t2.field_2 = 3 THEN 1 ELSE 0 END) AS 'pre_app_sent',
SUM(CASE WHEN t2.field_2 = 4 THEN 1 ELSE 0 END) AS 'pre_app_back',
SUM(CASE WHEN t2.field_2 = 5 THEN 1 ELSE 0 END) AS 'pre_app_ok',
SUM(CASE WHEN t2.field_2 = 6 THEN 1 ELSE 0 END) AS 'awaiting_kyc',
SUM(CASE WHEN t2.field_2 = 7 THEN 1 ELSE 0 END) AS 'kyc_complete',
SUM(CASE WHEN t2.field_2 = 8 THEN 1 ELSE 0 END) AS 'awaiting_full_app_data',
SUM(CASE WHEN t2.field_2 = 9 THEN 1 ELSE 0 END) AS 'full_app_sent',
SUM(CASE WHEN t2.field_2 = 10 THEN 1 ELSE 0 END) AS 'merchant_live'
FROM leads l
JOIN (SELECT
ae.id,
ae.model_id,
ae.fields,
TRIM(BOTH '"' FROM SUBSTRING_INDEX(SUBSTRING_INDEX(ae.fields,';', 1), ':', -1)) AS field_1,
TRIM(BOTH '"' FROM SUBSTRING_INDEX(SUBSTRING_INDEX(ae.fields,';', 2), ':', -1)) AS field_2,
ae.created_at
FROM action_events ae) t2
ON l.id = t2.model_id
WHERE t2.created_at >= '2019-01-01 00:00:00'
AND t2.created_at <= '2019-01-31 23:59:59'
And here is as far as I got with converting it to eloquent
$query->select(
DB::raw('SUM(CASE WHEN t2.field_2 = 1 THEN 1 ELSE 0 END) AS "lead"'),
DB::raw('SUM(CASE WHEN t2.field_2 = 2 THEN 1 ELSE 0 END) AS "in_review"'),
DB::raw('SUM(CASE WHEN t2.field_2 = 3 THEN 1 ELSE 0 END) AS "pre_app_sent"'),
DB::raw('SUM(CASE WHEN t2.field_2 = 4 THEN 1 ELSE 0 END) AS "pre_app_back"'),
DB::raw('SUM(CASE WHEN t2.field_2 = 5 THEN 1 ELSE 0 END) AS "pre_app_ok"'),
DB::raw('SUM(CASE WHEN t2.field_2 = 6 THEN 1 ELSE 0 END) AS "awaiting_kyc"'),
DB::raw('SUM(CASE WHEN t2.field_2 = 7 THEN 1 ELSE 0 END) AS "kyc_complete"'),
DB::raw('SUM(CASE WHEN t2.field_2 = 8 THEN 1 ELSE 0 END) AS "awaiting_full_app_data"'),
DB::raw('SUM(CASE WHEN t2.field_2 = 9 THEN 1 ELSE 0 END) AS "full_app_sent"'),
DB::raw('SUM(CASE WHEN t2.field_2 = 10 THEN 1 ELSE 0 END) AS "merchant_live"'));

Calculate date difference in sql for specific year

I have two dates, say start_date is 20141215 and end_date = 20150115. I would like to use SQL DATEDIFF to only count the dates within the year 2015 which I will specify in the query. Here is the current SQL I have written:
SELECT COUNT(leave_id),
sum(case when leave_status = 1 then 1 else 0 end) pending,
sum(case when leave_status = 2 then 1 else 0 end) declined,
sum(case when leave_status = 3 then 1 else 0 end) approved,
sum(case when leave_status = 4 then 1 else 0 end) rostered,
SUM(DATEDIFF(end_date, start_date)+1) as datetotals
FROM employee_leave WHERE
((YEAR(start_date) = :year) OR (YEAR(end_date) = :year))
AND employee_id = :emp_id
Thanks
You need to fix datediff() to only consider dates during the year. I think this does what you want:
SELECT COUNT(leave_id),
sum(case when leave_status = 1 then 1 else 0 end) pending,
sum(case when leave_status = 2 then 1 else 0 end) declined,
sum(case when leave_status = 3 then 1 else 0 end) approved,
sum(case when leave_status = 4 then 1 else 0 end) rostered,
SUM(DATEDIFF(least(end_date, date(concat_ws('-', :year, 12, 31))),
greatest(start_date, date(concat_ws('-', :year, 1, 1)))
) + 1) as datetotals
FROM employee_leave
WHERE ((YEAR(start_date) = :year) OR (YEAR(end_date) = :year)) AND
employee_id = :emp_id
Make it a AND condition rather like
WHERE
((YEAR(start_date) = :year) AND (YEAR(end_date) = :year))

how to select and group mysql data based on the following table

how can I achieve the desired result in mysql if my table looks like this.
result|year
1 |2011
2 |2011
1 |2011
0 |2011
1 |2012
2 |2012
1 = Won, 2 = lost, 0 = draw
Every year can have multiple values like this. Not sure how I can get the desired result like below.
year won lost draw totalPlayed
2011 2 1 1 3
2012 1 1 0 2
I have tried the following query but does not get the desired result
select year,
league_types.league_name,
sum(if(result = 1,1,0)) as won,
sum(if(result = 0,1,0)) as draw,
sum(if(result = 4,1,0)) as noResult,
sum(if(result = 2,1,0)) as lost,
sum(if(result = 3,1,0)) as tied,
sum(if(result > 0 and result < 4,1,0)) as played
from match_score_card
inner join fixtures on match_score_card.match_id = fixtures.match_id
inner join league_types on fixtures.league_id = league_types.league_id
where
team_id = 1 group by year order by year desc
Here is the SQL Fiddle that demonstrates the following query:
SELECT m.year,
SUM(CASE WHEN m.result = 1 THEN 1 ELSE 0 END) AS 'Won',
SUM(CASE WHEN m.result = 2 THEN 1 ELSE 0 END) AS 'Lost',
SUM(CASE WHEN m.result = 0 THEN 1 ELSE 0 END) AS 'Draw',
COUNT(*) AS 'TotalPlayed'
FROM MyTable AS m
GROUP BY m.year
I'm not familiar with that IF function in mySQL, but this standard SQL should work:
select year
, league_types.league_name
, sum(CASE WHEN result = 1 THEN 1 ELSE 0 END) as won
, sum(CASE WHEN result = 2 THEN 1 ELSE 0 END) as lost
, sum(CASE WHEN result = 3 THEN 1 ELSE 0 END) as draw
, sum(CASE WHEN result = 4 THEN 1 ELSE 0 END) as noResult
, sum(CASE WHEN result = 1
or result = 2 THEN 1 ELSE 0 END) as played
from match_score_card
inner join fixtures
on match_score_card.match_id = fixtures.match_id
inner join league_types
on fixtures.league_id = league_types.league_id
where team_id = 1
group by year, league_types.league_name
order by year desc, league_types.league_name
I'm guessing that you only want to count wins and losses as "played".

MySQL optimize multiple subquery into nested inner join?

I have 3 tables; contracts, dealers, and users.
users have many dealers and dealers have many contracts but the contracts are not directly associated with the users.
I am trying to build a report that gets me a monthly count of completed contracts grouped by user for the last 12 months.
So far I have built a multiple subquery, which is very slow: SQL Fiddle
SELECT *,
( SELECT count(*) FROM contracts
WHERE
dealer_id IN
( SELECT id FROM dealers WHERE user_id = User.id )
AND status = 'Paid'
AND completion_date BETWEEN
'2012-08-01 00:00:00' AND '2012-08-31 23:59:59'
) AS Aug_2012,
( SELECT count(*) FROM contracts
WHERE
dealer_id IN
( SELECT id FROM dealers WHERE user_id = User.id )
AND status = 'Paid'
AND completion_date BETWEEN
'2012-09-01 00:00:00' AND '2012-09-30 23:59:59'
) AS Sep_2012
FROM users AS User
WHERE
id IN( SELECT user_id FROM dealers WHERE active = 1 AND user_id IS NOT NULL GROUP BY user_id )
AND id != 1
ORDER BY User.name ASC
Instead of the subquery which selects each month I'd like to use something like this:
COUNT(*) as last_12_months,
SUM(case when MONTH(completion_date) = 8 then 1 else 0 end) as Aug_2012,
SUM(case when MONTH(completion_date) = 9 then 1 else 0 end) as Sep_2012,
etc.
Since I'd be returning multiple columns I would have to restructure it, but I'm not sure how. If I use an INNER JOIN what clause do I join on?
Here's the final query based on Mikhail's answer below:
SELECT
User.*,
SUM(case when MONTH(completion_date) = 8 then 1 else 0 end) AS Aug_2012,
SUM(case when MONTH(completion_date) = 9 then 1 else 0 end) AS Sep_2012,
SUM(case when MONTH(completion_date) = 10 then 1 else 0 end) AS Oct_2012,
SUM(case when MONTH(completion_date) = 11 then 1 else 0 end) AS Nov_2012,
SUM(case when MONTH(completion_date) = 12 then 1 else 0 end) AS Dec_2012,
SUM(case when MONTH(completion_date) = 1 then 1 else 0 end) AS Jan_2013,
SUM(case when MONTH(completion_date) = 2 then 1 else 0 end) AS Feb_2013,
SUM(case when MONTH(completion_date) = 3 then 1 else 0 end) AS Mar_2013,
SUM(case when MONTH(completion_date) = 4 then 1 else 0 end) AS Apr_2013,
SUM(case when MONTH(completion_date) = 5 then 1 else 0 end) AS May_2013,
SUM(case when MONTH(completion_date) = 6 then 1 else 0 end) AS Jun_2013,
SUM(case when MONTH(completion_date) = 7 then 1 else 0 end) AS Jul_2013,
SUM(case when completion_date BETWEEN '2012-08-01 00:00:00' AND '2013-07-31 23:59:59' then 1 else 0 end) as last_12_months
FROM users AS User
LEFT OUTER JOIN
(
SELECT id, user_id FROM dealers
WHERE active = 1 AND user_id IS NOT NULL
) AS Dealer ON User.id = Dealer.user_id
LEFT OUTER JOIN
(
SELECT completion_date, status, dealer_id FROM contracts
WHERE completion_date BETWEEN '2012-08-01 00:00:00' AND '2013-07-31 23:59:59' AND status = 'Paid' AND cancelled = 0
) AS Contract on Dealer.id = Contract.dealer_id
WHERE
User.id IN
(
SELECT user_id FROM dealers
WHERE active = 1 AND user_id IS NOT NULL
GROUP BY user_id
)
GROUP BY
User.id order by User.name asc
This is about 4 times faster.
Try this:
select
User.id, User.name,
sum(case when MONTH(completion_date) = 8 and Year(completion_date)=2012 then 1 else 0 end) as Aug_2012,
sum(case when MONTH(completion_date) = 9 and Year(completion_date)=2012 then 1 else 0 end) as Sep_2012,
sum(case when MONTH(completion_date) = 10 and Year(completion_date)=2012 then 1 else 0 end) as Oct_2012,
sum(case when MONTH(completion_date) = 11 and Year(completion_date)=2012 then 1 else 0 end) as Nov_2012,
sum(case when MONTH(completion_date) = 12 and Year(completion_date)=2012 then 1 else 0 end) as Dec_2012,
sum(case when MONTH(completion_date) = 1 and Year(completion_date)=2013 then 1 else 0 end) as Jan_2012,
sum(case when MONTH(completion_date) = 2 and Year(completion_date)=2013 then 1 else 0 end) as Feb_2012,
sum(case when MONTH(completion_date) = 3 and Year(completion_date)=2013 then 1 else 0 end) as Mar_2012,
sum(case when MONTH(completion_date) = 4 and Year(completion_date)=2013 then 1 else 0 end) as Apr_2012,
sum(case when MONTH(completion_date) = 5 and Year(completion_date)=2013 then 1 else 0 end) as May_2012,
sum(case when MONTH(completion_date) = 6 and Year(completion_date)=2013 then 1 else 0 end) as Jun_2012,
sum(case when MONTH(completion_date) = 7 and Year(completion_date)=2013 then 1 else 0 end) as Jul_2012
from users AS User
left outer join dealers on
User.id=dealers.user_id
left outer join contracts on
dealers.id=contracts.dealer_id
group by
User.id,
contracts.status
having
contracts.status='Paid'
order by
User.name asc;