How to join information from 3 tables into 1 column? - mysql

I have 3 tables dogs, breeds and entries. I need to get AVG(score) from entries, Name from dogs and name from breeds.
I have managed to connect 2 tables together and I'm struggle to get the last thing (breeds.name) attached to the table
SELECT AVG(score), dogs.name, dogs.breed_id
FROM entries
JOIN dogs
ON entries.dog_id = dogs.id
JOIN breeds
ON breeds.id = dogs.breed_id
GROUP BY breeds.name, dogs.name, dogs.breed_id
having count(entries.dog_id) > 1
order by AVG(score) DESC
LIMIT 10
current result:
How do I change breed_id for breed.name instead?

Considering that you need name from breeds, name from dogs, and AVG(score) from entries, the following SQL should do that.
SELECT breeds.name, dogs.name, AVG(score)
FROM entries
JOIN dogs ON entries.dog_id = dogs.id
JOIN breeds ON breeds.id = dogs.breed_id
GROUP BY breeds.name, dogs.name
order by AVG(score) DESC
LIMIT 10

You have answered your own question within the question. All you need to do is replace dogs.breed_id with breeds.name. As this will leave you with two columns named name in your result set you should alias them to make it clearer.
Assuming that the id columns are defined as PRIMARY KEYs you can GROUP BY dogs.id (or entries.dog_id) as both of the non-aggregated columns (dogs.name and breeds.name) are functionally dependent on dogs.id.
SELECT AVG(e.score) avg_score, d.name dog_name, b.name breed_name
FROM entries e
JOIN dogs d ON e.dog_id = d.id
JOIN breeds b ON b.id = d.breed_id
GROUP BY d.id
HAVING COUNT(e.dog_id) > 1
ORDER BY avg_score DESC
LIMIT 10

Related

Aggregating three tables but getting wrong values during the aggregation operation

"employee" Table
emp_id
empName
1
ABC
2
xyx
"client" Table:
id
emp_id
clientName
1
1
a
2
1
b
3
1
c
4
2
d
"collection" Table
id
emp_id
Amount
1
2
1000
2
1
2000
3
1
1000
4
1
1200
I want to aggregate values from the three tables input tables here reported as samples. For each employee I need to find
the total collection amount for that employee (as a sum)
the clients that are involved with the corresponding employee (as a comma-separated value)
Here follows my current query.
MyQuery:
SELECT emp_id,
empName,
GROUP_CONCAT(client.clientName ORDER BY client.id SEPARATOR '') AS clientName,
SUM(collection.Amount)
FROM employee
LEFT JOIN client
ON clent.emp_id = employee.emp_id
LEFT JOIN collection
ON collection.emp_id = employee.emp_id
GROUP BY employee.emp_id;
The problem of this query is that I'm getting wrong values of sums and clients when an employee is associated to multiple of them.
Current Output:
emp_id
empName
clientName
TotalCollection
1
ABC
a,b,c,c,b,a,a,b,c
8400
2
xyz
d,d
1000
Expected Output:
emp_id
empName
clientName
TotalCollection
1
ABC
a , b , c
4200
2
xyz
d
1000
How can I solve this problem?
There are some typos in your query:
the separator inside the GROUP_CONCAT function should be a comma instead of a space, given your current output, though comma is default value, so you can really omit that clause.
each alias in your select requires the table where it comes from, as long as those field names are used in more than one tables among the ones you're joining on
your GROUP BY clause should at least contain every field that is not aggregated inside the SELECT clause in order to have a potentially correct output.
The overall conceptual problem in your query is that the join combines every row of the "employee" table with every row of the "client" table (resulting in multiple rows and higher sum of amounts during the aggregation). One way for getting out of the rabbit hole is a first aggregation on the "client" table (to have one row for each "emp_id" value), then join back with the other tables.
SELECT emp.emp_id,
emp.empName,
cl.clientName,
SUM(coll.Amount)
FROM employee emp
LEFT JOIN (SELECT emp_id,
GROUP_CONCAT(client.clientName
ORDER BY client.id) AS clientName
FROM client
GROUP BY emp_id) cl
ON cl.emp_id = emp.emp_id
LEFT JOIN (SELECT emp_id, Amount FROM collection) coll
ON coll.emp_id = emp.emp_id
GROUP BY emp.emp_id,
emp.empName,
cl.clientName
Check the demo here.
Regardless of my comment, here is a query for your desired output:
SELECT
a.emp_id,
a.empName,
a.clientName,
SUM(col.Amount) AS totalCollection
FROM (SELECT e.emp_id,
e.`empName`,
GROUP_CONCAT(DISTINCT c.clientName ORDER BY c.id ) AS clientName
FROM employee e
LEFT JOIN `client` c
ON c.emp_id = e.emp_id
GROUP BY e.`emp_id`) a
LEFT JOIN collection col
ON col.emp_id = a.emp_id
GROUP BY col.emp_id;
When having multiple joins, you should be careful about the relations and the number of results(rows) that your query generates. You might as well have multiple records in output than your desired ones.
Hope this helps
SELECT emp_id,
empName,
GROUP_CONCAT(client.clientName ORDER BY client.id SEPARATOR '') AS clientName,
C .Amount
FROM employee
LEFT JOIN client
ON clent.emp_id = employee.emp_id
LEFT JOIN (select collection.emp_id , sum(collection.Amount ) as Amount from collection group by collection.emp_id) C
ON C.emp_id = employee.emp_id
GROUP BY employee.emp_id;
it works for me now

How to do multiple tasks in a single SQL query

I was given the database below:
movie(movie_id, movie_name, production_year, votes, ranking, rating)
movie_info(movie_id, movie_genre_id, note)
movie_genre(movie_genre_id, genre_name)
person(person_id, person_name, gender)
role(person_id, movie_id, role_name, role_type_id)
role_type(role_type_id, type_name)
I was asked to display the name of the top 7 directors with at least 3 movies in the list, the number of movies they are in and the average rating of their movies, sorted by the average rating. With the query below I managed to get the name of the directors, the number of movies they are in and the average rating, but I'm having issues limiting it to the top 7 and sorting them by the average rating. I tried using LIMIT and ORDER BY, but I'm getting syntax errors.
SELECT
person_name, COUNT(role.movie_id), AVG(rating)
FROM
movie
INNER JOIN
role
ON role.movie_id = movie.movie_id
INNER JOIN
person
ON role.person_id = person.person_id
INNER JOIN
role_type
ON role.role_type_id = role_type.role_type_id
WHERE
type_name = 'director'
GROUP BY
person_name
HAVING
COUNT(role.movie_id) > 2;
I can even order by the number of movies they did and limit it to the top 7, but for God I cannot order it by the AVG(rating)
person_name COUNT(role.movie_id) AVG(rating)
Hitchcock, Alfred 9 8.2888890372382
Kubrick, Stanley 8 8.2999999523163
Wilder, Billy 6 8.3000000317891
Spielberg, Steven 6 8.4000000953674
Scorsese, Martin 6 8.3166666030884
Nolan, Christopher 6 8.5333331425985
Tarantino, Quentin 6 8.3666666348775
In MySQL, Aliases defined in the Select clauses can be used in the Group By, Order By and Having clauses.
Use Order by .. DESC to sort the result-set in descending order and Limit 7 to get only 7 rows.
You should use proper Aliasing in multi table queries, to avoid ambiguous and unintended behavior.
You need to use Group By on person_id also, as there may be cases where director(s) have same name.
If you have duplicate entries in role table, you will have to use Count(Distinct ...) to avoid counting duplicate rows.
Try the following query:
SELECT
p.person_id,
p.person_name,
COUNT(r.movie_id) AS movies_count,
AVG(m.rating) AS average_rating
FROM
movie AS m
INNER JOIN
role AS r
ON r.movie_id = m.movie_id
INNER JOIN
person AS p
ON r.person_id = p.person_id
INNER JOIN
role_type AS rt
ON r.role_type_id = rt.role_type_id
WHERE
rt.type_name = 'director'
GROUP BY
p.person_id,
p.person_name
HAVING
movies_count > 2
ORDER BY
movies_count DESC,
average_rating DESC
LIMIT 7

Looking for the puppy owner's name that had the puppy most serviced by the Puppy Grooming

I have couple of tables:
The first one: Owner
Owner_ID Owner_Name
1 Sam
2 Andrea
3 Gabriela
Second one:
Service_Done
Puppy_ID servided_date
1 01/25/2012
2 02/18/2012
3 05/14/2012
Third One:
Puppy's table
Puppy_ID Owner_ID
1 1
2 2
3 3
I have this code:
Select owner_name
from puppy p, owner o
where p.owner_id = o.owner_ID
From here I am stuck. I was thinking of a join between the tables but I am not sure
The following will return the top 5 owners whose pets were serviced the most.
SELECT
a.`Owner_ID`,
a.`Owner_Name`,
COUNT(*) as `Times_Serviced`
FROM `Owner` a
JOIN `Puppys` b
ON a.`Owner_ID` = b.`Owner_ID`
JOIN `Service_Done` c
ON c.`Puppy_ID` = b.`Puppy_ID`
GROUP BY a.`Owner_ID`
ORDER BY count(*) DESC
LIMIT 5;
By changing the group by, you could also return the pets serviced the most. You could also group the results by year (or other time period).
The above query will give you the owner with the most puppies serviced by grouping it based on owner and order it based on the occurrences of servicing (descending).
SELECT Top 1 o.Owner_Name --Owner with most servicing
FROM Owner o
JOIN Puppys p ON o.Owner_ID = p.Owner_ID
JOIN Service_Done c ON c.Puppy_ID = p.Puppy_ID
GROUP BY o.Owner_ID --grouping based on owner
ORDER BY count(*) DESC --order in descending order on the count of occurences

Department name and number of students

I found one question in MySQL I am trying. Please tell me if following solution will work or is there any better solution?
select D.DEPT_NAME, COUNT(*)
from Departments D
left outer join STUDENTS S
on S.Dept_ID = D.Dept_ID
group by D.DEPT_NAME
order by 2 desc, 1
Students table has following fields:
Student_ID
Student_Name
Gender
Dept_ID
Departments table has following fields:
Dept_ID
Dept_Name
A university uses 2 data tables, Students and Departments, to store data
about its students and the departments associated with each major.
Write a query to print the respective department name and number of students
majoring in each department for all departments in the Departments table
(even ones with no current students).
Sort your results by descending number of students; if two or more departments have same number of students, then sort those departments alphabetically by department name.
Forgive me altering the formatting of the code.
I would change the ORDER BY, as follows:
SELECT
d.DEPT_NAME,
COUNT(s.STUDENT_ID)
FROM
Departments d
LEFT JOIN Students s ON d.DEPT_ID = s.DEPT_ID
GROUP by
d.DEPT_ID
ORDER by
COUNT(s.STUDENT_ID) DESC,
d.DEPT_NAME ASC
You need a way to count the students in each department, then you need a way to list all departments, even those without students.
Counting the students in each department: (http://sqlfiddle.com/#!15/39a8b/15/0)
SELECT Dept_ID, COUNT(*) Students
FROM STUDENTS
GROUP BY Dept_ID
Then, treating that as a subquery, left join it to your other table. (http://sqlfiddle.com/#!15/39a8b/16/0)
SELECT D.DEPT_NAME, S.Students
FROM Departments D
LEFT JOIN (
SELECT Dept_ID, COUNT(*) Students
FROM STUDENTS
GROUP BY Dept_ID
) S ON D.Dept_ID = S.Dept_ID
The LEFT JOIN preserves rows in the DEPARTMENTS table that don't match the ON clause. This gets you stuff like this.
Biology 7
Mathematics (NULL)
Sociology 11
Physics 3
So you have to deal with that (NULL) problem. Here's how. Change the SELECT to say
SELECT D.DEPT_NAME, IFNULL(S.Students,0)
It's a little tricky to join a table to an aggregate where the aggregate (the COUNT/GROUP BY query) has missing data. But that's how you do it.
You can figure out the ORDER BY stuff on your own.
SELECT d.department_name, COUNT(s.student_name) AS student_count
FROM student s
LEFT JOIN department d
ON s.department_id = d.department_id
GROUP BY department_name
ORDER BY d.department_name;
!!This is finally the correct answer !!
Don't hardcode the problem please stay tuned and work like professional
Excute below.
SELECT
ad.Dept_Name,
count(ass.Student_Id) as Stduent_Enrolled
FROM [Alok.Departments] ad
Left Outer Join [Alok.Students] ass
ON ad.Dept_ID = ass.Dept_ID
Group by ad.Dept_Name
ORDER by
CASE WHEN COUNT(ad.Dept_ID) >=2
THEN ad.DEPT_NAME END desc,
CASE WHEN COUNT(ad.Dept_ID) < 2
THEN ad.DEPT_NAME END asc
1 select department_name, count(student_id) as student_count
2 from student
3 left outer join department ON
4 department.department_id=student.department_id
5 group by department_name
6 order by department_name;
#jaat
Use this query
select count(*) from tblstud_info s,tbldept d where s.dno=d.dno group by d.dname

Find the frequency of rows from multiple joint tables

I have this problem with SQL and I can't figure it out.
Imagine that I have 3 tables as follows
Names
Nameid name
1 Starbucks Coffee
2 Johns Restaurant
3 Davids Restaurant
user_likes
userid Nameid
1 1
2 1
2 3
user_visited
userid Nameid
1 2
I want to find the places with the most number of (likes+visited). I also want to select all places not just those who have been liked or visited
I do:
SELECT n.nameid, n.name , COUNT(f.nameid) AS freq
FROM names AS n
LEFT JOIN user_likes ON n.nameid=user_likes.nameid
LEFT JOIN user_visited ON n.nameid=user_visited.nameid
ORDER BY freq DESC
But it doesn't give me the total frequency. The problem is, if a place is both visited and liked, it is counted only once, while I want it to be counted twice.
Any suggestions?
I've made a quick test and although I prefer Serge's solution, this one seemed to perform faster as the amount of items to join will be less:
SELECT n.nameId, n.name, coalesce(sum(likesCount), 0) totalCount FROM NAMES n
LEFT JOIN (
SELECT nameId, count(*) likesCount FROM user_likes
GROUP BY nameId
UNION ALL
SELECT nameId, count(*) visitsCount FROM user_visited
GROUP BY nameId
) s ON n.nameId = s.nameId
GROUP BY n.nameId
ORDER BY totalCount DESC
I'm assuming the following indexes:
alter table names add index(nameid);
alter table user_likes add index(nameid);
alter table user_visited add index(nameid);
Probably the OP can compare the efficiency of both queries with actual data and provide feedback.
SELECT n.name, t.nameid, COUNT(t.nameid) AS freq
FROM Names n
JOIN (
SELECT nameid FROM user_likes
UNION ALL
SELECT nameid FROM user_visited
) t
ON n.nameid = t.nameid
GROUP BY t.nameid ORDER BY freq DESC
Mosty, your usage of coalesce() gave me an idea and I came up with this:
SELECT n.nameid, n.name ,
SUM((IFNULL(user_likes.userid,0)>0)+(IFNULL(user_visited.userid,0)>0) ) AS freq
FROM names AS n LEFT JOIN user_likes ON n.nameid=user_likes.nameid LEFT JOIN
user_visited ON n.nameid=user_visited.nameid ORDER BY freq DESC
Since my example here was a simplification of my problem (I have to join more than two tables to the main table) I'm reluctant to use SELECT inside SELECT, because I know it's not very efficient. Do you see any fundamental problem with my solution?