SQL help required - sql

I have a question which asks:
Make a list of all data stored in da_students, da_enrolments and da_courses. Write a query that makes all columns appear only once in the output, the rows are sorted by students' first name ascending AND course_id descending so that students are grouped by course (without using a group by clause) and generated first/ last names( fname.., Lname...) do not appear in the output.
The current code I have is:
SELECT *
FROM DA_STUDENTS
FULL JOIN DA_ENROLMENTS ON student_id = student_student_id
FULL JOIN DA_COURSES ON course_course_id = course_id
ORDER BY first_name ASC, course_id DESC;
Any help please?

To start with: The sorting thing is not well explained. It even looks like it is indented to obfuscate. To sort by student name and course but have the students grouped by course in the end, simply means order by course and then by student.
Then: "generated first/last names ... do not appear in the output". What? How are they generated? Aren't they simple columns in the students table? As to showing something in the output or not: state what you want to show in your SELECT clause. Other columns will not be shown of course.
As to "Make a list of all data stored in da_students, da_enrolments and da_courses": This means all students, all courses plus the associtated enrolements:
from da_students s
cross join da_courses c
left join da_enrolments e on e.student_id = s.student_id and e.course_id = c.course_id
As to "Write a query that makes all columns appear only once in the output": This means, don't
select *
nor
select s.student_id, e.student_id, ...
but only
select s.student_id, ...
so as to show the student ID only once.

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

Microsoft SQL server select statements on multiple tables?

so I've been struggling with some of the select statements on multiple tables:
Employee table
Employee_ID
First_Name
Last_Name
Assignment table
Assignment_ID
Employee_ID
Host_Country_arrival_Date
Host_Country_departure_Date
Scheduled_End_Date
I'm being asked to display query to display employee full name, number of days between the host country arrival date and host country departure date, number of days between today's date and the assignment scheduled end date and the results sorted according to host country arrival date with the oldest date on top.
also, I'm not familiar with the sort function in SQL server..
Here's my query and I've been getting syntax errors:
SELECT
First_Name
Last_Name
FROM Employee
SELECT
Host_Country_Arrival_Date
Host_Country_Departure_Date
FROM Assignment;
So, Basically what your code is doing is 2 different queries. The first getting all the employees names, and the second one getting the dates of the assignments.
What you'll want to do here is take advantage of the relationship between the tables using a JOIN. That is basically saying "Give me all employees and all of HIS/HERS assignments". So, for each assignment that the employee has, it will bring a row in the result with his name and the assignment info.
To get the difference between days you use DATEDIFF passing 3 parameters, the timespan in which to calculate the difference, the first and the second date. It will then Subtract the first one from the second one and give you the result in the selected timespan.
And finnaly the sorting: Just add 'ORDER BY' followed by each column that you want to use for ordering and then specify if you want it ascending (ASC) or descending (DESC).
You can check how I would answer the if that question was proposed to me in a coding challenge.
SELECT
CONCAT(E.First_Name,' ', E.Last_Name) FullName,
DATEDIFF(DAY,Scheduled_End_Date,getdate()) DaysTillScheduledDate,
DATEDIFF(DAY,Host_Country_Arrival_Date,Host_Country_Departure_Date) DaysTillScheduledDate
FROM Employee As E --Is nice to add aliases
Inner Join
Assignment As A
on E.Employee_ID = A.Employee_ID -- Read a little bit about joins, there are a lot of material availabel an its going to be really necessary moving forward with SQL
order by Host_Country_Arrival_Date DESC -- Just put the field that you want to order by here, desc indicates that it should be descending
You should use a JOIN to link the tables together on Employee_ID:
SELECT
First_Name,
Last_Name,
Host_Country_Arrival_Date,
Host_Country_Departure_Date
FROM Employee
JOIN Assignment ON Assignment.Employee_ID = Employee.Employee_ID;
What this is saying basically is that for each employee, go out to the assignments table and add the assignments for that employee. If there are multiple assignments, the employee columns will be repeated on each row with the assignment columns for the assignment.
You need to look for the join and group by. Please find this link for reference tutorial
For now you may try this...
SELECT
CONCAT(Emp.First_Name,' ', Emp.Last_Name) FullName,
DATEDIFF(DAY,Scheduled_End_Date,getdate()) DaysTillScheduledDate,
DATEDIFF(DAY,Host_Country_Arrival_Date,Host_Country_Departure_Date) DaysTillScheduledDate
FROM Employee As Emp Inner Join Assignment As Assign on Emp.Employee_ID = Assign.Employee_ID
order by Host_Country_Arrival_Date DESC

Cannot find correct number of values in a table that are not in another table, though I can do otherwise

I want to retrieve the course_id in table course that is not in the table takes. Table takes only contains course_id of courses taken by students. The problem is that if I have:
select count (distinct course.course_id)
from course, takes
where course.course_id = (takes.course_id);
the result is 85 which is smaller than the total number of course_id in table course, which is 200. The result is correct.
But I want to find the number of course_id that are not in the table takes, and I have:
select count (distinct course.course_id)
from course, takes
where course.course_id != (takes.course_id);
The result is 200, which is equal the number of course_id in table course. What is wrong with my code?
This SQL will give you the count of course_id in table course that aren't in the table takes:
select count (*)
from course c
where not exists (select *
from takes t
where c.course_id = t.course_id);
You didn't specify your DBMS, however, this SQL is pretty standard so it should work in the popular DBMSs.
There are a few different ways to accomplish what you're looking for. My personal favorite is the LEFT JOIN condition. Let me walk you through it:
Fact One: You want to return a list of courses
Fact Two: You want to
filter that list to not include anything in the Takes table.
I'd go about this by first mentally selecting a list of courses:
SELECT c.Course_ID
FROM Course c
and then filtering out the ones I don't want. One way to do this is to use a LEFT JOIN to get all the rows from the first table, along with any that happen to match in the second table, and then filter out the rows that actually do match, like so:
SELECT c.Course_ID
FROM
Course c
LEFT JOIN -- note the syntax: 'comma joins' are a bad idea.
Takes t ON
c.Course_ID = t.Course_ID -- at this point, you have all courses
WHERE t.Course_ID IS NULL -- this phrase means that none of the matching records will be returned.
Another note: as mentioned above, comma joins should be avoided. Instead, use the syntax I demonstrated above (INNER JOIN or LEFT JOIN, followed by the table name and an ON condition).

why results of two queries are different?

select distinct ID, title, takes.course_id
from course join takes
on course.course_id = takes.course_id
where takes.course_id in
(select takes.course_id
from takes
where ID = '10204');
select ID, title, takes.course_id
from course join takes
on course.course_id = takes.course_id
where ID = '10204';
I want to query the course IDs and the titles of the courses that a student whose ID is 10204 takes. The first gives a result with 5000 rows which is incorrect. The second give a correct result. So what is wrong with the first?
The first query gives you data for all students that happen to take a course that 10204 also takes.
Essentially the first query can be read as "Find all courses and the students that take them, for any course that is also taken by student 10204". You can look at the first query as a 3 way join. The results of the subquery select takes.course_id from takes where ID = '10204' would be the "third" table.
Adding to the pile on since everyone seems to be offering bits and pieces, some of which are oddly irate...
The first query says "Give me information on the students and courses where the courses were also taken by student 10204"
The second query says "Give me information on the students and courses taken by student 10204"
You say you wanted to get the course IDs and Titles for the courses taken by the student 10204, so obviously the second query is the correct one. You don't care about other student's that have taken the same courses.
Perhaps, to put it into perspective, rewriting the first, and incorrect query will help:
select distinct ID, title, takes.course_id
from course
join takes
on course.course_id = takes.course_id
join takes as takes2
on takes.course_id = takes2.course_id
WHERE
takes2.ID = '10204');
Well that is could be because in the first query you are quering where the course_id in takes table is equal to a specific course_id in that table (WHICH CAN BE NOT UNIQUE)
and in the second query you are straightly querying where the course_id is equal to a unique ID in that table!
Thanks you guys. I think my problem is that I did not realize that other students can take the same courses with the student having ID 10204. That this why though the condition is to query only courses take by the students 10204, the results is all about the courses taken by both 10204 and other students.
Because takes.ID != course.ID. The first you use takes.ID in the where clause but the second you use course.ID

I am unable to return all values using joins in my sql query, what am I doing wrong?

I have three tables - Assignment, Grades, Student and I am trying to make this query work so that it returns all assignments even if there is no grade entered for a student yet.
The tables are setup like this
Assignment
AssignmentId, AssignmentName, PointsPossible
Grades (Junction table)
StudentId, AssignmentId, PointsReceived
Student
StudentId, StudentName
My query:
select
s.StudentName, a.AssignmentName, g.PointsReceived, a.PointsPossible
from
student s
cross join
assignment a
left outer join
grades g on s.StudentId = g.StudentId and g.AssignmentId = a.AssignmentId
order by
s.StudentName;
When I run the query I get all the names I need, but I don't get all the assignments back. I should be getting all the names, all the assignments, and if the assignment hasn't been graded yet, there should be a null value returned.
I need a little direction, maybe my tables are setup incorrectly.
You need to get all assignments even if there isn't a grade? The obvious question is: without a junction table, how do you know which assignments to provide for each student?
So, let me guess that you want to get a cross product of all students and all assignments, along with grades (if any). If so, you want to structure your query like this:
select s.StudentName, a.AssignmentName, a.PointsPossible, g.PointsReceived
from students s cross join
assignments a left outer join
grades g
on g.StudentId = a.StudentId and g.AssignmentId = a.AssignmentId;
order by s.StudentName;