MySQL join and count according to its column value - mysql

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

Related

Double the result in count case when I join two table

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

SQL query that finds all the column, that is in multiple rows, but have different value in another column?

If I have a "SALES" table, with columns of SaleID, Product#, and CustomerName. and a PRODUCTS table with two columns product_ID and Name. The contains 5 differnt products. In the SALES table populates when a sale is made.
How would I query customer_name with only Product_ID of 1 and 2?
sales table
SALES_ID PRODUCT_ID CUSTOMER_NAME
1 1 DAVE
2 2 DAVE
3 3 DAVE
4 1 TOM
5 2 TOM
6 1 JANE
7 1 MIKE
8 1 MIKE
9 3 MIKE
10 4 MARY
I would like a table result to be
SALES_ID PRODUCT_ID CUSTOMER_NAME
1 1 TOM
2 2 TOM
Select s.CustomerName from SALES s
INNER JOIN PRODUCTS p ON s.Product#=p.Product#
WHERE p.Product# =1
INTERSECT
Select s.CustomerName from SALES s
INNER JOIN PRODUCTS p ON s.Product#=p.Product#
WHERE p.Product# =2

Count and Distinct SQL query

I have a table of Books, that contains the following columns:
Book_Id User_Id
001 1
002 2
001 1
004 2
005 3
006 3
007 2
008 2
009 1
Where :
Book_Id - identifier of a book that a user read; User_Id - identifier
of a reader/user.
Let's assume that User1 read books three times, but 2 of them were same, so the user 1 read 2 distinct books (001 and 009). User 2 read 4 distinct books, while user 3 read 2 distinct books.
In overall, there are 2 users that read 2 distinct books, and 1 user that read 4 distinct books.
The output expected is as below:
Distinct_Books_Count --- User_Count
2 2
4 1
I tried the following:
SELECT COUNT(DISTINCT Book_Id), COUNT(User_Id) FROM Books GROUP BY
User_Id
But I receive the following table:
Distinct_Books_Count User_Count
2 3
4 4
2 2
So any alternative solution or changes?
I call this a "histogram of histograms" query. You can do it using two group bys:
SELECT num_books, COUNT(*)
FROM (SELECT b.User_Id, COUNT(DISTINCT Book_Id) as num_books
FROM Books b
GROUP BY User_Id
) b
GROUP BY num_books
ORDER BY num_books;

Counting multiple columns in one query

Say I have the following tables,
lesson
id description
-----------------
1 science
2 english
3 maths
lesson_student_rel
lesson_id student_id
----------------------
1 1
1 2
1 3
2 1
3 1
3 3
lesson_tutor_rel
lesson_id tutor_id
--------------------
1 1
2 1
2 2
lesson_assistant_rel
lesson_id assistant_id
------------------------
1 1
1 2
3 2
I would like to count the total number of students, tutors and assistants for each lesson so I get a result like,
lesson_id student_count tutor_count assistant_count
---------------------------------------------------------
1 3 1 2
2 1 2 0
3 2 0 1
I think I've been looking at this too long now and have got completely stuck, I've found a few similar questions but nothing that seems to answer this. Is it possible to do with one query? Any pointers would be good - an answer would be amazing.
Thanks!
I think this does it for you:
SELECT l.id,COUNT(DISTINCT lsr.student_id) student_count,COUNT(DISTINCT ltr.tutor_id) tutor_count
,COUNT(DISTINCT lar.assistant_id) assistant_count
FROM lesson l
LEFT JOIN lesson_student_rel lsr ON lsr.lesson_id=l.id
LEFT JOIN lesson_tutor_rel ltr ON ltr.lesson_id=l.id
LEFT JOIN lesson_assistant_rel lar ON lar.lesson_id=l.id
GROUP BY l.id

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