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

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.

Related

Return all results from a Query on a single row

I have a query that will return all the results for matching row values between two tables.
The tables are Students and StudentRace. The match in the WHERE statement is STUDENTS.ID = STUDENTRACE.STUDENTID.
I am trying to return the column STUDENTRACE.RACECD where all the matching RACECDs are in a single row in the resulting table. The query does something different, however. IF a STUDENTID has more than 1 RACECD it does not repeat each RACECD in a single row for the STUDENTID. It will return a separate row for each RACECD that matches the STUDENTID.Here is my query:
select
STUDENTS.ID as ID,
STUDENTS.STUDENT_NUMBER as STUDENT_NUMBER,
STUDENTRACE.STUDENTID as STUDENTID,
STUDENTS.FIRST_NAME as FIRST_NAME,
STUDENTS.LAST_NAME as LAST_NAME,
STUDENTRACE.RACECD as RACECD,
STUDENTS.ENROLL_STATUS as ENROLL_STATUS
from
STUDENTRACE STUDENTRACE,
STUDENTS STUDENTS
where
STUDENTS.ID = STUDENTRACE.STUDENTID
and ENROLL_STATUS = 0
Here is the result for a STUDENT ID = 23:
StudentID Racecd
23 B
23 W
This is close but not exactly what I would like to see. I would like the result to be:
StudentID Racecd
23 B,W
or something similar to that. I think I may need the CONCAT function and possibly a nested SELECT statement as well, but I am not sure. I am new to SQL so I am not sure how to move forward.
Like jarlh said I am not sure how you select 7 columns but result in 2?
but Listagg is what I think you are looking for. You can separate it by any delimiter (in this case I put comma). Also any outlying columns will need to appear in the group by
select
STUDENTRACE.STUDENTID as STUDENTID,
listagg(STUDENTRACE.RACECD,',') as RACECD
from STUDENTRACE STUDENTRACE,
STUDENTS STUDENTS
where STUDENTS.ID=STUDENTRACE.STUDENTID
AND ENROLL_STATUS = 0
group by STUDENTRACE.STUDENTID
If you want there two records to become one, you want to group by something, aggregating on something else. In this case you want a single record for each student so you can group by the student_id, and you want all races aggregated so you can use GROUP_CONCAT (in MYSQL, but you can use corresponding aggregation function if in other RDBMS) to concatenate the races. It would be like this:
SELECT s.id, s.name, GROUP_CONCAT(sr.race)
FROM students s join studentrace sr on s.id = sr.student_id
GROUP BY s.id
That is the base to get what you want, then you can add the other fields you are interested in on the select and on the where filters.
SQL Fiddle: http://www.sqlfiddle.com/#!9/ad59d6/1/0
Hope that helps.
If you are using Microsoft SQL Server, you might want to try the following code.
create table Person (
Name nvarchar(450)
)
insert into Person values ('Fabio'), ('Laura')
select stuff((select ',' + Name from Person for xml path('')), 1, 1, '')
The above select statement returns the following string.
Fabio,Laura

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)

Find potential duplicate names in database

I have two tables in a SQL Server Database:
Table: People
Columns: ID, FirstName, LastName
Table: StandardNames
Columns: Nickname, StandardName
Sample Nicknames would be Rick, Rich, Richie when StandardName is Richard.
I would like to find duplicate contacts in my People table but replace any of the nicknames with the standard name. IE: sometimes I have Rich Smith other times it is Richard Smith in the People table. Is this possible? I realize it might be multiple joins to the same table but can't figure out how to start.
Firstly, you need to determine how many duplicates you have in your People table...
SELECT p.FirstName, COUNT(*)
FROM People AS p
INNER JOIN StandardNames AS sn
ON CHARINDEX(sn.Nickname, p.FirstName) > 0 OR
CHARINDEX(sn.Nickname, p.LastName) > 0
GROUP BY p.FirstName
HAVING COUNT(*) > 1
That's just to get an idea of what data you're trying to find in relation to the Nicknames that may possibly exist inside (as a wildcard word search) the Firstname and Lastname columns.
If you are happy with the items found then expand on the query to update the values.
Let's say you wanted to change the Firstname to be the Standardname...
UPDATE p2
SET p2.FirstName = p2.Standardname
FROM
(SELECT p.ID, sn.StandardName
FROM People AS p
INNER JOIN StandardNames AS sn
ON CHARINDEX(sn.Nickname, p.FirstName) > 0 OR
CHARINDEX(sn.Nickname, p.LastName) > 0) AS a
INNER JOIN People AS p2 ON p2.ID = a.ID
So this will obviously find all the People IDs that have a match based on the query above, and it will update the People table by replacing the FirstName with the StandardName.
However, there are issues with this due to the limitation of your question.
the StandardNames table should have its own ID field. All tables should have an ID column as its primary table. That's just my view.
this is only going to work for data it matches using the CHARINDEX() function. What you really need is something to find based on a "sound" or similarity to the nicknames. Check out the SOUNDEX() function and apply your logic from there.
And this is assuming your IDs above are unique!
Good luck
You could standardize the names by joining, and count the number of occurrences. Extracting the ID is a bit fiddly, but also quite possible. I'd suggest the following - use a case expression to find the contact with the standard name, and if you don't have one, just take the id of the first duplicate:
SELECT COALESCE(MIN(CASE FirstName WHEN StandardName THEN id END), MIN(id)),
StandardName,
LastName,
COUNT(*)
FROM People p
LEFT JOIN StandardNames s ON FirstName = Nickname AND
GROUP BY StandardName, LastName

Joining section in the From clause

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.

Can't figure this error out?

Code :
SELECT * FROM Grade
WHERE grade = ‘MG’ ‘1-9’
SELECT * StaffNo, Name, DOB, ReportsTo,
FROM Staff
ORDER BY DOB DESC;
I keep getting
ORA-00911: invalid character
is this a problem with the code?
Couple of problems -
1.You either need to do a UNION between the first and second SELECT statement, in which case the columns (datatypes) on both the SELECTs should match. Also, one of the columns should be DOB
SELECT StaffNo, Name, DOB, ReportsTo --you can do * here if Grade has exactly 4 columns of same datatype as in columns in the select below
FROM Grade
WHERE grade IN ('MG', '1', '9') --Check for Missing grade or grade 1 or grade 9
UNION
SELECT StaffNo, Name, DOB, ReportsTo
FROM Staff
ORDER BY DOB DESC;
Or these are two entirely different queries like-
SELECT * FROM Grade
WHERE grade IN ('MG','1', '9');
SELECT StaffNo, Name, DOB, ReportsTo --* means all columns so its either * or just the column name specifically. Both can be done, but doesn't make sense
FROM Staff
ORDER BY DOB DESC;
2.’ is a wrong character in Oracle, it should rather be '.
3.In the second SELECT the column ReportsTo ends with a ,. This will be considered illegal by Oracle.
This is the problem, for potentially two reasons:
WHERE grade = ‘MG’ ‘1-9’
Have you actually included curly quotes in your query? If so, that's probably why Oracle is complaining about an invalid character.
However, it's then unclear exactly what you're trying to match. Do you want anything starting with MG and then a character between 1 and 9? If so, you could use:
WHERE grade BETWEEN 'MG1' AND 'MG9'
If that's not what you mean, you need to explain what you're trying to do more carefully - and understand that if a human can't understand your intention, it's even less likely that a SQL parser will...