Advanced MySQL left join IFNULL Query - mysql

I am looking for help with an advanced MySQL query. My current query, shown below, works fine. I would like to add an additional field, so I don't have to create a separate query. The new field, count(TableA.Field05), should result in the total number of records from TableA.
TableA has 10 records
TableB has 100 records
SELECT count(TableB.Answer) AS unAnswered
FROM TableA
LEFT JOIN TableB
ON ( TableA.Field01 = TableB.fkField01 AND TableA.Field02 = TableB.fkField02 AND TableB.Answer = '1')
WHERE TableB.fkField03 IS NULL AND TableA.Field04 = 10
GROUP BY TableA.Field01, TableA.Field02
Result:
unAnswered = 8 this is correct
The desired result is:
unAnswered = 8
count(TableA.Field05) = 10
--
This is an example of the data and results.
SELECT count(TableB.Answer) AS unAnswered
FROM TableA LEFT JOIN TableB ON ( TableA.Field01 = TableB.fkField01 AND TableA.Field02 = TableB.fkField02 AND TableB.Answer = '1')
WHERE TableB.fkField03 IS NULL AND TableA.Field04 = 10
GROUP BY TableA.Field01, TableA.Field02
TableA
Field01 = 1, Field02 = 1, Field03 = 10
Field01 = 1, Field02 = 2, Field03 = 21
Field01 = 1, Field02 = 3, Field03 = 22
Field01 = 1, Field02 = 4, Field03 = 34
TableB
Field01 = 1, Field02 = 1, Answer = 1
Field01 = 1, Field02 = 2, Answer = 1
Field01 = 1, Field02 = 3, Answer = 1
Field01 = 2, Field02 = 1, Answer = 1
Field01 = 2, Field02 = 2, Answer = 1
Field01 = 2, Field02 = 3, Answer = 1
Result
count(TableB.Answer) AS unAnswered = 1
Result trying to achive
count(TableB.Answer) AS unAnswered = 1
count(TableA.Field03) = 4
Any help will be greatly appreciated.

If you are just looking to add the count of tableA to your current query just add it to your select statement:
'SELECT count(TableB.Answer) AS unAnswered, count(TableA.Field02) FROM TableA ...'

I just figured out my own question.
SELECT **COALESCE(count(DISTINCT TableB.Answer)) AS unAnswered, count(DISTINCT TableA.Field05)**
FROM TableA
LEFT JOIN TableB
ON ( TableA.Field01 = TableB.fkField01 AND TableA.Field02 = TableB.fkField02 AND TableB.Answer = '1')
WHERE TableB.fkField03 IS NULL AND TableA.Field04 = 10
GROUP BY TableA.Field01, TableA.Field02
Adding COALESCE and DISTINCT solved my issue. Thank you everyone for your help.

Related

2 where condition one SQL sentence

I want to fetch that SQL rows:
SELECT aa.*
FROM Answers AS aa
WHERE event_id = 1 AND
( (aa.form_item_id = 1 AND form_item_reply = "John") AND
(aa.form_item_id = 2 AND form_item_reply = "Doe")
)
ORDER BY aa.id DESC
But it's given wrong result, I want to fetch that (aa.form_item_id = 1 AND form_item_reply = "John") and (aa.form_item_id = 2 AND form_item_reply = "Doe").
It have to give me above 2 condition result.
You can use OR
SELECT aa.* FROM Answers AS aa WHERE event_id = 1 AND
(
(aa.form_item_id = 1 AND form_item_reply = "John")
OR (aa.form_item_id = 2 AND form_item_reply = "Doe")
) ORDER BY aa.id DESC
Perhaps you just want OR:
SELECT aa.*
FROM Answers AS aa
WHERE event_id = 1 AND
( (aa.form_item_id = 1 AND form_item_reply = 'John') OR
(aa.form_item_id = 2 AND form_item_reply = 'Doe')
)
ORDER BY aa.id DESC

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.

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

COUNT with case statement not working properly

Suppose I have the following ouput from my query:
{
"player_first_name": "Albano",
"player_last_name": "Aleksi",
"yellow_cards": "14",
"orange_cards": "0",
"red_cards": "1",
"points": "15",
"player_id": "286635"
}
as you can see the player have 14 yellow cards, 1 red cards and 0 orange cards.
I want calculate the "point" earned by this playern counting each card in the following way:
yellow card: 1 point
orange card: 2 point
red card: 3 point
so the final result should be: 17
I tried to count the total of the points in the following way:
$sql = $this->db->prepare("SELECT
p.first_name AS player_first_name,
p.last_name AS player_last_name,
COUNT(CASE
WHEN c.card_id = 1 THEN 1
END) AS yellow_cards,
COUNT(CASE WHEN c.card_id = 2 THEN 1 END) AS orange_cards,
COUNT(CASE
WHEN c.card_id = 3 THEN 1
END) AS red_cards,
COUNT(CASE
WHEN c.card_id = 1 THEN 1
WHEN c.card_id = 2 THEN 2
WHEN c.card_id = 3 THEN 3
END) AS points,
p.id AS player_id
FROM `match` m
INNER JOIN player_cards c ON c.match_id = m.id
INNER JOIN player p ON c.player_id = p.id
WHERE m.round_id = :round_id
GROUP BY p.id
ORDER BY points DESC, player_last_name ASC");
as you can see I have the following statement:
COUNT(CASE
WHEN c.card_id = 1 THEN 1
WHEN c.card_id = 2 THEN 2
WHEN c.card_id = 3 THEN 3
END) AS points,
the card id correspond the id of the color, so:
1: yellow card
2: orange card
3: red card
why the toal is incorrect?
UPDATE
Expected output:
{
"player_first_name": "Albano",
"player_last_name": "Aleksi",
"yellow_cards": "14",
"orange_cards": "0",
"red_cards": "1",
"points": "17",
"player_id": "286635"
}
as you can see points is 17 because we have 14 yellow cards (each yellow card is worth 1 point), and we have 1 red card, each red card is worth 3 point, so: 14 + 3 = 17. But I get 15
Replace
COUNT(CASE
WHEN c.card_id = 1 THEN 1
WHEN c.card_id = 2 THEN 2
WHEN c.card_id = 3 THEN 3
END) AS points,
with
SUM(CASE
WHEN c.card_id = 1 THEN 1
WHEN c.card_id = 2 THEN 2
WHEN c.card_id = 3 THEN 3
END) AS points,
because with count you just count while you want the sum really. If you count the numbers 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3 you get fifteen, if you add them up you get seventeen.
However, there is obviously a card table the card_id refers to. This card table should naturally contain the value of the card. So join with the cards table and use
SUM(card.value)
instead of the above.
Since you just want to sum the numerical value of the card to get the total, then just use SUM(card_id) instead of COUNT:
SELECT
p.first_name AS player_first_name,
p.last_name AS player_last_name,
COUNT(CASE WHEN c.card_id = 1 THEN 1 END) AS yellow_cards,
COUNT(CASE WHEN c.card_id = 2 THEN 1 END) AS orange_cards,
COUNT(CASE WHEN c.card_id = 3 THEN 1 END) AS red_cards,
SUM(c.card_id) AS points,
p.id AS player_id
FROM match m
INNER JOIN player_cards c
ON c.match_id = m.id
INNER JOIN player p
ON c.player_id = p.id
WHERE
m.round_id = :round_id
GROUP BY
p.id
ORDER BY
points DESC, player_last_name;
Your PHP code should then look like this:
$stmt = $connection->prepare();
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
$red = $row["red_cards"];
$yel = $row["yellow_cards"];
$orange = $row["orange_cards"];
$points = $row["points"];
}
What if you try to count the points by query the cards from the database, save it in variables and then count them all without any inline CASE statements in SQL. Make a simple query where you get each player's red, orange and yellow cards and then count them as in the example.
// The query to get the cards, name etc.
$sql = "SELECT
p.first_name AS player_first_name,
p.last_name AS player_last_name,
c.card_id = 1 THEN 1 AS yellow_cards
c.card_id = 2 THEN 2 AS orange_cards
c.card_id = 3 THEN 3 AS red_cards
p.id AS player_id
FROM `match` m
INNER JOIN player_cards c ON c.match_id = m.id
INNER JOIN player p ON c.player_id = p.id
WHERE m.round_id = round_id
GROUP BY p.id
ORDER BY points DESC, player_last_name ASC";
// Prepared statement to get the cards as variables and then count the points
$stmt = $connection->prepare();
$stmt->execute();
$result = $stmt->get_result();
while($row = $result->fetch_assoc()){
$red = $row["red_cards"];
$yel = $row["yellow_cards"];
$orange = $row["orange_cards"];
$points = $red * 3 + $orange * 2 + $yel;
}
Please note that I used prepared statements in the example and the $connection variable covers the mySQL connection to the server.
Just replace all your count with sum and retry.
As mentioned in other answers, count() counts the number of non-NULL results. Hence, 0 is as non-NULL as any other value.
But, MySQL also offers a convenient shortcut that doesn't use CASE. Boolean expressions are treated as numbers in a numeric context, with "1" for true and "0" for false. So:
SELECT p.first_name AS player_first_name, p.last_name AS player_last_name,
SUM( c.card_id = 1 ) AS yellow_cards,
SUM( c.card_id = 2 ) AS orange_cards,
SUM( c.card_id = 3 ) AS red_cards,
SUM( CASE WHEN c.card_id IN (1, 2, 3) THEN c.card_id ELSE 0 END) AS points,
p.id AS player_id
FROM `match` m INNER JOIN
player_cards c
ON c.match_id = m.id INNER JOIN
player p ON c.player_id = p.id
WHERE m.round_id = :round_id
GROUP BY p.id
ORDER BY points DESC, player_last_name ASC;
If the only allowed card_ids are 0, 1, 2, and 3, then the points calculation can be simplified to:
SUM( c.card_id ) AS points,
The simplest code is:
SUM(c.card_id) AS points,
If you don't want to depend on card_id having certian values, then this is the next simplest:
SUM((c.card_id = 1) + 2 * (c.card_id = 2) + 3 * (c.card_id = 3))
This works because in MySQL true is 1 and false is 0.

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.