Mysql - Help for optimizing query - mysql

I have this query:
SELECT bi.id,
bi.location,
bi.expense_group,
bi.level,
bi.is_active,
bi.type,
full_name,
( bl.bud_amount ) AS BudgetAmount,
( COALESCE(( ( bl.bud_amount * 3 ) - (
+ bal.bal_amount1 + bal.bal_amount2
+ bal.bal_amount3 ) ), 0) ) AS Difference,
( COALESCE(Round(( + bal.bal_amount1 + bal.bal_amount2
+ bal.bal_amount3 ) / 3), 0) ) AS Average,
bal.bal_amount1 AS BAL1,
bal.bal_amount2 AS BAL2,
bal.bal_amount3 AS BAL3
FROM (SELECT *
FROM budget_items bi
WHERE bi.location IS NOT NULL) AS bi
LEFT JOIN (SELECT budget_item_id,
Sum(CASE
WHEN budget_id = 21491 THEN amount
END) AS bud_amount
FROM budget_lines
GROUP BY budget_item_id) AS bl
ON bl.budget_item_id = bi.id
JOIN (SELECT budget_item_id,
Ifnull(Sum(CASE
WHEN balance_id = 12841 THEN amount
END), 0) AS bal_amount1,
Ifnull(Sum(CASE
WHEN balance_id = 18647 THEN amount
END), 0) AS bal_amount2,
Ifnull(Sum(CASE
WHEN balance_id = 18674 THEN amount
END), 0) AS bal_amount3
FROM balance_lines
GROUP BY budget_item_id) AS bal
ON bal.budget_item_id = bi.id
ORDER BY bi.location
It takes a lot of time. In the budget_lines and balance_lines tables I have more than 5,000,000 rows in each.
I also attach the EXPLAIN of the query, so you'll ne able to see the problem.
All ids in every table are indexed. Is there any column that if would be indexed spped up the query? Or maybe I need to change it.
*** LEFT JOIN is necessary because I need to get all the items from nudget_items, even if they don't exist in the balance/budget_line table.
Schema is: every budget has its budget_lines. Every balance has its balance_lines. The query is aimed to have ONE table to summarize the differences between a budget and several balances.
You can see a bigger image here: http://i.stack.imgur.com/dlF8V.png
EDIT:
After #Sebas answers:
For #sabes hunger, I put here the DESCRIBE:
budget_items
budget_lines
balance_lines

Maybe something like this; but without sample data, and indexes it's difficult to see
SELECT *
FROM budget_items bi
WHERE bi.location IS NOT NULL) AS bi
INNER JOIN --Added inner for clarity -changed order I just like my inner's before my outers.
(SELECT budget_item_id, Sum(CASE WHEN balance_id = 12841 THEN coalesce(amount,0) END), 0) AS bal_amount1,
Sum(CASE WHEN balance_id = 18647 THEN coalesce(amount,0) END), 0) AS bal_amount2,
Sum(CASE WHEN balance_id = 18674 THEN coalesce(amount,0) END), 0) AS bal_amount3
FROM balance_lines
WHERE balance_ID in (12841, 18647, 18674) --This way balance_IDs which aren't in this list don't even get evaluated
GROUP BY budget_item_id) AS bal
ON bal.budget_item_id = bi.id
LEFT JOIN
(SELECT budget_item_id, Sum(CASE WHEN budget_id = 21491 THEN coalesce(amount,0) END) AS bud_amount
FROM budget_lines
WHERE budget_Id = 21491 --Again since we only care about anything but budget_ID 21491, we can limit the results to JUST that
GROUP BY budget_item_id) AS bl
ON bl.budget_item_id = bi.id

Related

MySQL - Slow Query when adding multiple derived tables - Optimization

For my query, the two derived tables at the bottom are causing a crazy slow up for this query. The query, as is, takes about 45-55 seconds to execute.. NOW, when i remove just one of those derived tables (it does not matter which one) the query goes down to 0.1 - 0.3 seconds. My questions; Is there an issue with having multiple derived tables? Is there a better way to execute this? My indexes all seem to be correct, I will also include the explain from this query.
select t.name as team, u.name as "REP NAME",
count(distinct activity.id) as "TOTAL VISITS",
count(distinct activity.account_id) as "UNIQUE VISITS",
count(distinct placement.id) as "COMMITMENTS ADDED",
CASE WHEN
count(distinct activity.account_id) = 0 THEN (count(distinct
placement.id) / 1)
else (cast(count(distinct placement.id) as decimal(10,2)) /
cast(count(distinct activity.account_id) as decimal(10,2)))
end as "UNIQUE VISIT TO COMMITMENT %",
case when o.mode='basic' then count(distinct placement.id) else
count(distinct(case when placement.commitmentstatus='fullfilled'
then placement.id else 0 end))
end as "COMMITMENTS FULFILLED",
case when o.mode='basic' then 1 else
(CASE WHEN
count(distinct placement.id) = 0 THEN (count(distinct(case when
placement.commitmentstatus='fullfilled' then placement.id else 0
end)) / 1)
else (cast(count(distinct(case when
placement.commitmentstatus='fullfilled' then placement.id else 0
end)) as decimal(10,2)) / cast(count(distinct placement.id) as
decimal(10,2)))
end) end as "COMMITMENT TO FULFILLMENT %"
from lpmysqldb.users u
left join lpmysqldb.teams t on t.team_id=u.team_id
left join lpmysqldb.organizations o on o.id=t.org_id
left join (select * from lpmysqldb.activity where
org_id='555b918ae4b07b6ac5050852' and completed_at>='2018-05-01' and
completed_at<='2018-06-01' and tag='visit' and accountname is not
null and (status='active' or status='true' or status='1')) as
activity on activity.user_id=u.id
left join (select * from lpmysqldb.placements where
orgid='555b918ae4b07b6ac5050852' and placementdate>='2018-05-01' and
placementdate<='2018-06-01' and (status IN ('1','active','true') or
status is null)) as placement on placement.userid=u.id
where u.org_id='555b918ae4b07b6ac5050852'
and (u.status='active' or u.status='true' or u.status='1')
and istestuser!='1'
group by u.org_id, t.name, u.id, u.name, o.mode
order by count(distinct activity.id) desc
Thank you for assistance!
I have edited below with changing the two bottom joins from joining on subqueries to joining on the table directly. Still yielding the same result.
This is a SLIGHTLY restructured query of your same. Might be simplified as the last two subqueries are all pre-aggregated for your respective counts and count distincts so you can use those column names directly instead of showing all the count( distinct ) embedded throughout the query.
I also tried to simplify the division by multiplying a given count by 1.00 to force decimal-based precision as result.
select
t.name as team,
u.name as "REP NAME",
Activity.DistIdCnt as "TOTAL VISITS",
Activity.UniqAccountCnt as "UNIQUE VISITS",
Placement.DistIdCnt as "COMMITMENTS ADDED",
Placement.DistIdCnt /
CASE WHEN Activity.UniqAccountCnt = 0
THEN 1.00
ELSE Activity.UniqAccountCnt * 1.00
end as "UNIQUE VISIT TO COMMITMENT %",
case when o.mode = 'basic'
then Placement.DistIdCnt
else Placement.DistFulfillCnt
end as "COMMITMENTS FULFILLED",
case when o.mode = 'basic'
then 1
else ( Placement.DistFulfillCnt /
CASE when Placement.DistIdCnt = 0
then 1.00
ELSE Placement.DistIdCnt * 1.00
END TRANSACTION )
END as "COMMITMENT TO FULFILLMENT %"
from
lpmysqldb.users u
left join lpmysqldb.teams t
on u.team_id = t.team_id
left join lpmysqldb.organizations o
on t.org_id = o.id
left join
( select
user_id,
count(*) as AllRecs,
count( distinct id ) DistIdCnt,
count( distinct account_id) as UniqAccountCnt
from
lpmysqldb.activity
where
org_id = '555b918ae4b07b6ac5050852'
and completed_at>='2018-05-01'
and completed_at<='2018-06-01'
and tag='visit'
and accountname is not null
and status IN ( '1', 'active', 'true')
group by
user_id ) activity
on u.id = activity.user_id
left join
( select
userid,
count(*) AllRecs,
count(distinct id) as DistIdCnt,
count(distinct( case when commitmentstatus = 'fullfilled'
then id
else 0 end )) DistFulfillCnt
from
lpmysqldb.placements
where
orgid = '555b918ae4b07b6ac5050852'
and placementdate >= '2018-05-01'
and placementdate <= '2018-06-01'
and ( status is null OR status IN ('1','active','true')
group by
userid ) as placement
on u.id = placement.userid
where
u.org_id = '555b918ae4b07b6ac5050852'
and u.status IN ( 'active', 'true', '1')
and istestuser != '1'
group by
u.org_id,
t.name,
u.id,
u.name,
o.mode
order by
activity.DistIdCnt desc
FINALLY, your inner queries are querying for ALL users. If you have a large count of users that are NOT active, you MIGHT exclude those users from each inner query by adding those join/criteria there too such as...
( ...
from
lpmysqldb.placements
JOIN lpmysqldb.users u2
on placements.userid = u2.id
and u2.status IN ( 'active', 'true', '1')
and u2.istestuser != '1'
where … ) as placement

CASE statement in SQL giving not proper values

I am having abnormal values when I run this part in my sql code. SQL syntax wise, everything is okay with this?
select
COUNT(CASE WHEN bt.idBillingStatus = 2
THEN 1
ELSE NULL END) AS successfulbillinghits,
SUM(CASE WHEN bt.idBillingStatus = 2
THEN price
ELSE 0.0 END)
AS old_revenue
from table
Overall Query is this. The result of successfulbillinghits should be equal to timesbilled
SELECT
cs.idCustomerSubscription,
cs.msisdn,
pro.name AS promoterName,
c.name AS ClubName,
c.idClub AS ClubID,
o.name AS operatorName,
o.idOperator AS OperatorID,
co.name AS country,
-- cu.customerSince AS CustomerSince,
cs.subscribeddate AS subscribeddate,
-- cs.subscriptionNotificationSent AS SubNotificationSent,
-- cs.eventId AS EventId,
cs.unsubscribeddate AS unsubscribeddate,
cs.firstBillingDate AS FirstBillingDate,
cs.lastBilledDate As LastBilledDate,
cs.lastAttemptDate AS LastAttemptDate,
-- smp.code AS packageName,
-- o.mfactor AS mmfactor,
-- cs.idSubscriptionSource AS SubscriptionChannel,
-- cs.idUnsubscribeSource AS UnsubscriptionChannel,
-- DATE(bt.creationDate) AS BillingCreationDate,
-- bt.price AS pricePerBilling,
-- cs.lastRetryDate As LastRetryDate,
-- cs.lastRenewalDate AS LastRenewalDate,
-- cs.isActive AS ActiveStatus,
-- COUNT(bt.idBillingTransaction) AS BillingAttempts,
curr.idcurreny_symbol AS CurrencyID,
curr.symbol AS currency,
date(bt.creationDate) AS BillingDate,
cs.lastBilledAmount As LastBilledAmount,
cs.timesbilled,
price,
-- sum(price),
-- revenueShareAmountLocal,
-- o.mfactor,
-- count(IFF (bt.idBillingStatus = 2,1,0)) as otherversion,
count(CASE WHEN bt.idBillingStatus = 2
THEN 1
ELSE 0 END) AS successfulbillinghits,
SUM(CASE WHEN bt.idBillingStatus = 2
THEN price
ELSE 0.0 END)
AS old_revenue
FROM
customersubscription cs
LEFT JOIN
billing_transaction bt
ON CONVERT(cs.msisdn USING latin1) = bt.msisdn
AND cs.idClub = bt.idClub
AND bt.creationDate BETWEEN cs.SubscribedDate AND COALESCE(cs.UnsubscribedDate, now())
INNER JOIN customer cu ON (cs.idCustomer = cu.idcustomer)
INNER JOIN operator o ON (o.idoperator = cu.idoperator)
INNER JOIN country co ON (co.`idCountry` = o.idCountry)
INNER JOIN curreny_symbol curr ON (curr.idcurreny_symbol = co.idCurrencySymbol)
LEFT JOIN Promoter pro ON cs.idPromoter = pro.id
INNER JOIN club_operator_relationships cor ON cor.clubId = cs.idClub
INNER JOIN club c ON c.idClub = cs.idClub
-- INNER JOIN operator op ON op.idOperator = cu.idOperator
WHERE
-- (cs.timesbilled > 0 and cs.subscribeddate < '2016-09-01 00:00:00' )
cs.subscribeddate between '2017-04-20 00:00:00' and '2017-04-21 00:00:00'
AND cs.idClub IN (39)
GROUP BY idCustomerSubscription, ClubName, operatorName, promoterName
Successfulbillinghits is much greater than timesbilled in the result
Instead of COUNTuse SUM, as count counts blanks or nulls also
select
SUM(CASE WHEN bt.idBillingStatus = 2
THEN 1
ELSE 0 END) AS successfulbillinghits,
SUM(CASE WHEN bt.idBillingStatus = 2
THEN price
ELSE 0.0 END)
AS old_revenue
from table
Instead of using CASE, you can use WHERE clause with these aggregate functions, e.g.:
SELECT COUNT(*) as `successfulbillinghits`, SUM(price) as `old_revenue`
FROM table bt
WHERE bt.idBillingStatus = 2;

How to use user variable as counter with inner join queries that contains GROUP BY statement?

I have 2 tables odds and matches :
matches : has match_id and match_date
odds : has id, timestamp, result, odd_value, user_id, match_id
I had a query that get the following information from those tables for each user:
winnings : the winning bets for each user. (when odds.result = 1)
loses : the lost bets for each user.(when odds.result != 1)
points : the points of each user.(the sum of the odds.odd_value) for each user.
bonus : for each continuous 5 winnings i want to add extra bonus to this variable. (for each user)
How to calculate bonus?
I tried to use this query and I faced a problem : (you can check it here SQL Fiddle)
the calculated bonus are not right for all the users :
first user:(winnings:13, bonus=2).
second user:(winnings:8, bonus=2)bonus here should be 1.
third user:(winnings:14, bonus=3)bonus here should be 2.
why does the query not calculate the bonus correctly?
select d.user_id,
sum(case when d.result = 1 then 1 else 0 end) as winnings,
sum(case when d.result = 2 then 1 else 0 end) as loses,
sum(case when d.result = 1 then d.odd_value else 0 end) as points,
f.bonus
FROM odds d
INNER JOIN
(
SELECT
user_id,SUM(CASE WHEN F1=5 THEN 1 ELSE 0 END) AS bonus
FROM
(
SELECT
user_id,
CASE WHEN result=1 and #counter<5 THEN #counter:=#counter+1 WHEN result=1 and #counter=5 THEN #counter:=1 ELSE #counter:=0 END AS F1
FROM odds o
cross join (SELECT #counter:=0) AS t
INNER JOIN matches mc on mc.match_id = o.match_id
WHERE MONTH(STR_TO_DATE(mc.match_date, '%Y-%m-%d')) = 2 AND
YEAR(STR_TO_DATE(mc.match_date, '%Y-%m-%d')) = 2015 AND
(YEAR(o.timestamp)=2015 AND MONTH(o.timestamp) = 02)
) Temp
group by user_id
)as f on f.user_id = d.user_id
group by d.user_id
I am not sure how your result related to matches table,
you can add back WHERE / INNER JOIN clause if you need.
Here is link to fiddle
and the last iteration according to your comments:
And here is a query:
SET #user:=0;
select d.user_id,
sum(case when d.result = 1 then 1 else 0 end) as winnings,
sum(case when d.result = 2 then 1 else 0 end) as loses,
sum(case when d.result = 1 then d.odd_value else 0 end) as points,
f.bonus
FROM odds d
INNER JOIN
(
SELECT
user_id,SUM(bonus) AS bonus
FROM
(
SELECT
user_id,
CASE WHEN result=1 and #counter<5 AND #user=user_id THEN #counter:=#counter+1
WHEN result=1 and #counter=5 AND #user=user_id THEN #counter:=1
WHEN result=1 and #user<>user_id THEN #counter:=1
ELSE
#counter:=0
END AS F1,
#user:=user_id,
CASE WHEN #counter=5 THEN 1 ELSE 0 END AS bonus
FROM odds o
ORDER BY user_id , match_id
) Temp
group by user_id
)as f on f.user_id = d.user_id
group by d.user_id

How to perform multiple calculations with in a single query

I have a situation where in i have to get the data from an Year Ago , Previous Month and Current Month. What is the best way to achieve this ?
I have a table which contains the year,month and data in it. In the below query have added a filter
c.ReportMonth = DATENAME(month, #12MonthsAgo) and c.ReportYear = Year(#12MonthsAgo)
This is for an year ago. In the same way if i have to get the previous month and current month, can i do that with in the same query by setting the filters ? how do we do that ?
Is there a better way other than i end up writing 3 select queries and then putting the select to a tmp table and later merging the tables ?
create table #TPTABLE
(
KPIName varchar(150)
,MetricName Varchar(200)
,MetricId INT
,DataSource varchar(50)
,[AnYearAgo] Float
,[PreviousMonth] float
,[CurrentMonth] float
);
insert into #TPTABLE
(KPIName,MetricName,MetricId,DataSource,[AnYearAgo])
SELECT
p.KPIName
,p.MetricName
,p.MetricId
,p.DataSource
,c.Value as [AnYearAgo]
FROM [IntegratedCare].[report].[KPIMetricDetails] p
LEFT JOIN [IntegratedCare].[report].[KPIMectricValues] c
ON p.[MetricId] = c.MetricId
WHERE c.ReportMonth = DATENAME(month, #12MonthsAgo) and c.ReportYear = Year(#12MonthsAgo)
ORDER BY KPI_Id ASC, [MetricId] ASC
SELECT
p.KPIName
,p.MetricName
,p.MetricId
,p.DataSource
,c.Value
,c2.Value
,c3.Value
FROM [IntegratedCare].[report].[KPIMetricDetails] p
LEFT JOIN [IntegratedCare].[report].[KPIMectricValues] c
ON p.[MetricId] = c.MetricId
AND c.[CommissionerCode] = COALESCE(NULLIF(#Commissioner, ''), c.[CommissionerCode])
ANd ReportMonth = DATENAME(month, #12MonthsAgo) and c.ReportYear = Year(#12MonthsAgo)
LEFT JOIN [IntegratedCare].[report].[KPIMectricValues] c2
ON p.[MetricId] = c2.MetricId
AND c2.[CommissionerCode] = COALESCE(NULLIF(#Commissioner, ''), c2.[CommissionerCode])
ANd c2.ReportMonth = DATENAME(month, #PreviousMonth) and c2.ReportYear = Year(#PreviousMonth)
LEFT JOIN [IntegratedCare].[report].[KPIMectricValues] c3
ON p.[MetricId] = c3.MetricId
AND c3.[CommissionerCode] = COALESCE(NULLIF(#Commissioner, ''), c3.[CommissionerCode])
ANd c3.ReportMonth = DATENAME(month, #PreviousMonth) and c3.ReportYear = Year(#PreviousMonth)
ORDER BY p.KPI_Id ASC, p.[MetricId] ASC
I think what you need is this:
insert into #TPTABLE
(KPIName,MetricName,MetricId,DataSource,[AnYearAgo])
SELECT
KPIName
,MetricName
,MetricId
,DataSource
,[AnYearAgo]
,[PreviousMonth]
,[CurrentMonth]
FROM (
SELECT
KPIName
,MetricName
,MetricId
,DataSource
,KPI_Id
,sum(case when c.ReportMonth = DATENAME(month, #12MonthsAgo) and c.ReportYear = Year(#12MonthsAgo) then c.Value else 0 end) as [AnYearAgo]
,sum(case when c.ReportMonth = DATENAME(month, #PreviousMonth) and c.ReportYear = Year(#PreviousMonth) then c.Value else 0 end) as [PreviousMonth]
,sum(case when c.ReportMonth = DATENAME(month, #CurrentMonth) and c.ReportYear = Year(#CurrentMonth) then c.Value else 0 end) as [CurrentMonth]
FROM [IntegratedCare].[report].[KPIMetricDetails] p
LEFT JOIN [IntegratedCare].[report].[KPIMectricValues] c
ON p.[MetricId] = c.MetricId
GROUP BY KPIName, MetricName, MetricId, DataSource, KPI_Id
ORDER BY KPI_Id ASC, [MetricId] ASC

MySQL LEFT JOIN query is very slow

SELECT bi.id,
bi.location,
bi.expense_group,
bi.level,
bi.is_active,
bi.type,
full_name,
Sum(DISTINCT bl.amount) AS
BudgetAmount,
Sum(DISTINCT Ifnull(( bl.amount * 6 ) - ( + bal1.amount + bal2.amount +
bal3.amount
+ bal4.amount + bal5.amount +
bal6.amount ), 0)) AS
Difference,
Sum(DISTINCT Ifnull(Round(( + bal1.amount + bal2.amount + bal3.amount
+ bal4.amount + bal5.amount + bal6.amount ) /
6), 0)
) AS Average,
Sum(DISTINCT bal1.amount) AS BAL1,
Sum(DISTINCT bal2.amount) AS BAL2,
Sum(DISTINCT bal3.amount) AS BAL3,
Sum(DISTINCT bal4.amount) AS BAL4,
Sum(DISTINCT bal5.amount) AS BAL5,
Sum(DISTINCT bal6.amount) AS BAL6
FROM (SELECT *
FROM budget_items
WHERE bi.location IS NOT NULL) AS bi
LEFT JOIN budget_lines AS bl
ON bi.id = bl.budget_item_id
AND bl.budget_id = 5983
LEFT JOIN balance_lines AS bal1
ON bi.id = bal1.budget_item_id
AND bal1.balance_id = 28839
LEFT JOIN balance_lines AS bal2
ON bi.id = bal2.budget_item_id
AND bal2.balance_id = 28633
LEFT JOIN balance_lines AS bal3
ON bi.id = bal3.budget_item_id
AND bal3.balance_id = 26664
LEFT JOIN balance_lines AS bal4
ON bi.id = bal4.budget_item_id
AND bal4.balance_id = 14500
LEFT JOIN balance_lines AS bal5
ON bi.id = bal5.budget_item_id
AND bal5.balance_id = 10199
LEFT JOIN balance_lines AS bal6
ON bi.id = bal6.budget_item_id
AND bal6.balance_id = 7204
GROUP BY bi.id
ORDER BY bi.position
As you can see actually there are only 3 tables, but I need to query one of them for each of the balances.
This is taking a lot of time. Where is my mistake?
After struggling with #Gordon Linoff post, I tried to fix it myself. #Gordon Linoff is that what you meant?
SELECT bi.id,
bi.location,
bi.expense_group,
bi.level,
bi.is_active,
bi.type,
full_name,
(bl.bud_amount) AS BudgetAmount,
(coalesce(( bl.bud_amount * 6 ) - (bal.bal_amount1 + bal.bal_amount2 + bal.bal_amount3 + bal.bal_amount4 + bal.bal_amount5 + bal.bal_amount6),
0)) AS Difference,
(coalesce(Round(( bal.bal_amount1 + bal.bal_amount2 + bal.bal_amount3 + bal.bal_amount4 + bal.bal_amount5 + bal.bal_amount6 ) /
6), 0)
) AS Average,
bal.bal_amount1 AS BAL1,
bal.bal_amount2 AS BAL2,
bal.bal_amount3 AS BAL3,
bal.bal_amount4 AS BAL4,
bal.bal_amount5 AS BAL5,
bal.bal_amount6 AS BAL6
FROM (SELECT *
FROM budget_items bi
WHERE bi.location IS NOT NULL
) AS bi
LEFT JOIN
(select budget_item_id, Sum(case when budget_id = 5983 then amount end) AS bud_amount
from budget_lines
group by budget_item_id
) AS bl
on bl.budget_item_id = bi.id
JOIN
(select budget_item_id,
IFNULL(Sum(case when balance_id = 28839 then amount end), 0) AS bal_amount1,
IFNULL(Sum(case when balance_id = 28633 then amount end), 0) AS bal_amount2,
IFNULL(Sum(case when balance_id = 26664 then amount end), 0) AS bal_amount3,
IFNULL(Sum(case when balance_id = 14500 then amount end), 0) AS bal_amount4,
IFNULL(Sum(case when balance_id = 10199 then amount end), 0) AS bal_amount5,
IFNULL(Sum(case when balance_id = 7204 then amount end), 0) AS bal_amount6
from balance_lines
group by budget_item_id
) AS bal
on bal.budget_item_id = bi.id
ORDER BY bi.location
Whenever you have sum(distinct . . . ) you have a problem. You are getting cartesian products on the left outer join, and your overall results will not be accurate if there are two lines that have the same amount.
I think you want to replace all those left outer join with a subquery like this:
select budget_item_id,
Sum(case when budget_id = 5983 then amount end) AS BAL,
Sum(case when budget_id = 28839 then amount end) AS BAL1,
Sum(case when budget_id = 28633 then amount end) AS BAL2,
Sum(case when budget_id = 26664 then amount end) AS BAL3,
Sum(case when budget_id = 14500 then amount end) AS BAL4,
Sum(case when budget_id = 10199 then amount end) AS BAL5,
Sum(case when budget_id = 7204 then amount end) AS BAL6
from balance_lines
group by budget_item_id
and then fix the rest of the query.
Here is an attempt to write the query. This undoubtedly has syntax errors:
SELECT bi.id,
bi.location,
bi.expense_group,
bi.level,
bi.is_active,
bi.type,
full_name,
(bl.bal) AS BudgetAmount,
(coalesce(( bl.bal * 6 ) - (bl.bal1 + bl.bal2 + bl.bal3 + bl.bal4 + bl.bal5 + bl.bal6),
0)) AS Difference,
(coalesce(Round(( bl.bal1 + bl.bal2 + bl.bal3 + bl.bal4 + bl.bal5 + bl.bal6 ) /
6), 0)
) AS Average,
bl.bal1 AS BAL1,
bl.bal2 AS BAL2,
bl.bal3 AS BAL3,
bl.bal4 AS BAL4,
bl.bal5 AS BAL5,
bl.bal6 AS BAL6
FROM (SELECT *
FROM budget_items bi
WHERE bi.location IS NOT NULL
) bi left outer join
(select budget_item_id,
Sum(case when budget_id = 5983 then amount end) AS BAL,
Sum(case when budget_id = 28839 then amount end) AS BAL1,
Sum(case when budget_id = 28633 then amount end) AS BAL2,
Sum(case when budget_id = 26664 then amount end) AS BAL3,
Sum(case when budget_id = 14500 then amount end) AS BAL4,
Sum(case when budget_id = 10199 then amount end) AS BAL5,
Sum(case when budget_id = 7204 then amount end) AS BAL6
from balance_lines
group by budget_item_id
) bal
on bal.budget_item_id = bi.id
ORDER BY bi.position
Note that I entirely removed the outer group by, assuming that bi.id is a unique key on the budget items table.