Slow MySQL queries using SUM() - mysql

I have to run two queries in my code to get my tenants balance. However, these queries are too slow.
First query, I get all the tenants and it's unit name:
SELECT t.TenantID
FROM Tenants t
JOIN Units u
ON t.UnitID = u.UnitID
Where t.Prospect = 2
AND t.PropertyID = 8
ORDER
BY CONCAT(Left(Replace(UnitName,'-',''),2),
REPEAT('0', (10-CHAR_LENGTH(UnitName))),
Right(Replace(UnitName,'-',''),
CHAR_LENGTH(Replace(UnitName,'-',''))-2
) )
It returns 500 rows
Then I get the balances in 4 conditions. This query will be inside of first query loop:
Select
SUM(CASE WHEN TransactionTypeID = 1 AND ChargeTypeID != 6 THEN TransactionAmount ELSE 0 END) AS TotalDebit,
SUM(CASE WHEN TransactionTypeID = 1 AND ChargeTypeID = 6 THEN TransactionAmount ELSE 0 END) AS HousingDebit,
SUM(CASE WHEN TransactionTypeID = 2 AND ChargeTypeID != 6 THEN TransactionAmount ELSE 0 END) AS TotalCredit,
SUM(CASE WHEN TransactionTypeID = 2 AND ChargeTypeID = 6 THEN TransactionAmount ELSE 0 END) AS HousingCredit
From TenantTransactions
Where TenantID= FirstQuery.TenantID
Am I doing the queries wrong? It's taking like 1 minute to run.

Do this in a single query with GROUP BY.
Try something like this:
SELECT t.TenantID, TotalDebit, HousingDebit, TotalCredit, HousingCredit
FROM Tenants t
JOIN Units u ON t.UnitID = u.UnitID
LEFT JOIN (
Select
TenantID,
SUM(CASE WHEN TransactionTypeID = 1 AND ChargeTypeID != 6 THEN TransactionAmount ELSE 0 END) AS TotalDebit,
SUM(CASE WHEN TransactionTypeID = 1 AND ChargeTypeID = 6 THEN TransactionAmount ELSE 0 END) AS HousingDebit,
SUM(CASE WHEN TransactionTypeID = 2 AND ChargeTypeID != 6 THEN TransactionAmount ELSE 0 END) AS TotalCredit,
SUM(CASE WHEN TransactionTypeID = 2 AND ChargeTypeID = 6 THEN TransactionAmount ELSE 0 END) AS HousingCredit
From TenantTransactions
Group By TenantID
) sums ON sums.TenantID = t.TenantID
Where t.Prospect = 2
AND t.PropertyID = 8
ORDER
BY CONCAT(Left(Replace(UnitName,'-',''),2),REPEAT('0', (10-CHAR_LENGTH(UnitName))),Right(Replace(UnitName,'-',''),CHAR_LENGTH(Replace(UnitName,'-',''))-2))
The inner query may still run for a while but it will only run once.
Try a compound covering index on TenantTransactions containing these columns: (TenantID, TransactionTypeID, ChargeTypeID, TransactionAmount) to optimize the query with the SUMs in it.
Try a compound index on Tenants with the columns (PropertyID, Prospect) in it.

Here's another way to do it with a subquery. You know, the performance problem might not be database performance, but the back and forth between your database and application server. So that is where a single query will help.
SELECT t.TenantID,
(SELECT SUM(CASE WHEN TransactionTypeID = 1 AND ChargeTypeID != 6 THEN TransactionAmount ELSE 0 END) From TenantTransactions TT WHERE TT.TenantID=t.TenantID) AS TotalDebit,
(SELECT SUM(CASE WHEN TransactionTypeID = 1 AND ChargeTypeID = 6 THEN TransactionAmount ELSE 0 END) From TenantTransactions TT WHERE TT.TenantID=t.TenantID) AS HousingDebit,
(SELECT SUM(CASE WHEN TransactionTypeID = 2 AND ChargeTypeID != 6 THEN TransactionAmount ELSE 0 END) From TenantTransactions TT WHERE TT.TenantID=t.TenantID) AS TotalCredit,
(SELECT SUM(CASE WHEN TransactionTypeID = 2 AND ChargeTypeID = 6 THEN TransactionAmount ELSE 0 END) From TenantTransactions TT WHERE TT.TenantID=t.TenantID) AS HousingCredit
FROM Tenants t
JOIN Units u
ON t.UnitID = u.UnitID
Where t.Prospect = 2
AND t.PropertyID = 8
ORDER
BY CONCAT(Left(Replace(UnitName,'-',''),2),REPEAT('0', (10-CHAR_LENGTH(UnitName))),Right(Replace(UnitName,'-',''),CHAR_LENGTH(Replace(UnitName,'-',''))-2))

Related

Need help in Merging Two query for HP ALM

I have two different query in HP ALM, but i want to merge and get it into one. I am not that good in SQL query so I am facing hard time in merging the query.
Query 1: Getting the execution Count for the tester
Select
TESTCYCL.TC_ACTUAL_TESTER as 'Tester',
sum(case when TC_Status In('Blocked','Passed','Failed','Not Completed') then 1 else 0 end) as 'Total',
sum(case when TC_Status = 'Passed' then 1 else 0 end) as 'Pass',
sum(case when TC_Status = 'Failed' then 1 else 0 end) as 'Fail',
sum(case when TC_Status = 'Blocked' then 1 else 0 end) as 'Blocked',
sum(case when TC_Status In('Not Completed','Defferred','N/A') then 1 else 0 end) as 'Others'
From TESTCYCL
Where
TESTCYCL.TC_EXEC_DATE = CAST(CURRENT_TIMESTAMP AS DATE)
And
TESTCYCL.TC_ACTUAL_TESTER in ('Username1')
Group by TC_ACTUAL_TESTER
Query 2: Getting the Defect raise by the tester
SELECT
BG_DETECTED_BY,
Sum(case when BG_Status Not in ('Closed','Defect Resolved','Rejected')then 1 else 0 end) as 'Defect Raised'
FROM BUG
Where BUG.BG_DETECTED_BY in ('username1')
AND BUG.BG_DETECTION_DATE = CAST(CURRENT_TIMESTAMP AS DATE)
Group by BG_DETECTED_BY
I have tried inner join/ Left Join but the count of the defect raised by the user is not matching
Query3 : That i have tried:
Select
TESTCYCL.TC_ACTUAL_TESTER as 'Tester',
sum(case when TC_Status In('Blocked','Passed','Failed','Not Completed') then 1 else 0 end) as 'Total',
sum(case when TC_Status = 'Passed' then 1 else 0 end) as 'Pass',
sum(case when TC_Status = 'Failed' then 1 else 0 end) as 'Fail',
sum(case when TC_Status = 'Blocked' then 1 else 0 end) as 'Blocked',
sum(case when TC_Status In('Not Completed','Defferred','N/A') then 1 else 0 end) as 'Others',
Sum(case when BG_Status Not in ('Closed','Defect Resolved','Rejected')then 1 else 0 end) as 'Defect Raised'
From TESTCYCL
Left Join BUG
on TESTCYCL.TC_ACTUAL_TESTER = BUG.BG_DETECTED_BY
AND BUG.BG_DETECTION_DATE = CAST(CURRENT_TIMESTAMP AS DATE)
Where TESTCYCL.TC_EXEC_DATE = CAST(CURRENT_TIMESTAMP AS DATE)
And TESTCYCL.TC_ACTUAL_TESTER in ('username1')
Group by TC_ACTUAL_TESTER
Out is mentioned as below:
Expected Output:
Tester Total Execution Passed Failed ... Defect Raised
A 5 3 2 10
Actual Output:
Tester Total Execution Passed Failed ... Defect Raised
A 56 3 2 45
The problem is you are doing the COUNT() over the product cartesian. instead of concatenating the result.
Right now you are doing
COUNT(A*B) instead of COUNT(A) || COUNT(B)
general example, if you have two queries
SELECT * FROM A (ex: 10 rows)
SELECT * FROM B (ex: 10 rows)
What you need is:
SELECT temp1.*, temp2.*
FROM (SELECT * FROM A) as temp1
JOIN (SELECT * FROM B) as temp2
ON temp1.ID = temp2.ID

MySql union Sum(Case())

How can I do a "union" in order to get only distinct values for "ACTIVITY" field, but with the variables started by "OUTPUT_" from "OUTPUT_TAB" appearing in the header. (Like "INPUT_" variables from "INPUT_TAB") .
It's possible ? I can not do it.
Check my Query PIC_QUERY:
SELECT
ACTIVIDADE,
SUM(CASE WHEN DATA_VAL_REGISTO = '2017-06-25' THEN 1 ELSE 0 END) INPUT_ACT_DATE_VOL,
SUM(CASE WHEN DATA_VAL_REGISTO = '2017-06-24' THEN 1 ELSE 0 END) PREV_DATE_VOL,
SUM(CASE WHEN WEEK(DATA_VAL_REGISTO,7) = '25' AND YEAR(DATA_VAL_REGISTO) = '2017' THEN 1 ELSE 0 END) INPUT_ACT_WEEK_VOL,
SUM(CASE WHEN WEEK(DATA_VAL_REGISTO,7) = '24' AND YEAR(DATA_VAL_REGISTO) = '2017' THEN 1 ELSE 0 END) INPUT_PREV_WEEK_VOL,
SUM(CASE WHEN MONTH(DATA_VAL_REGISTO) = '6' AND YEAR(DATA_VAL_REGISTO) = '2017' THEN 1 ELSE 0 END) INPUT_ACT_MONTH_VOL,
SUM(CASE WHEN MONTH(DATA_VAL_REGISTO) = '5' AND YEAR(DATA_VAL_REGISTO) = '2017' THEN 1 ELSE 0 END) INPUT_PREV_MONTH_VOL
FROM INPUT_TAB
GROUP BY ACTIVIDADE
UNION
SELECT
ACTIVIDADE,
SUM(CASE WHEN DATA_FIM_REP = '2017-06-25' THEN 1 ELSE 0 END) OUTPUT_ACT_DATE_VOL,
SUM(CASE WHEN DATA_FIM_REP = '2017-06-24' THEN 1 ELSE 0 END) OUTPUT_PREV_DATE_VOL,
SUM(CASE WHEN WEEK(DATA_FIM_REP,7) = '25' AND YEAR(DATA_FIM_REP) = '2017' THEN 1 ELSE 0 END) OUTPUT_ACT_WEEK_VOL,
SUM(CASE WHEN WEEK(DATA_FIM_REP,7) = '24' AND YEAR(DATA_FIM_REP) = '2017' THEN 1 ELSE 0 END) OUTPUT_PREV_WEEK_VOL,
SUM(CASE WHEN MONTH(DATA_FIM_REP) = '6' AND YEAR(DATA_FIM_REP) = '2017' THEN 1 ELSE 0 END) OUTPUT_ACT_MONTH_VOL,
SUM(CASE WHEN MONTH(DATA_FIM_REP) = '5' AND YEAR(DATA_FIM_REP) = '2017' THEN 1 ELSE 0 END) OUTPUT_PREV_MONTH_VOL
FROM OUTPUT_TAB
GROUP BY ACTIVIDADE
RESULT: PIC_RESULT
You don't want a union of two query results, but a join:
SELECT
actividade,
i.input_act_date_vol, i.input_prev_date_vol, i.input_act_week_vol,
i.input_prev_week_vol, i.input_act_month_vol, i.input_prev_month_vol,
o.output_act_date_vol, o.output_prev_date_vol, o.output_act_week_vol,
o.output_prev_week_vol, o.output_act_month_vol, o.output_prev_month_vol
FROM
(
SELECT
actividade,
SUM(data_val_registo = '2017-06-25') AS input_act_date_vol,
SUM(data_val_registo = '2017-06-24') AS input_prev_date_vol,
SUM(WEEK(data_val_registo, 7) = 25 AND YEAR(data_val_registo) = 2017) AS input_act_week_vol,
SUM(WEEK(data_val_registo, 7) = 24 AND YEAR(data_val_registo) = 2017) AS input_prev_week_vol,
SUM(MONTH(data_val_registo) = 6 AND YEAR(data_val_registo) = 2017) AS input_act_month_vol,
SUM(MONTH(data_val_registo) = 5 AND YEAR(data_val_registo) = 2017) AS input_prev_month_vol
FROM input_tab
GROUP BY actividade
) i
JOIN
(
SELECT
actividade,
SUM(data_fim_rep = '2017-06-25') AS output_act_date_vol,
SUM(data_fim_rep = '2017-06-24') AS output_prev_date_vol,
SUM(WEEK(data_fim_rep, 7) = 25 AND YEAR(data_fim_rep) = 2017) AS output_act_week_vol,
SUM(WEEK(data_fim_rep, 7) = 24 AND YEAR(data_fim_rep) = 2017) AS output_prev_week_vol,
SUM(MONTH(data_fim_rep) = 6 AND YEAR(data_fim_rep) = 2017) AS output_act_month_vol,
SUM(MONTH(data_fim_rep) = 5 AND YEAR(data_fim_rep) = 2017) ASoutput_prev_month_vol
FROM OUTPUT_TAB
GROUP BY actividade
) o USING (actividade);
In case there can be input without output or vice versa, you'll have to use an outer join instead. In case there can even be both, input without output and output without input, you'll have to look up how to emulate a full outer join in MySQL.
I've removed CASE WHEN .. THEN 1 ELSE 0 END, because a boolean expression results in true or false which equal to 1 and 0 in MySQL.
BTW: As this is all about 2017, you can get your queries simpler and faster by using WHERE YEAR(data_val_registo) and WHERE YEAR(data_fim_rep) = 2017 and removing the condition from the sum expressions.

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;

Counting number of Sales from two tables

I am using SUM ( CASE WHEN ) to count number of Yes and No, it work fine.
I am havin problem counting number of matching mobile number from two tables. It dont seem to be counting correctly.
There is a MobileNO field in dairy table and mobile field in the sales table
SELECT
D.Username,
SUM(CASE WHEN D.type = 'Yes' THEN 1 ELSE 0 END) as Yes,
SUM(CASE WHEN D.type = 'No' THEN 1 ELSE 0 END) as No,
SUM(CASE WHEN D.type = '' THEN 1 ELSE 0 END) as Other,
(SELECT SUM(CASE WHEN D.MobileNo = S.mobile THEN 1 ELSE 0 END) from sales as S) as Sales,
COUNT(*) as TOTAL FROM dairy as D
WHERE source = 'Company' AND UNIX_TIMESTAMP(CheckDate) >= 1293840000 AND UNIX_TIMESTAMP(CheckDate) <= 1322697600
group by D.Username order by TOTAL DESC
SELECT
D.Username,
SUM(CASE WHEN D.type = 'Yes' THEN 1 ELSE 0 END) as Yes,
SUM(CASE WHEN D.type = 'No' THEN 1 ELSE 0 END) as No,
SUM(CASE WHEN D.type = '' THEN 1 ELSE 0 END) as Other,
SUM(CASE WHEN S.mobile IS NULL THEN 0 ELSE 1 END) as Sales,
COUNT(*) as TOTAL
FROM dairy as D
LEFT JOIN (SELECT DISTINCT mobile FROM sales) as S ON D.MobileNo = S.mobile
WHERE source = 'Company' AND UNIX_TIMESTAMP(CheckDate) >= 1293840000 AND UNIX_TIMESTAMP(CheckDate) <= 1322697600
group by D.Username order by TOTAL DESC