MySQL Division From Same Column with Different Keys - mysql

I have a table with 5 fields
id = this is autoincrementing field
form_id = this field is the form being filled out
entry_id = this is the identifier of the entry
meta_key = this is the identifier of the specific form fields
meta_value = this is the value entered into the form field
If I do a:
SELECT SUM(meta_value) FROM tbl WHERE meta_key = 55;
I get the sum of all the people who like apples.
If I do a:
SELECT SUM(meta_value) FROM tbl WHERE meta_key = 75;
I get the sum of all the people total.
I am trying to write one query that will give me the % of people who like apple so in essence ((RESULT of Q1) / (RESULT of Q2)) * 100
So far I've looked into self joins and subqueries but I'm essentially stuck now because all the tutorials on subqueries focus on the WHERE clause.

Simplest thing I can think of:
SELECT
(SELECT SUM(meta_value) FROM tbl WHERE meta_key = 55) /
(SELECT SUM(meta_value) FROM tbl WHERE meta_key = 75) * 100
as percentage;
Would that work for you?

You can try this query, it avoid any possibility of division by zero, just in case total = 0.
SELECT
CASE
WHEN b.sum_b IS NULL OR b.sum_b = 0 THEN 0
ELSE (a.sum_a / b.sum_b) * 100
END as percent
FROM (
SELECT SUM(meta_value) as sum_a FROM tbl WHERE meta_key = 55
) a,
(
SELECT SUM(meta_value) as sum_b FROM tbl WHERE meta_key = 75
) b

You can use conditional aggregation
SELECT (SUM(CASE WHEN meta_key = 55 THEN meta_value ELSE 0 END) /
SUM(CASE WHEN meta_key = 75 THEN meta_value ELSE 0 END)) * 100 -- be careful you may got divide by zero error
FROM tbl
WHERE meta_key IN (55, 75);
However, this would produce integer division result. So, you can include 1.0 before division if you want exact result.

To avoid division by zero, I would simply recommend:
SELECT (SUM(CASE WHEN meta_key = 55 THEN meta_value ELSE 0 END) /
SUM(CASE WHEN meta_key = 75 THEN meta_value END)
) * 100
FROM tbl
WHERE meta_key IN (55, 75);
Note that the second CASE has no ELSE. If there are no matches, then this returns NULL, avoiding the divide-by-zero.
That said, MySQL does not return an error for divide-by-zero. It simply returns NULL. But that is highly database-specific behavior. I think every other database returns an error and you should be conscious of such potential issues.

Related

how to get the desired result based on some certain condition in sql using case?

I have a table which traces the users records I want to know which are the complete and process users's records based on their status
Here is the sql query
SELECT users.UserID,users.UserName,users.FirstName,users.LastName,users.Email,
CASE WHEN inword.inword_status = '3' THEN count(*) END As 'Process' ,
CASE WHEN inword.inword_status = '4' THEN count(*) END AS 'Complete'
FROM tbl_user users
INNER JOIN tbl_inword inword on users.UserID=inword.UserID
Where inword.Status=1 and users.Status=1 and
inword.CreatedDate BETWEEN '2020-10-01' and '2020-10-31' and inword.inword_status in (3)
group by users.UserID
Here is Query Output
My Expected result is
UserID Name Total Process Complete
1 Umair 1 1 0
1 Basit 20 20 0
1 Zaidi 34 32 2
Any Help would be Appreciated
You're not doing your conditional aggregation correctly, you should use something like:
COUNT(CASE WHEN inword.inword_status = '3' THEN inword.UserId END) As 'Process' ,
COUNT(CASE WHEN inword.inword_status = '4' THEN inword.UserId END) AS 'Complete'
Or you can take advantage of MySQL treating booleans as 1 or 0 in a numeric context and simplify to:
SUM(inword.inword_status = '3') As 'Process' ,
SUM(inword.inword_status = '4') AS 'Complete'

COUNT in CASE statement?

I would like to categorize records based on the count of one field.
My code is :
select `posts`.`post_customer_fs_id` AS `post_customer_fs_id`,count(`post_id`) AS `open_post_count`,
CASE count(`post_id`)
WHEN count(`post_id`)=1 THEN 'A'
WHEN count(`post_id`)>1 THEN 'B'
END AS AAA
from `posts` where (`posts`.`post_status` = '3') AND posts.post_type_id='1' group by `posts`.`post_customer_fs_id` ;
For some reason I m receiving A for the records with COUNT 1 but NULL for the records with COUNT bigger than 1.
The post_id is an INTEGER field..
Any help it will be very appreciated!
In
CASE count(post_id)
WHEN count(post_id)=1 THEN 'A'
You are comparing count(post_id) with count(post_id)=1 where the latter results in true or false which equals 1 or 0 in MySQL. You want
CASE
WHEN count(post_id)=1 THEN 'A'
instead.
TRY :::
select post_customer_fs_id AS 'post_customer_fs_id', count(post_id) AS 'open_post_count',
CASE
WHEN count(post_id)=1 THEN 'A'
WHEN count(post_id)>1 THEN 'B'
ELSE 'C'
END AS 'AAA'
from posts where post_status = '3' AND post_type_id= '1'
group by post_customer_fs_id ;

mysql join case count with single query

i am very new to programming and now i have problem to solve it quickly. Trying for 1weeks only and give some result that can satisfy me. Any one can help me with this, heres my table and result that i wanted to show:
enter image description here
this the result
the problem ss
i am trying and trying this code for more than a weeks and its still nothing:
enter code here SELECT idkt.id,
idkt.nama,
idkt.satuan,
trgt.nilai ,
tbl.*,
ROUND((tbl.Triwulan_1 / trgt.nilai * 100), 2) Persen_1,
ROUND((tbl.Triwulan_2 / trgt.nilai * 100), 2) Persen_2,
ROUND((tbl.Triwulan_3 / trgt.nilai * 100), 2) Persen_3,
ROUND((tbl.Triwulan_4 / trgt.nilai * 100), 2) Persen_4
FROM indikator idkt
left JOIN (
SELECT id_indikator,
YEAR(tgl) tahun,
SUM(CASE
WHEN MONTH(tgl) BETWEEN 1 AND 3 THEN
1
END) AS Triwulan_1,
SUM(CASE
WHEN MONTH(tgl) BETWEEN 4 AND 6 THEN
1
END) AS Triwulan_2,
SUM(CASE
WHEN MONTH(tgl) BETWEEN 7 AND 9 THEN
1
END) AS Triwulan_3,
SUM(CASE
WHEN MONTH(tgl) BETWEEN 10 AND 12 THEN
1
END) AS Triwulan_4
FROM msttrans
WHERE YEAR(tgl) = ".$tahun."
GROUP BY id_indikator, YEAR(tgl)) tbl
ON (idkt.id = tbl.id_indikator)
left JOIN target trgt
ON (trgt.id_indikator = idkt.id
AND YEAR(trgt.tahun) = tbl.tahun)
left JOIN (select target.nilai as fnilai from target where id_indikator is not NULL) trgt on trgt.id_indikator = idkt.id
ORDER BY idkt.id ASC
If any inputs to arithmetic expressions like
ROUND((tbl.Triwulan_1 / trgt.nilai * 100), 2)
are null, the resulting expression will also be null. You probably should try changing these sorts of expressions from
SUM(CASE WHEN MONTH(tgl) BETWEEN 1 AND 3 THEN 1 END) AS Triwulan_1
to
SUM(CASE WHEN MONTH(tgl) BETWEEN 1 AND 3 THEN 1 ELSE 0 END) AS Triwulan_1
to avoid too many attempts to work with null values.
Also, you should troubleshoot by testing your inner queries one-by-one. Make sure they work (especially the one containing GROUP BY) before you use it in a join.
Edit LEFT JOIN operations with ON conditions that don't match any rows return null values for the columns in the left table. This is a common source of unexpected null values. (If you use JOIN in place of LEFT JOIN the query suppresses rows from the right table if they don't match the left table.)
You could troubleshoot these nulls with expressions like this.
SELECT ... tbl.Triwulan_1, trgt.nilai, ROUND((tbl.Triwulan_1 / trgt.nilai * 100), 2), ...

How do I calculate the difference of two alias for sorting

Considering the following code:
SELECT SUM(w.valor),
SUM(CASE WHEN w.tipo = '+' THEN w.valor ELSE 0 END) AS total_credit,
SUM(CASE WHEN w.tipo = '-' THEN w.valor ELSE 0 END) AS total_debit,
w.clientUNIQUE,
c.client as cclient
FROM wallet AS w
LEFT JOIN clients AS c ON w.clientUNIQUE = c.clientUNIQUE
WHERE w.status='V'
GROUP BY w.clientUNIQUE
ORDER BY total_credit-total_debit
I'm trying to calculate the difference of two aliased calculated values for sorting purposes, but I'm getting the following error:
Reference 'total_credit' not supported (reference to group function)
What am I doing wrong and how can I order results by using the difference value between the two aliases?
You can't refer to columns by their alias in the same select expression, so there are 2 options...
Repeat the expressions in the order by (yuk):
ORDER BY
SUM(CASE WHEN w.tipo = '+' THEN w.valor ELSE 0 END) AS total_credit -
SUM(CASE WHEN w.tipo = '-' THEN w.valor ELSE 0 END) AS total_debit
Or easier on the brain and easier to maintain (DRY), order via a sub query:
select * from (
<your query without the ORDER BY>
) q
ORDER BY total_credit - total_debit

get count of two table fields in one query

I am trying to get the count of females and males in the gender field of a table.
Is there a way to get the count of each in one query?
Something like:
select * from table count(where gender = 'm') as total_males, count(where gender = 'f') as total_females;
or will it require two queries?
select count(*) from table where gender = 'm';
select count(*) from table where gender = 'f';
This is basically a PIVOT. MySQL does not have a pivot so you can use an aggregate function with a CASE statement to perform this:
select
sum(case when gender = 'm' then 1 else 0 end) Total_Male,
sum(case when gender = 'f' then 1 else 0 end) Total_Female
from yourtable
See SQL Fiddle with Demo
Or using COUNT:
select
count(case when gender = 'm' then 1 else null end) Total_Male,
count(case when gender = 'f' then 1 else null end) Total_Female
from yourtable;
See SQL Fiddle with Demo
Something like this will work:
SELECT SUM(IF(t.gender='m',1,0)) AS total_males
, SUM(IF(t.gender='f',1,0)) AS total_females
FROM mytable t
The "trick" here is that we are using a conditional test to return either a 0 or a 1 for each row, and then adding up the 0's and 1's. To make this a little more clear, I am using the SUM aggregate function rather than COUNT, although COUNT could be used just as easily, though we'd need to return a NULL in place of the zero.
SELECT COUNT(IF(t.gender='m',1,NULL)) AS total_males
, COUNT(IF(t.gender='f',1,NULL)) AS total_females
FROM mytable t
Consider that the two expressions in the SELECT list of this query:
SELECT COUNT(1)
, SUM(1)
FROM mytable t
Will return the same value.
If you want to avoid the MySQL IF function, this can also be done using the ANSI SQL CASE expression:
SELECT SUM( CASE WHEN t.gender = 'm' THEN 1 ELSE 0 END )) AS total_males
, SUM( CASE WHEN t.gender = 'f' THEN 1 ELSE 0 END )) AS total_females
FROM mytable t
select sum(case when gender='m' then 1 else null end) as total_males, sum(case when gender='f' then 1 else null end) as total_females from ...
Should work just fine!
If your only issue is to avoid two queries, you can always write two queries as subselects of one query.
Select (select 1 from dual) as one, (select 2 from dual) as two from dual
This would work for your scenario, too.