Joining section in the From clause - sql

Here,I have used joining section in the from clause ...
select course_id, semester, year, sec_id, avg (tot_cred)
from takes natural join student
where year = 2009
group by course_id, semester, year, sec_id
having count (ID) >= 2
Now, My question is , Is this sql query correct ? If yes then why ? Or If not then why ? Thanks.

Your query is technically correct. However, I would advise you strongly to never using natural join. It is a bug waiting to happen. Why? It uses the names -- and only the names -- of columns in the underlying tables. It does not even use declared foreign key relationships.
Instead, use an explicit on or using clause:
select courseid, semester, year, secid, avg(totcred)
from takes t join
student s
using (studentid)
where year = 2009
group by courseid, semester, year, secid
having count(*) >= 2;
Also:
I assume that the spaces in "course id", "sec id", and "tot cred" are simply typos.
Use table aliases.
Qualify the column names -- that is identify what table they are coming from.

Related

how do I make a good subquery in SQL in this code

I need a bit of help with an SQL statement, pretty much a beginner so just go easy on me.
The Program want me to give out every Student who studied for less than 7 years at a School
Select schoolid, characterid, firstname, lastname, count(year) as num
from schoolhouse natural join student natural join character
group by schoolid, characterid, firstname, lastname
So far, so good, with this code I can already see a relation with the counted years but I can't make a where statement which includes the "num" count from the select statement.
WHERE clauses are applied to the individual rows after the tables are joined, but before they are grouped/aggregated. HAVING clauses are used to assert conditions on the results of the aggregation. Just add HAVING count(year) < 7
Select schoolid, characterid, firstname, lastname, count(year) as num
from schoolhouse natural join student natural join character
group by schoolid, characterid, firstname, lastname
having count(year) < 7
But also always qualify which table your columns come from. In this query it's not clear if the year column is from the schoolhouse table, the student table or the character table.
It should look more like...
SELECT TABLE.schoolid, TABLE.characterid, TABLE.firstname, TABLE.lastname, COUNT(TABLE.year) AS num
FROM schoolhouse NATURAL JOIN student NATURAL JOIN character
GROUP BY TABLE.schoolid, TABLE.characterid, TABLE.firstname, TABLE.lastname
HAVING COUNT(TABLE.year) < 7
(Replacing each occurance of TABLE with the correct table name for each case.)
Finally, using words such as character and year as column or table names is usually frowned upon. Such words ofter appear as "key-words" in SQL and can cause errors or ambiguity. In general, if something even might appear as an SQL key-word, don't use it as a column or table name.

Retrieving column value in table2 via same ID in table1

I have this SQL query that returns overdue assignments
SELECT DUE_DATE,
SUBJECT,
ASSIGNMENT,
STUDENT_NAME,
TEACHER_NAME
FROM(SELECT DISTINCT
a.due_date AS due_date,
a.subject AS subject,
a.assignment AS assignment,
a.student_name AS student_name,
a.student_id AS student_id,
a.teacher_name AS teacher_name,
a.teacher_id AS teacher_id
FROM DB.ASSIGNMENT a,
DB.ALL b,
WHERE (trunc(a.DATE_CREATED) >= trunc(db.utc_sysdate)))
WHERE((trunc(due_date) < trunc(db.utc_sysdate));
and I want to include both the teacher and student emails as additional columns in my SQL query - I was wondering how to map their id in table ASSIGNMENT in order to get their respective emails in table ALL with the existing query I have?
We do lack some information, but - wouldn't your query be like this?
select distinct
a.due_date,
a.subject,
a.assignment,
a.student_name,
a.student_email,
a.teacher_name,
a.teacher_email
from db.assignment a join db.all b
on trunc(a.date_created) >= trunc(b.utc_sysdate)
and trunc(a.due_date) < trunc(b.utc_sysdate);
What's the difference, if compared to your query?
your query is invalid
comma after db.all b
the final where clause references db. "alias" (although it is probably schema name, according to inline view's from clause)
there's no point in aliasing column names using exactly the same name; what's the difference between a.due_date as due_date and a.due_date itself? None. So don't use it, you're just causing confusion
as you want to include student's and teacher's e-mail addresses, why don't you just do that? Add those columns into the query ...
it seems that you don't need an inline view; put both where conditions into the same query and remove columns you don't need (both IDs)

SQL Statement Using Multiple Tables

The database being used for this question is structured as follows with Primary Keys bolded, and Foreign Keys ' '.
Countries (Name, Country_ID, area_sqkm, population)
Teams (team_id, name, 'country_id', description, manager)
Stages (stage_id, took_place, start_loc, end_loc, distance, description)
Riders (rider_id, name, 'team_id', year_born, height_cms, weight_kgs, 'country_id', bmi)
Results ('stage_id', 'rider_id', time_seconds)
I am stuck at the question of:
Q: Create a list (year, numridersborn) where we count the number of riders born in different years. Output columns: year, numridersborn. Order by: year
I am currently at :
SELECT year_born AS "year", COUNT(rider_id) as "numridersborn" WHERE ....
May I know how can I go about getting the solution?
Thank you
year_born is in the rider table already so there is no need to Join here.
Just:
SELECT year_born as year, count(*) as numridersborn
FROM Riders
GROUP BY year_born
ORDER BY year_born;
Which is pretty much what you had already with the addition of the GROUP BY and ORDER BY

SQL Aggregation AVG statement

Ok, so I have real difficulty with the following question.
Table 1: Schema for the bookworm database. Primary keys are underlined. There are some foreign key references to link the tables together; you can make use of these with natural joins.
For each publisher, show the publisher’s name and the average price per page of books published by the publisher. Average price per page here means the total price divided by the total number of pages for the set of books; it is not the average of (price/number of pages). Present the results sorted by average price per page in ascending order.
Author(aid, alastname, afirstname, acountry, aborn, adied).
Book(bid, btitle, pid, bdate, bpages, bprice).
City(cid, cname, cstate, ccountry).
Publisher(pid, pname).
Author_Book(aid, bid).
Publisher_City(pid, cid).
So far I have tried:
SELECT
pname,
bpages,
AVG(bprice)
FROM book NATURAL JOIN publisher
GROUP BY AVG(bpages) ASC;
and receive
ERROR: syntax error at or near "asc"
LINE 3: group by avg(bpages) asc;
You can't group by an aggregate, at least not like that. Also don't use natural join, it's bad habit to get into because most of the time you'll have to specify join conditions. It's one of those things you see in text books but almost never in real life.
OK with that out of the way, and this being homework so I don't want to just give you an answer without an explanation, aggregate functions (sum in this case) affect all values for a column within a group as limited by the where clause and join conditions, so unless your doing every row you have to specify what column contains the values you are grouping by. In this case our group is Publisher name, they want to know per publisher, what the price per page is. Lets work out a quick select statement for that:
select Pname as Publisher
, Sum(bpages) as PublishersTotalPages
, sum(bprice) as PublishersTotalPrice
, sum(bprice)/Sum(bpages) as PublishersPricePerPage
Next up we have to determine where to get the information and how the tables relate to eachother, we will use books as the base (though due to the nature of left or right joins it's less important than you think). We know there is a foreign key relation between the column PID in the book table and the column PID in the Publisher table:
From Book B
Join Publisher P on P.PID = B.PID
That's what is called an explicit join, we are explicitly stating equivalence between the two columns in the two tables (vs. implying equivalence if it's done in the where clause). This gives us a many to one relation ship, because each publisher has many books published. To see that just run the below:
select b.*, p.*
From Book B
Join Publisher P on P.PID = B.PID
Now we get to the part that seems to have stumped you, how to get the many to one relationship between books and the publishers down to one row per publisher and perform an aggregation (sum in this case) on the page count per book and price per book. The aggregation portion was already done in our selection section, so now we just have to state what column the values our group will come from, since they want to know a per publisher aggregate we'll use the publisher name to group on:
Group by Pname
Order by PublishersPricePerPage Asc
There is a little gotcha in that last part, publisherpriceperpage is a column alias for the formula sum(bprice)/Sum(bpages). Because order by is done after all other parts of the query it's unique in that we can use a column alias no other part of a query allows that, without nesting the original query. so now that you have patiently waded through my explanation, here is the final product:
select Pname as Publisher
, Sum(bpages) as PublishersTotalPages
, sum(bprice) as PublishersTotalPrice
, sum(bprice)/Sum(bpages) as PublishersPricePerPage
From Book B
Join Publisher P on P.PID = B.PID
Group by Pname
Order by PublishersPricePerPage Asc
Good luck and hope the explanation helped you get the concept.
You need ORDER BY clause and not GROUP BY to sort record. So change your query to:
SELECT pname, AVG(bprice)
FROM book NATURAL JOIN publisher
GROUP by pname
ORDER BY AVG(bpages) ASC;
You need Order By for sorting, which was missing:
SELECT
pname,
bpages,
AVG(bprice)
FROM book NATURAL JOIN publisher
GROUP BY pname, bpages
order by AVG(bpages) ASC;
Base on what you're trying to achieve. You can try my query below. I used the stated formula in a CASE statement to catch the error when a bprice is divided by zero(0). Also I added ORDER BY clause in your query and there's no need for the AVG aggregates.
SELECT
pname,
CASE WHEN SUM(bpages)=0 THEN '' ELSE SUM(bprice)/SUM(bpages) END price
FROM book NATURAL JOIN publisher
GROUP BY pname
ORDER BY pname ASC;
The ASC is part of the ORDER BY clause. You are missing the ORDER BY here.
Reference: http://www.tutorialspoint.com/sql/sql-group-by.htm
Check this
SELECT pname, AVG(bprice)
FROM book NATURAL JOIN publisher
GROUP by pname
ORDER BY AVG(bpages)

Output a record that appears more than x times

I am a beginner in SQL, and I need help to get the solution for this condition.
Output the name of any person who bought tickets to visit the Park more than 4 days in a single month. (Also, output the month.)
create table visitor(
visitID char(n),
name char(n) not null,
primary key (visitID)
);
create table ticket(
ticketID char(n),
ticketType char(n),
day int(n),
month char(n),
year int(n),
visitID char(n),
primary key (ticketID), foreign key (visitID) references visitor
);
I am unable to test my code, but what I have so far is
SELECT name, month
FROM Visitor NATURAL JOIN Ticket AS t
JOIN (SELECT name, month
FROM Visitor NATURAL JOIN Ticket
GROUP BY month, year
HAVING COUNT(1) > 4
) AS s
ON t.name = s.name AND t.month = s.month;
I don't know if this is right or not.
Please help.
SELECT s.name, t.month
FROM Visitor inner JOIN Ticket AS t
ON t.visitID= s.visitID
group by month, year
having count(day)>4
Your query may be right technically. But the subquery answers your question:
SELECT name, month
FROM Visitor NATURAL JOIN Ticket
GROUP BY month, year
HAVING COUNT(1) > 4;
Two points, though. Don't use natural join. It can do strange things, based on the names of columns in the table. Explicitly name the columns, using on or using. And, in some databases, month and year might be reserved words. So they are bad names for columns. You can quote them (in most databases) with double quotes. So:
SELECT "month", "year"
FROM Visitor INNER JOIN
Ticket
using (VisitorId)
GROUP BY "month", "year"
HAVING COUNT(1) > 4;
The query might need to count distinct dates. In this case, the having clause should be:
HAVING count(distinct "day") > 4
I have just started learning Mysql. I am still going through the basic syntax and everything so cant really help. But don't you think there is a fault in your question. As Name attribute in the visitor table is not unique(and it should not even be unique). So basically your not finding the same person. Your just trying to find different person that have the same name that visited the park 4 times in a month.