HeidiSQL: count all students in different courses - mysql

I'm trying to calculate the amount of students from each course, but the amount JOINs required stumbles me a bit.
Tables: student, group and course
students quantity was taken from the group table
SELECT id, SUM(`students quantity`) AS students_all FROM course
JOIN student ON student.group = course.id
JOIN group ON group.`students quantity`=student.id
GROUP BY student.course
ORDER BY `students_all` DESC
What confuses me is this part: I know, I'm doing something wrong, but I can't figure out what exactly
SUM(`students quantity`) AS students_all FROM course
JOIN student ON student.group = course.id
JOIN group ON group.`students quantity`=student.id
The result I have is the single course.id displayed and students_all having all the students overall, instead of the all students from specific course.
What I need to recieve from the query
course.id: 1 2 3 4
students_all: 7 7 6 7
What I get
course.id: 1
students_all: 27
Since I have Russian inteface, I'll post samples this way, to avoid confusion
Group
Student (excluding all the irrelevant data)
As for the course table: the only relevant column there is ID which range is 1-4

It's very late reply of mine, but I solved it myself. No need to answer!

Related

Get count of rows from joining 2 tables

I have 2 tables:
Table1: users
id
name
faculty_id
level_id
1
john
1
1
2
mark
1
1
3
sam
1
2
Table 2: subjects
id
title
faculty_id
1
physics
1
2
chemistry
1
3
english
2
SQL query:
SELECT count(subjects.id) FROM users INNER JOIN subjects ON users.faculty_id = subjects.faculty_id WHERE users.level_id = 1
I'm trying to get count of subjects where users.level_id = 1, Which should be 2 in this case physics and chemistry.
But it's returning more than 2.
Why is that and how to get only 2?
I would recommend exists:
SELECT COUNT(*)
FROM subjects s
WHERE EXISTS (SELECT 1
FROM users u
WHERE u.faculty_id = s.faculty_id AND
u.level_id = 1
);
This counts subjects where a user exists with a level of 1.
You are joining users and subjects on faculty_id; this produces every combination of user and subject rows (2 users and 2 subjects makes 4 combined rows); change your query to SELECT users.*, subjects.* FROM... to see how this works.
count(subjects.id) counts the number of non-null subjects.id values in your results; you can just do count(distinct subjects.id).
The two tables are not directly related as none is parent to the other. The faculty table is parent to both tables and this is what relates the two tables indirectly.
When joining the faculties' students with the faculties' subjects per faculty, you get all combinations (john|physics, mark|physics, sam|physics, john|chemistry, mark||chemistry, ...). Whether John really has the subject Physics cannot even be gathered from the database. We see that John studies a faculty containing the subjects Physics and Chemistry, but does every student have every subject belonging to their faculty? You probably know but we don't. That shows that in order to write proper queries, one should know their database :-)
Now you are joining the tables and get all students per faculty multiplied with all subjects per faculty. You limit this to level_id = 1, which gets you 2 students x 2 subjects = 4. You could use COUNT(*) for this, because you are counting rows. By applying COUNT(subjects.id) instead you are only counting rows for which the subject ID is not null, but that is true for all rows, because all four combined rows have either subject ID 1 (Physics) or 2 (Chemistry). Counting something that cannot be null makes no sense, except for counting distinct, as has already been suggested. You can COUNT(DISTINCT subjects.id) to get the distinct number of subjects matching yur conditions.
This, however, has two drawbacks. First, the query doesn't clearly show your intention. Why do you join all students with all subjects, when your are not really interested in the (four) combinations? Secondly, you are building an unnecessary intermediate result (four rows in your small example) that must be searched for duplicates, so these can be removed from the counting. That means more memory consumed and more work for the DBMS.
What you want to count is subjects. So select from the subjects table. Your condition is that a student exists with level 1 for the same faculty. Conditions belong in the WHERE clause. Use EXISTS as Gordon suggests in his answer or use IN which is slightly shorter to write and may hence be considered a tad more readable (but that boils down to personal preference, as EXISTS and IN express exactly the same thing here).
select count(*)
from subjects
where faculty_id in (select faculty_id from users where level_id = 1);
You can just add "distinct" before subjects.id
your SQL query like:
SELECT count(distinct subjects.id) FROM users INNER JOIN subjects ON users.faculty_id = subjects.faculty_id WHERE users.level_id = 1
You want to count level_id and you have mentioned subject_id in the code. I would suggest first join two tables.
SELECT users.name, users.level_id,
subjects.title
FROM users
INNER JOIN subjects ON
users.faculty_id = subjects.faculty_id as new_table
After joining the table u can get the count.
SELECT level_id, COUNT(level_id)
FROM new_table
GROUP BY level_id
WHERE level_id = 1
(You have not mentioned group by in your code.)

Eliminate certain duplicated rows after group by

With this db:
Chef(cid,cname,age),
Recipe(rid,rname),
Cooked(orderid,cid,rid,price)
Customers(cuid,orderid,time,daytime,age)
[cid means chef id, and so on]
Given orders from customers, I need to find for each chef, the difference between his age and the average of people who ordered his/her meals.
I wrote the following query:
select cid, Ch.age - AVG(Cu.age) as Diff
from Chef Ch NATURAL JOIN Cooked Co,Customers Cu
where Co.orderid = Cu.orderid
group by cid
This solves the problem, but if you assume that customers has their unique id, it might not work,because then one can order two meals of the same chef and affect the calculation.
Now I know it can be answered with NOT EXISTS but I'm looking for a soultion which includes the group by function (something similar to what I wrote). So far I couldn't find (I searched and tried many ways, from select distinct , to manipulation in the where clause ,to "having count(distinct..)" )
Edit: People asked for an exmaple. i'm coding using SQLFiddle and it crashes alot, so I'll try my best:
cid | cuid | orderid | Cu.age
-----------------------------
1 333 1 20
1 200 2 41
1 200 5 41
2 4 3 36
Let's say Chef 1's age is 50 . My query will give you 50 - (20+40+40/3) = 16 and 2/3. althought it should actually be 50 - (20+40/2) = 20. (because the guy with id 200 ordered two recipes of our beloved Chef 1.).
Assume Chef 2's age is 47. My query will result:
cid | Diff
----------
1 16.667
2 11
Another edit: I wasn't taught any particular sql-query form.So I really have no idea what are the differences between Oracle's to MySql's to Microsoft Server's, so I'm basically "freestyle" querying.(I hope it will be good in my exam as well :O )
First, you should write your query as:
select cid, Ch.age - AVG(Cu.age) as Diff
from Chef Ch join
Cooked Co
on ch.cid = co.cid join
Customers Cu
on Co.orderid = Cu.orderid
group by cid;
Two different reasons:
NATURAL JOIN is just a bug waiting to happen. List the columns that you want used for the join, lest an unexpected field or spelling difference affect the results.
Never use commas in the FROM clause. Always use explicit JOIN syntax.
Next, the answer to your question is more complicated. For each chef, we can get the average age of the customers by doing:
select cid, avg(age)
from (select distinct co.cid, cu.cuid, cu.age
from Cooked Co join
Customers Cu
on Co.orderid = Cu.orderid
) c
group by cid;
Then, for the difference, you need to bring that information in as well. One method is in the subquery:
select cid, ( age - avg(cuage) ) as diff
from (select distinct co.cid, cu.cuid, cu.age as cuage, c.age as cage
from Chef c join
Cooked Co
on ch.cid = co.cid join
Customers Cu
on Co.orderid = Cu.orderid
) c
group by cid, cage;

Display the number of entries in one table, for a key shared by two tables in SQL while also including entries with no place in the other

I'm very new to MySQL, so still learning. Basically, what I currently have is two tables:
Table 1, people, has a name, date of birth, and phone number.
Table 2, meetings, has a name, meeting date, and meeting time.
The entries in "name" in both tables are the same (although the names are mentioned multiple times), although Table 1 has an extra name, Paul, who has no meetings scheduled (so there are no entries of that person in Table 2).
I want to print out a simple, 2 column result showing:
name | number of meetings
david | 3
john | 4
jerry | 5
paul | 0
I am able to print out:
david | 3
john | 4
jerry | 5
using
SELECT name, COUNT(*) as Number_Of_Meetings FROM meetings GROUP BY name;
although this only really uses the name data in meetings so isn't really going to be helpful when trying to print out paul as well because he isn't included.
Using the people table I can also do the much more complicated
SELECT p.name, COUNT(*) as Number_of_Meetings FROM meetings m JOIN people p ON p.name = m.name GROUP BY p.name;
This instead shows the name entries in people but only the ones with an entry in meetings, but I cannot for the life of me figure out how to also show
paul | 0
I've tried so many options but honestly cannot figure it out.
Can somebody help please?
Since you want all records from the people table event when there aren't any matching records in meetings you'll want to use a left join:
SELECT p.name, COUNT(m.name) as Number_of_Meetings
FROM people p
LEFT JOIN meetings m ON p.name = m.name
GROUP BY p.name;

Pretty basic SQL query involving count

Let's say I have the following schema
Company:
-> company_id
-> company_name
Building_to_company:
-> building_id
-> company_id
So each building has its own id as well as a company id which relates it to a single company.
the following query gives two columns -- one for the company name, and then its associated buildings.
SELECT company.company_name, building_to_company.building_id
FROM company, building_to_company
WHERE company.company_id = building_to_company.company_id;
The returned table would look something like this:
Company Name | Building Id
Smith Banking 2001
Smith Banking 0034
Smith Banking 0101
Smith Banking 4055
Reynolds 8191
TradeCo 7119
TradeCo 8510
So that's all simple enough.
But I need to do something a bit different. I need 2 columns. One for the company name and then on the right the number of buildings it owns. And then for a little extra challenge I only want to list companies with 3 or less buildings.
At this point the only real progress I've made is coming up with the query above. I know I some how have to use count on the building_id column and count the number of buildings associated with each company. And then at that point I can limit things by using something like WHERE x < 4
You've basically got it in words already. Assuming company_name is unique, all you have to add to your explanation to get it to work is a GROUP BY clause:
SELECT company.company_name, COUNT(building_to_company.building_id)
FROM company
INNER JOIN building_to_company
ON company.company_id = building_to_company.company_id
GROUP BY company.company_name
(SQL Fiddle demo of this query in action)
To limit it to companies with 3 or less buildings, the key is you have to use a HAVING clause and not WHERE. This is because you want to filter based on the results of an aggregate (COUNT); simply put, WHERE filters come before aggregation and HAVING come after:
SELECT company.company_name, COUNT(building_to_company.building_id)
FROM company
INNER JOIN building_to_company
ON company.company_id = building_to_company.company_id
GROUP BY company.company_name
HAVING COUNT(building_to_company.building_id) < 4
(SQL Fiddle demo of this query in action)
I think you want something like this
SELECT c.company_name, count(b.building_id)
FROM
company as c,
building_to_company as b
WHERE c.company_id = b.company_id
GROUP BY c.company_name;
Use the Below SQL-statement to find the company name with its own building count in descending order.
SELECT company.company_name, count(building_to_company.building_id)
FROM company (nolock), building_to_company(nolock)
WHERE company.company_id = building_to_company.company_id
group by company.company_name
having count(building_to_company.building_id)<=3
order by count(building_to_company.building_id) desc
Kindly let me know if you have any issue. Thanks

mysql left join duplicates

ive been searching for hours but cant find a solution. its a bit complicated so i'll break it down into a very simple example
i have two tables; people and cars
people:
name_id firstname
1 john
2 tony
3 peter
4 henry
cars:
name_id car_name
1 vw gulf
1 ferrari
2 mustang
4 toyota
as can be seen, they are linked by name_id, and john has 2 cars, tony has 1, peter has 0 and henry has 1.
i simply want to do a single mysql search for who has a (1 or more) car. so the anwser should be john, tony, henry.
the people table is the master table, and im using LEFT JOIN to add the cars. my problem arises from the duplicates. the fact that the table im joining has 2 entries for 1 id in the master.
im playing around with DISTINCT and GROUP BY but i cant seem to get it to work.
any help is much appreciated.
EDIT: adding the query:
$query = "
SELECT profiles.*, invoices.paid, COUNT(*) as num
FROM profiles
LEFT JOIN invoices ON (profiles.id=invoices.profileid)
WHERE (profiles.id LIKE '%$id%')
GROUP BY invoices.profileid
";
try this
select distinct p.name_id, firstname
from people p, cars c
where p.name_id = c.name_id
or use joins
select distinct p.name_id, firstname
from people p
inner join cars c
on p.name_id = c.name_id
If you only want to show people that have a car, then you should use a RIGHT JOIN. This will stop any results from the left table (people) to be returned if they didn't have a match in the cars table.
Group by the persons name to remove duplicates.
SELECT firstname
FROM people P
RIGHT JOIN cars C ON C.name_id = P.name_id
GROUP BY firstname
SELECT DISTINCT firstname
FROM people
JOIN cars ON cars.name_id = people.name_id;
If this doesn't work you might have to show us the full problem.
The way to propose it there's no need for a left join since you need at least a car per person. Left join is implicitely an OUTER join and is intended to return the results with 0 corresponding records in the joinned table.