How to optimize subquery - mysql

I have a query with multiple subqueries to count how many users involved in a certain transaction depends on status.
SELECT * FROM (
SELECT
DATE(changes.created_at) AS `date`,
SUM(IF(changes.status = 15, 1, 0)) AS installed_daily,
SUM(IF(changes.status = 3, 1, 0)) AS port_reserved,
(
SELECT COUNT(DISTINCT created_by) FROM applicant_state_changes
WHERE DATE(created_at) = DATE(changes.created_at) AND status = 3
) AS user_port_reserved,
SUM(IF(changes.status = 5, 1, 0)) AS document_validated,
(
SELECT COUNT(DISTINCT created_by) FROM applicant_state_changes
WHERE DATE(created_at) = DATE(changes.created_at) AND status = 5
) AS user_document_validated,
SUM(IF(changes.status = 7, 1, 0)) AS account_created,
(
SELECT COUNT(DISTINCT created_by) FROM applicant_state_changes
WHERE DATE(created_at) = DATE(changes.created_at) AND status = 7
) AS user_account_created,
SUM(IF(changes.status = 11, 1, 0)) AS jo_created,
(
SELECT COUNT(DISTINCT created_by) FROM applicant_state_changes
WHERE DATE(created_at) = DATE(changes.created_at) AND status = 11
) AS user_jo_created
FROM applicant_state_changes AS changes
GROUP BY DATE(changes.created_at)
LIMIT 100 OFFSET 0
) a
ORDER BY date ASC;
This takes around 130 secs. Without the subqueries, my query takes up to 0.5 sec only.

you can use conditional aggregation using case when expression
SELECT
DATE(changes.created_at) AS `date`,
SUM(IF(changes.status = 15, 1, 0)) AS installed_daily,
SUM(IF(changes.status = 3, 1, 0)) AS port_reserved,
count(distinct case when status = 3 then created_by end) as user_port_reserved
,
SUM(IF(changes.status = 5, 1, 0)) AS document_validated,
count(distinct case when status = 5 then created_by end) AS user_document_validated,
SUM(IF(changes.status = 7, 1, 0)) AS account_created,
count(distinct case when status = 7 then 1 end) AS user_account_created,
SUM(IF(changes.status = 11, 1, 0)) AS jo_created,
count(distinct case when status = 11 then created_by end) AS user_jo_created
FROM applicant_state_changes AS changes where
GROUP BY DATE(changes.created_at)
LIMIT 100 OFFSET 0

Related

MySql get balance of accounts

i want to calculate the balance of 3 accounts.
I have 2 tables:
accounts with id, name and start-balance
transactions with value, charge-account, type and paid
To calculate the balance i have to add the start-balance (from accounts) with alle the transaction-values where charge-account = account-id, paid = 1 and type = 1. Then i have to subtract (correct word?) all the transaction-values where charge-account = account-id, paid = 1 and type = 0
At the end, if everything would work i just want to see what balance the accounts have right now.
i tried this query but i get wrong results, it looks like it adds the start-balance multiple times...
SELECT
SUM(IF(a.id = 1, IF(t.type = 1 AND t.charge_account = 1, t.value, 0) - IF(t.type = 0 AND t.charge_account = 1, t.value, 0), 0) + a.start-balance) as "balanc_1",
SUM(IF(a.id = 2, IF(t.type = 1 AND t.charge_account = 2, t.value, 0) - IF(t.type = 0 AND t.charge_account = 2, t.value, 0), 0) + a.start-balance) as "balance_2",
SUM(IF(a.id = 3, IF(t.type = 1 AND t.charge_account = 3, t.value, 0) - IF(t.type = 0 AND t.charge_account = 3, t.value, 0), 0) + a.start-balance) as "balance_3"
FROM test.transactions t, test.accounts a
WHERE t.paid = 1;
transactions:
accounts:
how it should be like:
SELECT a.id,
MAX ( a.`start-balance` ) +
SUM ( CASE WHEN t.type = 1 then t.value
WHEN t.type = 2 then -t.value
ELSE 0
END ) as balance
FROM accounts a
JOIN transactions t
ON a.id = t.`charge-account`
WHERE a.id IN (1,2,3)
AND t.paid = 1
GROUP BY id
You need to use UNION and then group by account id
select accountid, sum(amount ) as amount from (
select accountid, startamount as amount from accounts
union
select accountid, transactionamount from transactions WHERE ....
) t
group by accountid

MySQL help making query

I need to display the consumption of products for 6 months.
My table Consumption is made as:
int ID_Conso
int ID_Product
int Quantity_Conso
int Month_Conso
int Year_COnso
there is 1 record/1 product/1 month as shown in picture below
the query I want to write for my view must show the result as :
Here is my query :
SELECT c.id_Product,
t1.conso1,
t2.conso2,
t3.conso3,
t4.conso4,
t5.conso5,
t6.conso6
FROM Consumption c,
(SELECT quantity_conso as "conso1"
FROM `consumption`
WHERE year= Year(Now()) and month = 1) t1,
(SELECT quantity_conso as "conso2"
FROM `consumption`
WHERE year= Year(Now()) and month = 2) t2,
(SELECT quantity_conso as "conso3"
FROM `consumption`
WHERE year= Year(Now()) and month = 3) t3,
(SELECT quantity_conso as "conso4"
FROM `consumption`
WHERE year= Year(Now()) and month = 4) t4,
(SELECT quantity_conso as "conso5"
FROM `consumption`
WHERE year= Year(Now()) and month = 5) t5,
(SELECT quantity_conso as "conso6"
FROM `consumption`
WHERE year= Year(Now()) and month = 6) t6
this query displays all the records for all the id_product (doesn't display null if one record is missing)
I have tried to use if exist but without success.
Use aggregation function SUM with IF, and GROUP BY on Id_Product. Try the following query:
SELECT
Id_Product,
SUM(IF(Month_Conso = 1, Quantity_Conso, 0)) AS QtyConso_Month1,
SUM(IF(Month_Conso = 2, Quantity_Conso, 0)) AS QtyConso_Month2,
SUM(IF(Month_Conso = 3, Quantity_Conso, 0)) AS QtyConso_Month3,
SUM(IF(Month_Conso = 4, Quantity_Conso, 0)) AS QtyConso_Month4,
SUM(IF(Month_Conso = 5, Quantity_Conso, 0)) AS QtyConso_Month5,
SUM(IF(Month_Conso = 6, Quantity_Conso, 0)) AS QtyConso_Month6
FROM Consumption
GROUP BY Id_Product
ORDER BY Id_Product ASC

Adding UNION to a complex SELECT in MySQL

I would like help adding a UNION to get all the months to a query. I tried adding union in a bunch of spots but just can't get it right.
SELECT MONTH(FirstPublishedToPortal) AS theMonth,
YEAR(FirstPublishedToPortal) AS theYear,
MONTHNAME(FirstPublishedToPortal) AS theMonthName,
COUNT(DISTINCT PONum) AS POCount,
SUM(unconfirmedEmailSent) AS unconfirmedEmailsSent,
( SELECT COUNT(DISTINCT PONum)
FROM POFlags
WHERE unconfirmedEmailSent = 0
AND MONTH(FirstPublishedToPortal) = theMonth
AND YEAR(FirstPublishedToPortal) = theYear
AND VendorNum = '2222'
) AS onTimeConfirmed,
SUM(lateEmailSent) AS lateEmailsSent,
( SELECT COUNT(DISTINCT PONum)
FROM POFlags
WHERE lateEmailSent = 0
AND MONTH(FirstPublishedToPortal) = theMonth
AND YEAR(FirstPublishedToPortal) = theYear
AND VendorNum = '2222'
) AS onTimePOCount
FROM POFlags
WHERE VendorNum = '2222'
AND FirstPublishedToPortal >= '2017-01-08'
GROUP BY theYear DESC, theMonth DESC
ORDER BY FirstPublishedToPortal DESC
Where do the union clauses go in this query?
I think there needs to be something like the following code but I don't understand where to put it to work correctly with the GROUP BY or ORDER BY.
(SELECT 1, null, 'January', 0, 0, 0, 0, 0)
UNION
(SELECT 2, null, 'February', 0, 0, 0, 0, 0)
UNION
(SELECT 3, null, 'March', 0, 0, 0, 0, 0)
.
.
(SELECT 12, null, 'December, 0, 0, 0, 0, 0)
SQL FIDDLE HERE
Use a left join based on a nested query with union.
Use Coalesce to apply your default values.
Move where condition on join clause
warning, 2017 is hardcoded
Something like this : http://sqlfiddle.com/#!9/458808/27
SELECT
all_month.nMonth AS theMonth,
coalesce(YEAR(FirstPublishedToPortal),2017) AS theYear,
all_month.sMonth AS theMonthName,
coalesce(COUNT(DISTINCT PONum),0) AS POCount,
coalesce(SUM(unconfirmedEmailSent),0) AS unconfirmedEmailsSent,
coalesce(( SELECT COUNT(DISTINCT PONum)
FROM POFlags
WHERE unconfirmedEmailSent = 0
AND MONTH(FirstPublishedToPortal) = theMonth
AND YEAR(FirstPublishedToPortal) = theYear
AND VendorNum = '2222'
),0) AS onTimeConfirmed,
coalesce(SUM(lateEmailSent),0) AS lateEmailsSent,
coalesce(( SELECT COUNT(DISTINCT PONum)
FROM POFlags
WHERE lateEmailSent = 0
AND MONTH(FirstPublishedToPortal) = theMonth
AND YEAR(FirstPublishedToPortal) = theYear
AND VendorNum = '2222'
),0) AS onTimePOCount
FROM (
(SELECT 1 nMonth, 'January' sMonth) UNION ALL
(SELECT 2 nMonth, 'February' sMonth ) UNION ALL
(SELECT 3 nMonth, 'March' sMonth ) UNION ALL
(SELECT 4 nMonth, 'April' sMonth ) UNION ALL
(SELECT 5 nMonth, 'May' sMonth ) UNION ALL
(SELECT 6 nMonth, 'June' sMonth ) UNION ALL
(SELECT 7 nMonth, 'July' sMonth ) UNION ALL
(SELECT 8 nMonth, 'August' sMonth ) UNION ALL
(SELECT 9 nMonth, 'September' sMonth) UNION ALL
(SELECT 10 nMonth, 'October' sMonth ) UNION ALL
(SELECT 11 nMonth, 'November' sMonth )UNION ALL
(SELECT 12 nMonth, 'December' sMonth)
) all_month left join POFlags on
MONTH(FirstPublishedToPortal)=nMonth and
VendorNum = '2222' and
FirstPublishedToPortal >= '2017-01-08'
GROUP BY
theYear DESC, nMonth DESC
ORDER BY
nMonth DESC
Another way exist using a global union between your orginal result and the default value, but this method require a second group by...

Multiple nested if statment in MySQL query

I'm trying the query below and MySQL gave me this error: Invalid use of group function
SELECT C.`some_name`,
SUM(IF(A.`med_type` = 1, SUM(A.`med_qty`), 0)) AS total,
SUM(IF(A.`is_rejected` = 4, 1 , 0)) AS approved,
SUM(IF(A.`is_rejected` = 2, 1 , 0)) AS qeue,
SUM(IF(A.`is_rejected` = 3, 1 , 0)) AS rejected,
SUM(IF(A.`is_rejected` = 1, 1 , 0)) AS fresh
FROM `ne_media` A
INNER JOIN `ne_member` B ON A.`mem_id` = B.`mem_id`
INNER JOIN `ne_some` C ON B.`some_id` = C.`some_id`
GROUP BY C.`some_id`;
I want to sum med_qty just if med_type = 1.
How do I do it?
Use:
SUM(CASE WHEN (A.`med_type` = 1) THEN A.`med_qty` ELSE 0 END)) AS total,
or:
SUM(IF(A.`med_type` = 1, A.`med_qty`, 0)) AS total,
You can't do aggregates on aggregates like you tried to in the original.

Rewriting MySQL query with subqueries to joins

I have written a fairly complex SQL query to get some statistics about animals from an animal sampling database. This query includes a number of subqueries and I would now like to see if it is possible to rewrite this query in any way to use joins instead of subqueries. I have a dim idea that this might reduce query time. (it's now about 23s on a mac mini).
Here's the query:
SELECT COUNT(DISTINCT a.AnimalID), TO_DAYS(a.VisitDate) AS day,
DATE_FORMAT(a.VisitDate, '%b %d %Y'), a.origin,
(
SELECT COUNT(DISTINCT a.AnimalID)
FROM samples AS a
JOIN
custom_animals AS b
ON a.AnimalID = b.animal_id
WHERE
b.organism = 2
AND
TO_DAYS(a.VisitDate) = day
) AS Goats,
(
SELECT COUNT(DISTINCT a.AnimalID)
FROM samples AS a
JOIN custom_animals AS b
ON a.AnimalID = b.animal_id
WHERE
b.organism = 2
AND
b.sex = 'Female'
AND
TO_DAYS(a.VisitDate) = day
) AS GF,
(
SELECT COUNT(DISTINCT a.AnimalID)
FROM samples AS a
JOIN custom_animals AS b
ON a.AnimalID = b.animal_id
WHERE
b.organism = 3
AND
b.sex = 'Female'
AND
TO_DAYS(a.VisitDate) = day
) AS SF
FROM
samples AS a
JOIN custom_animals AS b
ON a.AnimalID = b.animal_id
WHERE
project = 5
AND
AnimalID LIKE 'AVD%'
GROUP BY
TO_DAYS(a.VisitDate);
Thanks to ksogor my query is now way faster at;
SELECT DATE_FORMAT(s.VisitDate, '%b %d %Y') AS date,
s.origin,
SUM(IF(project = 5 AND s.AnimalID LIKE 'AVD%', 1, 0)) AS sampled_animals,
SUM(IF(ca.organism = 2, 1, 0)) AS sampled_goats,
SUM(IF(ca.organism = 2 AND ca.sex = 'Female', 1, 0)) AS female_goats,
SUM(IF(ca.organism = 3 AND ca.sex = 'Female', 1, 0)) AS female_sheep
FROM samples s JOIN custom_animals ca ON s.AnimalID = ca.animal_id
GROUP BY date;
I would still need to make this query select distinct s.AnimalID though as right now it counts the samples we have taken from these animals instead of the animals themselves. Anyone got any idea?
After some more help from ksogor I now have a great query:
SELECT DATE_FORMAT(s.VisitDate, '%b %d %Y') AS date,
s.origin,
SUM(IF(project = 5 AND s.AnimalID LIKE 'AVD%', 1, 0)) AS sampled_animals,
SUM(IF(ca.organism = 2, 1, 0)) AS sampled_goats,
SUM(IF(ca.organism = 2 AND ca.sex = 'Female', 1, 0)) AS female_goats,
SUM(IF(ca.organism = 3 AND ca.sex = 'Female', 1, 0)) AS female_sheep
FROM (
SELECT DISTINCT AnimalID AS AnimalID,
VisitDate,
origin,
project
FROM samples
) s
JOIN custom_animals ca ON s.AnimalID = ca.animal_id
GROUP BY date;
You can just use if or case statements, like this:
SELECT SUM(if(project = 5 AND AnimealID LIKE 'AVD%', 1, 0)) AS countbyproj,
TO_DAYS(s.VisitDate) AS day,
DATE_FORMAT(s.VisitDate, '%b %d %Y') AS date,
s.origin,
SUM(if(ca.organism = 2, 1, 0)) AS countGoats,
SUM(if(ca.organism = 2 AND ca.sex = 'Female', 1, 0)) AS countGF,
SUM(if(ca.organism = 3 AND ca.sex = 'Female', 1, 0)) AS countSF
FROM samples s JOIN custom_animals ca ON s.AnimalID = ca.animal_id
GROUP BY TO_DAYS(a.VisitDate);
I can't check query, I don't know what result you're expected and which tables/relations you have, so this is only example with idea.
If you need count unque AnimealID's for each day:
SELECT SUM(byproj) AS countbyproj,
day,
date,
origin,
SUM(Goats) AS countGoats,
SUM(GF) AS countGF,
SUM(SF) AS countSF
FROM (
SELECT s.AnimealID,
if(project = 5 AND AnimealID LIKE 'AVD%', 1, 0) AS byproj,
TO_DAYS(s.VisitDate) AS day,
DATE_FORMAT(s.VisitDate, '%b %d %Y') AS date,
s.origin,
if(ca.organism = 2, 1, 0)) AS Goats,
if(ca.organism = 2 AND ca.sex = 'Female', 1, 0) AS GF,
if(ca.organism = 3 AND ca.sex = 'Female', 1, 0) AS SF
FROM samples s JOIN custom_animals ca ON s.AnimalID = ca.animal_id
) dataset
GROUP BY dataset.day, dataset.AnimealID;