How do I count the results of a select in SQL - mysql

I was given the following question for an assignment
Write a SQL statement to show the sum of Hours Worked for each Type of OWNER but exclude services of employees who have Experience Level of Junior and exclude any Owner Type with Less Than Four members
I'm having a hard time figuring out how to accomplish the last part of the question. I believe that it would require counting a portion of a select statement but I'm not sure how to do that.
So far, this is what I have:
SELECT SUM(HoursWorked), OwnerType
FROM PROPERTY_SERVICE
JOIN EMPLOYEE ON EMPLOYEE.EmployeeID=PROPERTY_SERVICE.EmployeeID
JOIN OWNED_PROPERTY ON OWNED_PROPERTY.PropertyID = PROPERTY_SERVICE.PropertyID
INNER JOIN OWNER ON OWNER.OwnerID=OWNED_PROPERTY.OwnerID
WHERE NOT EMPLOYEE.ExperienceLevel='Junior'
*This is where I believe the solution should go*
GROUP BY OWNER.OwnerType;

Presumably, you just want a HAVING clause:
SELECT SUM(HoursWorked), o.OwnerType
FROM PROPERTY_SERVICE ps
JOIN EMPLOYEE e ON e.EmployeeID = ps.EmployeeID
JOIN OWNED_PROPERTY op ON op.PropertyID = ps.PropertyID
INNER JOIN OWNER o ON o.OwnerID = op.OwnerID
WHERE e.ExperienceLevel <> 'Junior'
GROUP BY o.OwnerType
HAVING COUNT(*) < 4
This excludes groups of rows having the same OwnerType that contain less than 4 rows. You might need to adjust the expression to your exact definition of a member.
Note that I added table aliases to the query to make it easier to read and write. I would also recommend prefixing column HoursWorked with the table it belongs to, to avoid ambiguity.

Related

MySQL - Cannot reference result of a subquery

Hit a roadblock and hoping someone here is able to help please?
edit: DB<>FIDDLE : https://dbfiddle.uk/?rdbms=mysql_5.5&fiddle=c73f8ec9a60f530fe4ad489dc743f9b9
I have 3 tables:
marks - which uses grades;
target - which also uses grades;
grades - a lookup for grades to points.
What I am trying to do is calculate the total points for grades within the marks table, then calculate the total target points by multiplying the points value for the target by the number of grades within the marks table for a given person (adno).
I'm able to sum and count the points values from the marks table without a problem, but as I've used an inner joint for the marks to grades already I cannot add a further one for target to grades so I've used a subquery.
However when I try to use the result of subquery (EDIT single_target_points , not target_points as I originally posted) in the calculation in the line straight after it I get the error :
[Err] 1054 - Unknown column 'single_target_points' in 'field list'
This is the query I am trying:
SELECT
marks.adno,
Sum(grades.points) AS total_points,
Count(grades.points) AS no_of_subjects,
(SELECT grades.points FROM targets INNER JOIN grades ON targets.grade = grades.grade WHERE targets.adno = marks.adno GROUP BY grades.points) AS single_target_points,
single_target_points*no_of_subjects AS target_points
FROM
marks
INNER JOIN grades ON marks.resultvalue = grades.grade
INNER JOIN targets ON targets.adno = marks.adno
GROUP BY
marks.adno
This will resolve the query for you, but not sure what you are doing is completely accurate. I took your in-line query and made it a subquery including the "adno" (person id) to get all distinct points. Using that and JOINING based on the person like the others.
SELECT
m.adno,
Sum(g.points) AS total_points,
Count(g.points) AS no_of_subjects,
SUM( TP.single_target_points * g.points) AS target_points
FROM
marks m
JOIN grades g
ON m.resultvalue = g.grade
JOIN students s
ON m.adno = s.adno
JOIN targets t
ON m.adno = t.adno
JOIN
( SELECT distinct
t2.adno,
t2.grade,
g2.points single_target_points
FROM
targets t2
JOIN grades g2
ON t2.grade = g2.grade ) TP
on t.adno = TP.adno
and t.grade = TP.grade
GROUP BY
m.adno
Now, that being said, it looks like you are trying to compute a person's GPA (grade point average). If you can EDIT your existing post and provide samples of data (use spaces to align, not tabs) even if fictional names (but not necessary since things here are all ID values and otherwise not private). It would help to see the basis of what computations are going on. If not done correctly, you will get Cartesian results and skew your points due to multiple entries * multiple entries = oversized expected answer values.
Also, I Updated your dbfiddle. You inserted into adno instead of students the sample records, inserted into targets the grades, so those tables were blank. I had to correct that. Then changed grades to integer since doing math (via sum( g.points)). Best to use proper data types vs everything as character.
At least the queries are now working, but does not make sense if this is GPA calculations -- as far as I know and have done for U.S. college transcript purposes.

Can someone explain these three SQL queries to me?

I'm a database class practicing some SQL problems to sharpen my skills from my book. Here is the database schema I am using for reference to the problems (the solutions posted are correct, I just need some explanation to better understand them):
Q1: Retrieve the name of each employee who has a dependent with the same first name and is the same sex as the employee.
SELECT E.FNAME, E.LNAME
FROM EMPLOYEE AS E
WHERE E.SSN IN (SELECT D.ESSN, FROM DEPENDENT AS D, WHERE E.FNAME = D.DEPENDENT_NAME AND D.SEX = E.SEX);
I’m a bit confused about this query. Shouldn’t the first FROM clause include “DEPENDENT AS D”? Why are we nesting this? I don’t see a reason why. Just seems to make things more complicated.
Q2: Retrieve the name of the employees who don’t have any dependents.
SELECT E.FNAME, E.LNAME
FROM EMPLOYEE AS E
WHERE NOT EXISTS (SELECT *, FROM DEPENDENT AS D, WHERE D.ESSN = E.SSN);
Instead of using WHERE NOT EXISTS, can't we just use NULL?
Why use the “WHERE NOT EXISTS” clause? Can’t we just use NULL?
Q3: List the names of managers who have at least one dependent.
SELECT FNAME, LNAME
FROM EMPLOYEE
WHERE EXISTS(SELECT * FROM DEPARTMENT WHERE SSN = MGR_SSN)
WHERE EXISTS (SELECT * FROM DEPENDENT WHERE ESSN = SSN);
I honestly just don’t understand the rationale behind this query. I get it somewhat, but am having a hard time even explaining it to myself. Shouldn’t the FROM clause include DEPARTMENT and DEPENDENT? Why does it include just EMPLOYEE? If someone can walk me through this query step by step and explain me why we use those WHERE EXISTS clauses the way it’s being used, that would be really helpful.
Someone needs to teach that class about appropriate comma use.
Q1: No, you wouldn't want the first FROM to be on DEPENDENT, you are trying to select employee information, not dependents'. The subquery is used to find the essn (parent ssn) values for the dependents who have the same name and gender as their parent.
Edit: Upon further inspection of the Q1 query, it does seems rather convoluted; and almost definitely not the best way to go about getting that information. Leaving the subquery as it is, using an EXISTS instead of an IN would not suffice, as it is possible there is a dependent with the same name and gender as the correlated employee that is not actually the employee's dependent. If AND D.ESSN = E.SSN were added to the subquery's WHERE criteria, then EXISTS would be a valid choice. However, there is really no way to "un-correlate" the subquery that does not make it completely obvious that an INNER JOIN on the three fields involved would have been a better choice (outside of the rare cases where some nincompoop names more than one of their same-gendered dependents after themselves; in which case an INNER JOIN is still probably a better solution but now DISTINCT should be used in the SELECT.).
Q2: I'm not sure how you would plan to use NULL.
Q3: Again, you wouldn't (or at least don't need to) select FROM the other tables in the outer query because you are not needing data from them in the results. The two EXISTS conditions are (likely) using correlated subqueries (probably with poor efficiency/performance, JOINs are usually much faster); perhaps it would be more obvious written with aliases like this:
SELECT e.FNAME, e,LNAME
FROM EMPLOYEE AS e
WHERE EXISTS(SELECT * FROM DEPARTMENT AS d WHERE e.SSN = d.MGR_SSN)
AND EXISTS (SELECT * FROM DEPENDENT AS d WHERE d.ESSN = e.SSN);
(Edit: As another answerer noted; you can't have multiple WHERE's in (the same "level" of) a query.
or to translate it to English...
"Give me the first and last name of employees where there is a
department whose manager's ssn is the same as employee's ssn, and
there is a dependent whose parent's ssn is the same as the
employee's."
First one wouldnt run. Have to many , Should be:
SELECT E.FNAME, E.LNAME
FROM EMPLOYEE AS E
WHERE E.SSN IN (SELECT D.ESSN
FROM DEPENDENT AS D
WHERE E.FNAME = D.DEPENDENT_NAME
AND D.SEX = E.SEX);
And if you ask if can be write as a JOIN, yes like this:
SELECT DISTINCT E.FNAME, E.LNAME
FROM EMPLOYEE AS E
JOIN DEPENDENT D
ON E.SSN = D.ESSN
AND E.FNAME = D.DEPENDENT_NAME
AND D.SEX = E.SEX
Second one, again too many , and yes you can check for NULL but need to use LEFT JOIN
SELECT E.FNAME, E.LNAME
FROM EMPLOYEE AS E
LEFT JOIN DEPENDENT D
ON E.SSN = D.ESSN
WHERE D.ESSN IS NULL -- IF doesnt have any match, will only have a NULL row
Third One cant have two WHERE you need AND
SELECT FNAME, LNAME
FROM EMPLOYEE
WHERE EXISTS (SELECT * FROM DEPARTMENT WHERE SSN = MGR_SSN)
AND EXISTS (SELECT * FROM DEPENDENT WHERE ESSN = SSN);
First exists tell you that employee is also a manager. Second exists tell you that employee have a dependent

Join error and order by

I'm trying to write a query which does the below:
For every guest who has the word “Edinburgh” in their address show the total number of nights booked. Be sure to include 0 for those guests who have never had a booking. Show last name, first name, address and number of nights. Order by last name then first name.
I am having problems with making the join work properly,
ER Diagram Snippet:
Here is my current (broken) solution:
SELECT last_name, first_name, address, nights
FROM booking
RIGHT JOIN guest ON (booking.booking_id = guest.id)
WHERE address LIKE '%Edinburgh%';
Here is the results from that query:
The query is partially complete, hoping someone can help me out and create a working version. I'm currently in the process of learning SQL so apologies if its a rather basic or dumb question!
Your query seems almost correct. You were joining the booking id with guets id which gave you some results because of overlapping (matching) ids, but this most likely doesn't correspond to the foreign keys. You should join on guest_id from booking to id from guest.
I'd add grouping to sum all booked nights for a particular guest (assuming that nights is an integer):
SELECT g.last_name, g.first_name, g.address, SUM(b.nights) AS nights
FROM guest AS g
LEFT JOIN booking AS b ON b.guest_id = g.id
WHERE g.address LIKE '%Edinburgh%'
GROUP BY g.last_name, g.first_name, g.address;
Are you sure that nights spent should be calculated using nights field? Why can it be null? If you'd like to show zero for null values just wrap it up with a coalesce function like that:
COALESCE(SUM(b.nights), 0)
Notes:
Rewriten RIGHT JOIN into LEFT JOIN, but that doesn't affect results - it's just cleaner for me
Using aliases eg. AS g makes the code shorter when specifying joining columns
Reference every column with their table alias to avoid ambiguity
SELECT g.first_name,
g.last_name,
g.address,
COALESCE(Sum(b.nights), 0)
FROM booking b
RIGHT JOIN guest g
ON ( b.guest_id = g.id )
WHERE address LIKE 'edinburgh%'
GROUP BY g.last_name,
g.first_name,
g.address;
This post answers your questions about how to make the query.
MySQL SUM with same ID
You can simply use COALESCE as referenced here to avoid the NULL Values
How do I get SUM function in MySQL to return '0' if no values are found?

Inefficient Query

SELECT submissions.created_at, guests.accepted
FROM submission
INNER JOIN guests
ON guests.event_id = 1
ORDER BY submissions.created_at ASC;
Currently this query is taking a long time to run (may not even return everything).
What I am trying to accomplish:
Grab the field submissions.created at and guests.accepted (the two are linked by a submission_id)
given a certain event_id (event 1 for example).
What is wrong with my query?
You forgot to give the JOIN condition in your query. Try this:
SELECT submissions.created_at, guests.accepted
FROM submission s
INNER JOIN guests g on g.event_id = s.submissions_id
where guests.event_id = 1
ORDER BY submissions.created_at ASC;
SELECT submissions.created_at, guests.accepted
FROM submission
INNER JOIN guests
ON guests.(column to match against) = submissions.(column to match on)
where guests.event_id=1
ORDER BY submissions.created_at ASC;
As many others here have already said, your join is a little goofed. It's attempting to join the one row that matches in the guests table against every single row in the submission column.
You would need to do your join like this :
Inner join guest on guest.event_id = submissions_id
where guest.event_id = 1
When you join you need to join on two columns from different table (most of the time id columns)
You can read the MySQL example for more explanation. And here is the link to the MySQL reference manual

Getting object if count is less then a number

I have 2 simple tables - Firm and Groups. I also have a table FirmGroupsLink for making connections between them (connection is one to many).
Table Firm has attributes - FirmID, FirmName, City
Table Groups has attributes - GroupID, GroupName
Table FirmGroupsLink has attributes - FrmID, GrpID
Now I want to make a query, which will return all those firms, that have less groups then #num, so I write
SELECT FirmID, FirmName, City
FROM (Firm INNER JOIN FirmGroupsLink ON Firm.FirmID =
FirmGroupsLink.FrmID)
HAVING COUNT(FrmID)<#num
But it doesn't run, I try this in Microsoft Access, but it eventually should work for Sybase. Please show me, what I'm doing wrong.
Thank you in advance.
In order to count properly, you need to provide by which group you are couting.
The having clause, and moreover the count can't work if you are not grouping.
Here you are counting by Firm. In fact, because you need to retrieve information about the Firm, you are grouping by FirmId, FirmName and City, so the query should look like this:
SELECT Firm.FirmID, Firm.FirmName, Firm.City
FROM Firm
LEFT OUTER JOIN FirmGroupsLink
ON Firm.FirmID = FirmGroupsLink.FrmID
GROUP BY Firm.FirmID, Firm.FirmName, Firm.City
HAVING COUNT(FrmID) < #num
Note that I replace the INNER JOIN by a LEFT OUTER JOIN, because you might want Firm which doesn't belongs to any groups too.