SQL query, AVG and COUNT on multiple tables - mysql

I need a query, the query that i used doesn't work the way i want for some reason
Here's all the tables involved in the query.
Here's the query i want :
Show a list of books with their average ratings and its number of recommendations
result should be like this :
What i already tried :
SELECT book.isbn, AVG(ratings.rating) AS [AVG Ratings], COUNT(recommend.isbn) AS [Number of recommendation]
FROM book INNER JOIN
recommend ON book.isbn = recommend.isbn INNER JOIN
ratings ON book.isbn = ratings.isbn
GROUP BY book.isbn
But it didn't work, somehow the the AVG rating works great, but the # of recommendations does not, it conflicts with the ratings table.
here's what the result is:
However when i try each one alone, everything works great like this :
for AVG ratings :
SELECT book.isbn, AVG(ratings.rating) AS [AVG Ratings]
FROM book INNER JOIN
ratings ON book.isbn = ratings.isbn
GROUP BY book.isbn
Here's the result :
And for the # of recommendations :
SELECT book.isbn, COUNT(recommend.isbn) AS [Number of recommendation]
FROM book INNER JOIN
recommend ON book.isbn = recommend.isbn
GROUP BY book.isbn
Here's the result :
So i want a query to combine the two views into one view

If you want to get accurate results, then you need to do the aggregations before the join:
SELECT b.isbn, r.AvgRating, re.NumRecommendation
FROM book b LEFT JOIN
(SELECT r.isbn, AVG(r.rating) as AvgRating
FROM rating r
GROUP BY r.isbn
) r
ON b.isbn = r.isbn LEFT JOIN
(SELECT r.isbn, COUNT(*) as NumRecommendation
FROM recommendation r
GROUP BY r.isbn
) re
on b.isbn = r.isbn ;
Note that I also switched to left outer joins, so you will get results for all books, even those that are missing either ratings or recommendations.

SELECT
book.isbn,
IFNULL(AVG(ratings.rating),"Not yet rated") AS [AVG Ratings],
IFNULL(COUNT(DISTINCT recommend.iduse),0) AS [Number of recommendation]
FROM
book
LEFT JOIN recommend ON book.isbn = recommend.isbn
LEFT JOIN ratings ON book.isbn = ratings.isbn
GROUP BY book.isbn
Should to the trick:
Counting the distinct users recommending a book will fix the cartesian product issue
Left joins with the usual IFNULL() plumbing will make it work on books that have either no recommendations or no ratings

If you are using SQL Server you can use the over clause:
SELECT
B.isbn,
AVG(ra.rating) OVER (PARTITION BY B.isbn) AS [AVG RATE],
COUNT(re.isbn) OVER (PARTITION BY B.isbn) AS [RECOMMEND COUNT]
FROM Book B
LEFT JOIN recommend re ON B.isbn = re.isbn
LEFT JOIN ratings ra ON B.isbn = ra.isbn

Related

Join queries are not working as expected when trying to compare a count result with a value

I'm learnin SQL from a book and i'm trying to do some exercices on join queries.The only problem that i'm facing is that all of my join queries are not working while they seem well
students(student_id,student_names,student_age)
courses_students(course_id,student_id)
courses(course_id,course_schedule,course_room,teacher_id)
teachers(teacher_id,teacher_names)
The query is "which courses have more than 5 students enrolled?"
Here is what i've done :
SELECT course_name,
count
(SELECT count(*)
FROM courses) AS COUNT
FROM students,
courses,
courses_students
WHERE students.student_id=courses_students.student_id,
courses.course_id=courses_students.course_id
AND COUNT > 5
And the other one is what are the names of students enrolled in at least 2 courses scheduled for the same hours
My query :
SELECT student_name,
schedule
FROM students,
courses,
courses_students
WHERE students.student_id=courses_students.student_id,
courses.course_id=courses_students.course_id
AND COUNT > 2
In this inner query:
(SELECT count(*)
FROM courses) AS COUNT
you need to narrow down what is included in the COUNT. As it is, it is selecting all items in the courses table. The inner query does not know about the restrictions in the outer query. Try adding a where clause in this inner query. You might need to add table aliases to uniquely refer to the correct courses table, so there is no ambiguity whether it is referring to the courses table in the inner query or the outer query.
And, as noted in other answers, this is not the best way to structure joins.
In MySQL you are required to define joins explicitly. Unlike Oracle it can't handle joins with sign of =.
SELECT course_name,
count
(SELECT count(*)
FROM courses) AS COUNT
FROM students
INNER JOIN courses on courses.course_id=courses_students.course_id
INNER JOIN courses_students on students.student_id=courses_students.student_id
WHERE COUNT(*) > 2
You need to aggregage by course and then assert the number of students:
SELECT
c.course_name,
COUNT(*) AS cnt
FROM courses c
INNER JOIN courses_students cs
ON c.course_id = cs.course_id
INNER JOIN students s
ON cs.student_id = s.student_id
GROUP BY
c.course_name
HAVING
COUNT(*) > 5;
stackoverflow is actually not a site to do homework, but as you already have given a try to solve the task, here is a solution for question number one:
SELECT cs.course_id
FROM courses_students cs
INNER JOIN students s
ON cs.course_id = s.course_id
GROUP BY cs.course_id
HAVING count(*) > 5
Read about the GROUP BY and HAVING clause - nice way to solve some problems.
Question number 2 could be solved like this:
SELECT student_names
FROM students s
INNER JOIN courses_students cs
ON cs.student_id = s.student_id
INNER JOIN (
SELECT course_id
FROM courses c
GROUP BY course_schedule
HAVING count(*) > 1
) sub
ON sub.course_id = cs.course_id
The INNER JOIN with the subquery is selecting courses which are scheduled at the same time (having the same course_schedule).
As the other tables are "connected" with INNER JOINs, we will finally just have the subset of students which are participating one of those courses.

Get total count and comments with posts

I want to get the total likes and total count of the every post in a single query with the help of joins.
I am using this query. but the result is wrong
SELECT blog.id, count(blog_comments.id) as likes , count(blog_likes.id) as comments
FROM blog LEFT JOIN
blog_comments
ON blog.id = blog_comments.blog_id LEFT JOIN
blog_likes
ON blog.id = blog_likes.blog_id
GROUP BY blog.id
Please check the image for table structure:
Your problem is that you are aggregating along two dimensions at the same time. The produces a Cartesian product -- a row with each like pairs with each comment, for a total of l * c rows.
The simplest way to fix this is to use the DISTINCT keyword:
SELECT b.id, count(DISTINCT bl.id) as likes , count(DISTINCT bc.id) as comments
FROM blog b LEFT JOIN
blog_comments bc
ON b.id = bc.blog_id LEFT JOIN
blog_likes
ON b.id = bl.blog_id
GROUP BY b.id;
If you have posts that have lots of likes and lots of comments, this is not recommended, because it creates a Cartesian product of the two.
There are several solutions for this, but I would recommend correlated subqueries:
select b.id,
(select count(*) from blog_likes bl where bl.blog_id = b.id) as likes,
(select count(*) from blog_comments bc where bc.blog_id = b.id) as comments
from blogs b;
This can take advantage of indexes on blog_likes(blog_id) and blog_comments(blog_id).
This is according to my table it will help you...
SELECT people.pe_name, COUNT(distinct orders.ord_id) AS num_orders, COUNT(items.item_id) AS num_items FROM people INNER JOIN orders ON orders.pe_id = people.pe_id INNER JOIN items ON items.ord_id = orders.ord_id GROUP BY people.pe_id;

MySQL JOIN tables with COUNT values

I have the following tables in my database.I only listed the important columns which can be used for joining.
I need to get the following output
Currently I'm using two seperate queries for each COUNT value
For assigned licenses
select
products.id,products.name,COUNT(assigned_licenses.id)
from
deployment_users
inner join
assigned_licenses
on
deployment_users.id = assigned_licenses.deployment_user_id
inner join
products
on
assigned_licenses.id = products.id
and
deployment_users.customer_id = 10
group by
assigned_licenses.id
;
For total licenses
select
products.id,products.name,COUNT(total_licenses.id)
from
customers
inner join
total_licenses
on
customers.iccode = licenses.iccode
inner join
products
on
total_licenses.id = products.id
and
customers.id = 10
group by
total_licenses.id
;
Since there are more than a 1,000 products that need to be listed,I want to combine them into a single query.How can I do that?
Your specification leaves some room for interpretation (e.g. can a user have assigned licenses without total licenses? if yes my query will fail.) but I would go with this.
SELECT
products.id,
products.name,
Count(Distinct total_licenses.id) As CountTotalLicenses,
Count(Distinct assigned_liceses.deployment_users_id) As CountAssignedLicenses
FROM products
LEFT JOIN total_licenses ON total_licenses.products_id = products.id
LEFT JOIN customers ON customers.iccode = total_licenses.customers_iccode
LEFT JOIN assigned_licenses ON assigned_liceses.total_licenses_id = total_licenses.id
WHERE
customers.id = 10
GROUP BY
products.id,
products.name
For the future it would be awesome if you could paste code as code and not as an image. People cannot simple copy paste snippets of your code and have to type everything again...
Try joining Both of your query
SELECT * FROM (
(First Query) as assigned_licn
INNER JOIN
(Second Query) as total_licn
USING (id)
);

Getting an average for each row based on a separate query

Above is my table schema. My task is to Write a SQL command to display for each publisher the publisher’s name, the publisher’s location and the
average cost of the books that the publisher sells. I have a mostly working query:
SELECT Publisher.name, Publisher.location,
(SELECT AVG(Book.cost)
FROM (Book
INNER JOIN Publisher
ON Book.publisherName = Publisher.name)
WHERE Book.publisherName = Publisher.name
) bookAverage FROM Book
INNER JOIN Publisher ON Book.publisherName = Publisher.name;
The problem is that this returns the average of all books in the Books table. How can I change this to only return the average cost of the books associated with each publisher?
Here's a fiddle with the schema implemented already:
http://sqlfiddle.com/#!9/7a9909/11/0
SELECT p.name, p.location, AVG(b.Cost) as AverageBookCost
FROM
Publisher p
INNER JOIN book b
ON b.publisherName = p.name
GROUP BY
p.name, p.location
http://sqlfiddle.com/#!9/7a9909/18
Only 1 join, no sub queries, inner selects nothing needed as you are looking for the straight forward aggregate of the join between the tables. Simply specify you GROUP BY clause correctly.
You are close. You just have too many JOINs. For instance, the subquery only needs the correlation clause:
SELECT p.name, p.location,
(SELECT AVG(b.cost)
FROM Book b
WHERE b.publisherName = p.name
) as bookAverage
FROM Publisher p;
If you were to write this as a JOIN, you would properly write it using a LEFT JOIN and GROUP BY:
SELECT p.name, p.location, AVG(b.cost) as bookAverage
FROM Publisher p JOIN
Book b
ON b.publisherName = p.name
GROUP BY p.name, p.location;
SELECT Publisher.name, Publisher.location, costs.avg_cost
FROM Publisher
INNER JOIN (
SELECT AVG(Book.cost) as avg_cost, Book.publisherName
FROM Book
GROUP BY Book.publisherName
) AS costs ON costs.publisherName = Publisher.name;
Complementing the great answer/solution from #Matt you can get Publisher Name, Publisher Location, Nbr of books, Total cost of Books and Avg price per Book easily in one single shot:
SELECT b.publisherName, p.location, COUNT(1) AS total_books,
ROUND(SUM(cost), 2) AS books_total_cost, ROUND(AVG(cost), 2) AS avg_cost_per_book
FROM book b
JOIN publisher p ON p.name = b.publisherName
WHERE 1=1
GROUP BY b.publisherName
ORDER BY b.publisherName
;
I think that the query itself is well self-explanatory but if you have any question, please, be my guest :)

3 table query with count

I'm having huge difficulty with a 3 table query.
The scenario is that TEAM has many or no MEMBERS, a MEMBER could have many or no TASKS. What I want to get is the number of TASKS for every TEAM. TEAM has its own ID, MEMBER holds this as a FK on TEAM_ID, TASK has MEMBER_ID on the TASK.
I want to get a report of TEAM.NAME, COUNT(Person/Team), Count(Tasks/Team)
I have myself so confused, My thinking was to use an Outer Join on TEAM and MEMBER so I have all the teams with any members they have. From here I'm getting totally confused. If anyone can just point me in the right direction so I have something to work from I'd be so greateful
You want to use count distinct:
MySQL COUNT DISTINCT
select t.name as Team,
count(distinct m.ID) as Member_cnt,
count(distinct t.ID) as Task_cnt
from team t
left join member m
on t.ID= m.TEAM_ID
left join tasks t
on t.MEMBER_ID= m.ID
group by t.name;
I think you can do what you want with aggregation -- and count(distinct):
select t.name,
count(distinct m.memberid) as nummembers,
count(distinct tk.taskid) as numtasks
from team t left join
member m
on t.teamid = j.teamid left join
tasks tk
on tk.memberid = m.memberid
group by t.name;
Try this out :
SELECT Team.name, COUNT(Person.id_person), COUNT(Tasks.id_task)
FROM Team t,
LEFT JOIN Person p on p.team_id = t.id_team
LEFT JOIN Tasks ts on ts.person_id = p.id_person
GROUP BY p.team_id, ts.person_id