Multiple nested if statment in MySQL query - mysql

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.

Related

How to improve/simplify a query with a lot of subquerys?

I have a sql query that sum and counts some registers from my db, the problem is that I'm using a lot of subquerys and all of them have the same condition, I need to find a better solution because this is causing slowness in my system.
I tried to use a simple left join but mysql returned a single row and I want that counts and sum every register on the table dbgeneralesImportacion.
I have something like this:
SELECT dbgeneralesImportacion.id,
sapito.dbgeneralesImportacion.documentosCompletos AS 'documentosCompletos',
(SELECT COUNT(c.id)
FROM sapito.dbcontenedores c
WHERE c.operacion = dbgeneralesImportacion.id ) AS 'ContadorContenedores',
(SELECT SUM(IF(c.estatus = 1, 1, 0))
FROM sapito.dbcontenedores c
WHERE c.operacion = dbgeneralesImportacion.id ) AS 'ContadorDespachos',
(SELECT SUM(IF(c.estatus = 2, 1, 0))
FROM sapito.dbcontenedores c
WHERE c.operacion = dbgeneralesImportacion.id ) AS 'ContadorCompletado',
(SELECT SUM(IF(c.desconsolidacion = 1, 1, 0))
FROM sapito.dbcontenedores c
WHERE c.operacion = dbgeneralesImportacion.id ) AS 'DesconsolidacionPuerto',
(SELECT SUM(c.bultos) FROM sapito.dbcontenedores c WHERE c.operacion = dbgeneralesImportacion.id) AS 'bultos'
FROM dbgeneralesImportacion
And my idea was to do this but counts all register and return a single row:
SELECT dbgeneralesImportacion.id AS 'id',
COUNT(c.id) AS 'ContadorContenedores',
SUM(IF(c.estatus = 1, 1, 0)) AS 'ContadorDespachos',
SUM(IF(c.estatus = 2, 1, 0)) AS 'ContadorCompletado',
SUM(IF(c.desconsolidacion = 1, 1, 0)) AS 'DesconsolidacionPuerto',
SUM(c.bultos) AS 'bultos'
FROM dbgeneralesImportacion
LEFT JOIN dbcontenedores c ON c.operacion = dbgeneralesImportacion.id
Thank you everyone
You could use a join a and group by
SELECT a.id
, a.documentosCompletos
, COUNT(c.id) ContadorContenedores
, SUM(IF(c.estatus = 1, 1, 0)) ContadorDespachos
, SUM(IF(c.estatus = 2, 1, 0)) ContadorCompletado
, SUM(IF(c.desconsolidacion = 1, 1, 0)) DesconsolidacionPuerto
, SUM(c.bultos) bultos
FROM dbgeneralesImportacion a
INNER JOIN sapito.dbcontenedores c ON c.operacion = a.id
GRUP BY a.id, a.documentosCompletos
Not positive, but couldn't you do something like this
SELECT dbgeneralesImportacion.id,
sapito.dbgeneralesImportacion.documentosCompletos AS 'documentosCompletos',
COUNT(c.id) AS 'ContadorContenedores',
SUM(IF(c.estatus = 1, 1, 0)) AS 'ContadorDespachos',
SUM(IF(c.estatus = 2, 1, 0)) AS 'ContadorCompletado',
SUM(IF(c.desconsolidacion = 1, 1, 0)) AS 'DesconsolidacionPuerto',
SUM(c.bultos) AS 'bultos'
FROM dbgeneralesImportacion, sapito.dbcontenedores c
WHERE c.operacion = dbgeneralesImportacion.id
GROUP BY dbgeneralesImportacion.id
You need a GROUP BY, but you can also simplify the query:
SELECT gi.id AS id,
COUNT(c.id) AS ContadorContenedores,
SUM( c.estatus = 1 ) AS ContadorDespachos,
SUM( c.estatus = 2 ) AS ContadorCompletado,
SUM( c.desconsolidacion = 1 ) AS DesconsolidacionPuerto,
SUM(c.bultos) AS bultos
FROM dbgeneralesImportacion LEFT JOIN
dbcontenedores c
ON c.operacion = gi.id
GROUP BY gi.id;
MySQL treats boolean values as numbers in a numeric context, with true being 1 and false being 0. This makes it easy to count up matching values.
Also, only use single quotes for string and date constants. Using them for column aliases can lead to hard-to-debug problems.

How to optimize subquery

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

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 getting and Invalid use of group function

I'm trying to count the result of tc.personnel_id if there's more than 1 result, then the output is 1. And then finally, it will get the sum. But I am getting an "Invalid use of group function" error when i execute this code. Any help will be appreciated.
SELECT tv.id,
tv.vesselName,
SUM(IF(tpi.returningCrew = 0, 1,
IF(
COUNT(tc.personnel_id) > 1, 1, 0)
)
) AS ex_crew,
SUM(IF(tpi.returningCrew = 1, 1, 0)) AS new_hire
FROM tbl_vessels AS tv
LEFT JOIN tbl_personnel_info AS tpi
ON tv.id = tpi.lastJoinedVsl
LEFT JOIN tbl_contracts AS tc
ON tpi.id = tc.personnel_id
LEFT JOIN tbl_management AS tm
ON tm.id = tv.management
WHERE tpi.emp_status = 'ON-BOARD'
AND tc.status = 'ACTIVE'
AND tv.trade_route = 'WORLD WIDE'
AND tm.management = 'TOKYO'
GROUP BY vesselName;
Aggregate functions can not be nested. So something like SUM(.. COUNT(..) ..) is not possible. You might try something like this:
SELECT tv.id,
tv.vesselName,
CASE WHEN COUNT(tc.personnel_id) > 1
THEN SUM(IF(tpi.returningCrew = 0, 1, 1)
ELSE SUM(IF(tpi.returningCrew = 0, 1, 0)
END AS ex_crew,
SUM(IF(tpi.returningCrew = 1, 1, 0)) AS new_hire
FROM [..]
SUM(IF(tpi.returningCrew = 0, 1, 1) can be rewriten to SUM(1) or to COUNT(*).
SUM(IF(tpi.returningCrew = 0, 1, 0) cann also be rewritten to SUM(tpi.returningCrew = 0)
And SUM(IF(tpi.returningCrew = 1, 1, 0)) to SUM(tpi.returningCrew = 1)
So the final select could be:
SELECT tv.id,
tv.vesselName,
CASE WHEN COUNT(tc.personnel_id) > 1
THEN COUNT(*)
ELSE SUM(tpi.returningCrew = 0)
END AS ex_crew,
SUM(tpi.returningCrew = 1) AS new_hire
FROM [..]

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;