Double the result in count case when I join two table - mysql

I have two tables, and I want to join some column from 1 table to another
my table looks like this
School_tbl:
School_ID
School_name
Division
1
School 1
Division 1
2
School 2
Division 2
3
School 3
Division 3
Student_tbl:
Student_id
Student_name
Gender
Grade
School_id
S1
Manny
M
Grade 1
1
S2
Donaire
M
Grade 2
1
S3
Pandesal
F
Grade 1
1
S4
Donisya
M
Grade 1
2
S5
Jinki
F
Grade 2
2
S6
Apol
F
Grade 2
3
S7
Zuh
M
Grade 3
3
MY query is:
SELECT
school_tbl.School_ID,
School_tbl.school_name,
School_tbl.Division,
GROUP_CONCAT(DISTINCT Student_tbl.grade ORDER BY Student_tbl.grade) AS 'Grade',
COUNT(CASE WHEN student_tbl.gender = 'M' THEN 1 END) AS 'Total Male',
COUNT(CASE WHEN student_tbl.gender = 'F' THEN 1 END) AS 'Total Female',
COUNT (DISTINCT (Student_id)) AS 'Total Student'
FROM
Student_tbl
LEFT JOIN School_tbl ON (school_tbl.school_id = student_tbl.School_id)
GROUP BY student_tbl.School_id
Expected output is:
School_tbl.School_id
School_tbl.School_name
School_tbl.Division
Grade
Total Male
Total Female
Total Student
1
School 1
Division 1
Grade 1, Grade 2
2
1
3
2
School 2
Division 2
Grade 1, Grade 2
1
1
2
3
School 3
Division 3
Grade 2, Grade 3
1
1
2
But what I get is:
School_tbl.School_id
School_tbl.School_name
School_tbl.Division
Grade
Total Male
Total Female
Total Student
1
School 1
Division 1
Grade 1, Grade 2
4
2
3
2
School 2
Division 2
Grade 1, Grade 2
2
2
2
3
School 3
Division 3
Grade 2, Grade 3
2
2
2
it doubles the result in total male and female,
I also tried putting distinct in count(case) like count(Distinct case gender = 'M' then 1 end) but then it returns 1 result in all rows in total male and female

I create the same tables that you mentioned and tested your query so the result:
change your query to:
SELECT
school_tbl.School_ID,
School_tbl.school_name,
School_tbl.Division,
GROUP_CONCAT(DISTINCT Student_tbl.grade ORDER BY Student_tbl.grade) AS 'Grade',
COUNT(CASE WHEN student_tbl.gender = 'M' THEN 1 END) AS 'Total Male',
COUNT(CASE WHEN student_tbl.gender = 'F' THEN 1 END) AS 'Total Female'
FROM
Student_tbl
LEFT JOIN School_tbl ON (school_tbl.school_id = student_tbl.School_id)
GROUP BY student_tbl.School_id

Related

MySQL percentage of values in a GROUP BY query

Let's say, I have 3 tables: appointments, doctors and departments.
appointments:
id status doctor_id
---- --------- ---------
1 approved 1
2 cancelled 4
3 approved 4
4 approved 1
5 approved 4
6 NULL 5
7 approved 2
8 NULL 5
9 approved 4
10 approved 3
11 cancelled 1
12 NULL 4
13 approved 3
14 cancelled 1
15 approved 4
16 cancelled 4
17 cancelled 2
18 NULL 4
19 cancelled 1
20 cancelled 4
doctors:
id name department_id
---- --------- -------------
1 John 1
2 Robert 2
3 Patricia 3
4 Mary 1
5 Susan 3
departments:
id name
---- ---------
1 Dermatology
2 Neurology
3 Radiology
What I need is percentage of approved appointments to total of approved and cancelled (approved / (approved + cancelled) * 100) in Dermatology department, grouped by doctors.
I used the following query, which got me closer to solution.
SELECT COUNT(*) AS appointment_count,
doctors.name AS doctor_name,
appointments.status AS appointment_status
FROM appointments
LEFT JOIN doctors ON appointments.doctor_id = doctors.id
LEFT JOIN departments ON doctors.department_id = departments_id
WHERE departments.id = 1
GROUP BY doctors.id,
appointments.status
result:
count doctor_name appointment_status
----- ----------- ---------
2 John approved
3 John cancelled
0 John NULL
4 Mary approved
3 Mary cancelled
2 Mary NULL
But I need the percentage of approved / (approved + cancelled) for each doctors. So the result should be:
approved_percentage doctor_name
------------------- -----------
%40 John
%57 Mary
How can I achieve such a result?
In MySQL, the simplest way to do conditional counts and averages is simply to use the boolean expression. This suggests:
SELECT d.name, count(*),
SUM(a.status = 'approved') as approved_count,
AVG(a.status = 'approved') * 100 as approved_percentage
FROM doctors d INNER JOIN
appointments a
ON a.doctor_id = d.id
GROUP BY d.name;
You can group only by doctors and then use count(if()) functionality like this:
SELECT d.name, count(*), count(if(a.status = 'approved', 1, null)) approved_count,
count(if(a.status = 'approved', 1, null))/count(*) * 100 approved_percentage
FROM doctors d
INNER JOIN appointments a ON a.doctor_id = d.id
GROUP BY d.name

MySQL join and count according to its column value

If I have a MySQL table looking something like this:
breeds
id name
-------------------------------
1 Labrador
2 Jack Russel Terrier
3 Shetland Sheepdog
And a MySQL table looking like this:
dogs
id owner breed sex
-----------------------------------
1 Sara 1 f
2 Kent 1 f
3 Billy 1 m
4 Joe 2 f
5 Billy 2 m
Is it possible to run a MySQL query to get output like this:
id name females males
------------------------------------------------
1 Labrador 2 1
2 Jack Russel Terrier 1 1
3 Shetland Sheepdog 0 0
I would like to have a JOIN or similar that count the number of females/males from the dogs table.
You can do this:
SELECT b.id,b.name,
IFNULL(SUM(CASE WHEN sex='f' THEN 1 ELSE 0 END),0) as females,
IFNULL(SUM(CASE WHEN sex='m' THEN 1 ELSE 0 END),0) as males
FROM breeds b LEFT JOIN
dogs d on b.id=d.breed
GROUP BY b.id,b.name
Explanation:
using LEFT JOIN will include the record eventhough there is male/female count. IFNULL will replace the null value with 0.
Result:
id name females males
-------------------------------------
1 Labrador 2 1
2 Jack Russel Terrier 1 1
3 Shetland Sheepdog 0 0
Sample result in SQL Fiddle.
Or alternatively:
SELECT id, name,
(SELECT COUNT(*) FROM dogs WHERE breed=b.id AND sex='f') females,
(SELECT COUNT(*) FROM dogs WHERE breed=b.id AND sex='m') males
FROM breeds b
see here: http://www.sqlfiddle.com/#!9/03da0/1

How to count how many times that each pair of item has same order id in mySQL

i want to count that how many times that each pair of SKU have been ordered together(have same orderid)
I have an order table of customers that has 2 column ORDERID and SKUID
for an example
ORDERID SKUID
1 1
1 2
1 3
2 1
2 2
2 4
3 1
3 4
i want a result table as
SKU1 SKU2 COUNT
1 2 2
1 3 1
1 4 2
2 3 1
2 4 1
3 4 0
thank you
You can do this with a self-join and aggregation:
select o1.sku, o2.sku, count(distinct o1.orderid) as numorders
from orders o1 join
orders o2
on o1.orderid = o2.orderid and o1.sku < o2.sku;
If you know that the skus are unique in each order, then you can use count(*) instead of count(distinct).

Add total of 3 rows for specific id

I have three tables:
Students
-------------------------------------------------------------
studentId first last gender weight
-------------------------------------------------------------
1 John Doe m 185
2 John Doe2 m 130
3 John Doe3 m 250
Lifts
-------------------
liftId name
-------------------
1 Bench Press
2 Power Clean
3 Parallel Squat
4 Deadlift
5 Shoulder Press
StudentLifts
------------------------------------------------
studentLiftId studentId liftId weight
------------------------------------------------
1 1 1 185
2 2 3 130
3 3 1 190
4 1 2 120
5 2 1 155
6 3 2 145
7 1 1 135
8 1 1 205
9 2 3 200
10 1 3 150
11 2 2 110
12 3 3 250
I would like to have four top lists:
Bench Press
Parallel Squat
Power Clean
Total of the above 3
I can successfully grab a top list for each specific lift using the following query:
SELECT s.studentId, s.first, s.last, s.gender, s.weight, l.name, sl.weight
FROM Students s
LEFT JOIN (
SELECT *
FROM StudentLifts
ORDER BY weight DESC
) sl ON sl.studentId = s.studentId
LEFT JOIN Lifts l ON l.liftId = sl.liftId
WHERE l.name = 'Bench Press'
AND s.gender = 'm'
AND s.weight > 170
GROUP BY s.studentId
ORDER BY sl.weight DESC
However, I am stuck on how to add the highest total of each lift for each student. How can I first find the highest total for each student in each lift, and then add them up to get a total of all three lifts?
Edit
The result set that I am looking for would be something like:
-------------------------------------------------
studentId first last weight
-------------------------------------------------
3 John Doe3 585
1 John Doe 475
2 John Doe2 465
I also forgot to mention that I would actually like two lists, one for students above 170 and one for students below 170.
SELECT -- join student a total weight to the student table
A.studentId,
A.first,
A.last,
C.totalWeight
FROM
Student A,
(
SELECT -- for each studet add the max weights
sum(B.maxWeight) as totalWeight,
B.studentID
FROM (
SELECT -- for each (student,lift) select the max weight
max(weight) as maxWeight,
studentId,
liftID
FROM
StudentLifts
GROUP BY
studentId,
liftID
) B
GROUP BY
studentId
) C
WHERE
A.studentID = C.studentId
-- AND A.weight >= 170
-- AND A.weight < 170
-- pick one here to generate on of the two lists.

table joins with multiple group_concat

I have a problem regarding joining tables with group_concat. Here are the details.
table_orders:
item_cd order_id descs quantity status seq_no
1 100 coca-cola 2 A 232
2 100 pizza 1 A 233
3 101 cheeseburger 5 A 234
4 102 pepsi 4 A 235
4
table_instructions:
item_cd instruction
3 more cheese
3 less vegetable
cancelled_item_table:
quantity seq_no
1 234
1 234
1 235
Now what I want to achieve is like this:
item_cd descs quantity instructions cancelled_item
1 coca-cola 2 - -
2 pizza 1 - -
3 cheeseburger 2 more cheese, less vegetable 1,1
4 pepsi 4 - 1
This is my current query:
SELECT
ord.item_cd,
ord.order_id,
ord.descs,
ord.quantity,
GROUP_CONCAT(x.quantity) as cancelled,
GROUP_CONCAT(i.instruction) as instruct
FROM table_orders ord
LEFT JOIN cancelled_item_table x ON ord.seq_no = x.seq_no
LEFT JOIN table_instructions i ON ord.item_cd = i.item_cd
WHERE ord.status = 'A'
GROUP BY ord.order_id
and here is the output:
item_cd descs quantity instructions cancelled_item
1 coca-cola 2 - 1
2 pizza 1 - 1
3 cheeseburger 2 more cheese, more cheese,
less vegetable, less vegetable 1,1,1,1
4 pepsi 4 - 1
If you notice, cheeseburger has 2 cancelled item and 2 instruction, but the output is 4, looks like it's multiplying.
Since the join with cancelled_item_table multiplies rows, you have to join to an already grouped subquery, like this:
SELECT
ord.item_cd,
ord.order_id,
ord.descs,
ord.quantity - coalesce(x.tot,0) as quantity,
GROUP_CONCAT(i.instruction) as instruct,
x.cancelled
FROM
table_orders ord LEFT JOIN table_instructions i
ON ord.item_cd = i.item_cd LEFT JOIN
(select seq_no, count(*) as tot, GROUP_CONCAT(quantity) as cancelled
from cancelled_item_table
group by seq_no) x ON ord.seq_no = x.seq_no
WHERE ord.status = 'A'
GROUP BY ord.item_cd, ord.order_id, ord.descs, quantity