Appropriate SELECT Statement - sql

I have the following tables:
CREATE TABLE Subject
(
Subject_Code INTEGER,
Subject_Year VARCHAR (8),
PRIMARY KEY (Subject_Code, Subject_Year),
Teacher_ID INTEGER REFERENCES
);
CREATE TABLE Teacher
(
TeacherID INTEGER PRIMARY KEY,
FirstName TEXT,
Department_ID INTEGER References Academic Department(Department_ID)
);
CREATE TABLE Subject-taken
(
Marks_Obtained INTEGER,
Subject_Code INTEGER REFERENCES subject (Subject_Code),
Candidate_ID INTEGER REFERENCES Candidate (Candidate_ID),
PRIMARY KEY (Subject_Code, Candidate_ID)
);
CREATE TABLE Academic_Department
(
Department_ID INTEGER PRIMARY KEY,
Department_Name TEXT
);
I've already tried the following select statement
SELECT m.subject_code,
MIN (marks_obtained) AS Min_Marks,
MAX (marks_obtained) AS Max_Marks
FROM Subject-taken m, Subject a
GROUP BY m.Subject_Code;
Want to use the join function any suggestions on where to use it in order to join the departments with subjects and students

Make use of joins to link your data between tables. Use group by to make statistics by some fields. You can try something like this:
SELECT
Subjects.Subject_Code,
Subjects.Subject_Name,
Teachers.TeacherID,
Academic_Department.Department_ID,
min(Subject-taken.Marks_Obtained) as min_marks,
max(Subject-taken.Marks_Obtained) as max_marks,
avg(Subject-taken.Marks_Obtained) as avg_marks,
stddev_samp(subject-taken.Marks_Obtained) as stddev_marks
FROM
Subjects LEFT JOIN
Teachers ON Subjects.TeacherID = Subjects.TeacherID LEFT JOIN
Academic_Department ON Teachers.Department_ID = Academic_Department.Department_ID LEFT JOIN
Subject-taken ON Subjects.Subject_Code = Subject-taken.Subject_Code
GROUP BY
Subjects.Subject_Code,
Subject.Subject_Name,
Teacher.TeacherID,
Academic_Department.Department_ID
I don't really know if stddev_samp is the aggregate function you need, stddev_pop is also available. Please refer to this PostgreSQL documentation table to find out.

Related

SQL Find all courses who have more students enrolled then the allowed enroll_limit for the course

I'm currently taking a database class and I am stuck on a homework problem due tonight
Find the courses given in the ‘Sloan’ building which have enrolled more students than their enrollment limit. Return the courseno, enroll_limit, and the actual enrollment for those courses.
I'm stuck trying to count how many students are in each course.
CREATE TABLE Course (
courseno VARCHAR(7),
credits INTEGER NOT NULL,
enroll_limit INTEGER,
classroom VARCHAR(10),
PRIMARY KEY(courseNo), );
CREATE TABLE Student (
sID CHAR(8),
sName VARCHAR(30),
major VARCHAR(10),
trackcode VARCHAR(10),
PRIMARY KEY(sID),
FOREIGN KEY (major,trackcode) REFERENCES Tracks(major,trackcode) );
CREATE TABLE Enroll (
courseno VARCHAR(7),
sID CHAR(8),
grade FLOAT NOT NULL,
PRIMARY KEY (courseNo, sID),
FOREIGN KEY (courseNo) REFERENCES Course(courseNo),
FOREIGN KEY (sID) REFERENCES Student(sID) );
My current very broken attempt is
SELECT sloancourse.courseno
FROM course sloancourse
WHERE
sloancourse.classroom = 'Sloan'
and sloancourse.courseno IN (
SELECT c.courseno
FROM student s, enroll e, course c
WHERE
c.courseno = e.courseno
and s.sid = e.sid
and sloancourse.courseno = c.courseno
)
;
Find the courses given in the ‘Sloan’ building which have enrolled more students than their enrollment limit. Return the courseno, enroll_limit, and the actual enrollment for those courses.
You would typically join courses and enrollment (using stanard join syntax, with the on keyword), group by course, and finally filter on courses whose student count with a having clause.
select
c.courseno,
c.enroll_limit,
count(*) actual_enrollment
from course c
inner join enroll e on e.courseno= c.courseno
where c.classroom = 'Sloan'
group by c.courseno, c.enroll_limit
having count(*) > c.enroll_limit
Note that you don't need to bring in the student table to get the desired results.
You could try something like this:
SELECT
Course.courseno,
Course.enroll_limit,
Couse.classroom
FROM Course, Enroll
WHERE Course.courseno = Enroll.courseno
AND Course.classroom = 'Sloan'
GROUP BY Course.courseno
HAVING count(*) > Course.enroll_limit
Postgres docs have an example on these aggregate functions: https://www.postgresql.org/docs/current/tutorial-agg.html
Basically, when you group by one or multiple properties, the resulting table will result in a unique row for the property/combination of properties. In this example, you would have one row for each of the courseno unique values (as they are grouped by this value).

For each employee how many work orders did they work on in 2018?

I am still new to sql and I am trying to figure out for each employee how many work orders did they work on in the year 2018. I have two tables tblWorkOrder and tblTimeCharge they are both connected by the field IDEmployee.
tblWorkOrder has a column called DateCompleted which lets you know when the work order form was completed. However, Whenever I run my Code I get the error no such column.tblWorder.DateCompleted.
Sample Code
SELECT tblEmployee.IDEmployee, tblEmployee.FirstName, COUNT(tblTimeCharge.IDWorkOrder)
FROM tblEmployee LEFT JOIN tblTimeCharge ON tblTimeCharge.IDEmployee = tblEmployee.IDEmployee
GROUP BY tblEmployee.IDEmployee, tblEmployee.FirstName
Having tblWorkOrder.DateCompleted between '01/01/2018' and '12/31/2018';
For my output I would like to have the employees first name as well as the count of how many workorders that were completed for that year.
CREATE TABLE tblEmployee(
IDEmployee Integer NOT NULL Primary Key,
LastName Text,
FirstName Text,
EmpNum Text,
IDGender Integer,
IDRace Integer,
DOB Date,
City Integer,
State Text,
TerminationDate Date,
IDTerminationReason Date,
Email Text,
Phone Text,
IDposition Integer,
HireDate Date,
Adjust Float,
Foreign Key (IDGender) References tblGender(IDGender),
Foreign Key (IDRace) References tblRace(IDRace),
Foreign Key (IDTerminationReason) References tblTermination(IDTR),
Foreign Key (IDPosition) References tblPosition(IDPosition)
);
CREATE TABLE tblTimeCharge (
IDTimeCharger Integer NOT NULL Primary Key,
IDWorkOrder Integer,
IDEmployee Integer,
TimeChargeHours Integer,
TimeChargeDate Date,
Foreign Key (IDWorkOrder) References tblWorkOrder(IDWorkOrder),
Foreign Key (IDEmployee) References tblEmployee(IDEmployee)
);
CREATE TABLE tblWorkOrder (
IDWorkOrder Integer NOT NULL Primary Key,
WONNumber Integer,
IDPriority Integer,
IDCategory Integer,
IDBldg Integer,
Assigned Integer,
DateAvailable Date,
DateNeeded Date,
DateCompleted Date,
DateCreated Date,
Foreign Key (IDPriority) References tblPriority(IDPriority),
Foreign Key (IDCategory) References tblCategory(IDCategory),
Foreign Key (IDBldg) References tblBuilding(IDBldg)
);
Learn to use table aliases! They make the query easier to write and to read!
SELECT e.IDEmployee, e.FirstName, COUNT(wc.IDWorkOrder)
FROM tblEmployee e LEFT JOIN
tblTimeCharge tc
ON tc.IDEmployee = e.IDEmployee LEFT JOIN
tblWorkCharge wc
ON wc.IdWorkCharge= tc.WorkCharge AND
wc.DateCompleted >= '2018-01-01' AND
wc.DateCompleted < '2019-01-01'
GROUP BY e.IDEmployee, e.FirstName;
The key here is putting the date comparisons in the ON clause, so the LEFT JOIN is not turned into an inner join. Also note the use of standard date formats.
This is wnat you want i beleive:
You were missing the table tblWorkOrder in your query
SELECT tblEmployee.IDEmployee, tblEmployee.FirstName,
COUNT(tblTimeCharge.IDWorkOrder)
FROM tblEmployee LEFT JOIN
tblTimeCharge ON tblTimeCharge.IDEmployee = tblEmployee.IDEmployee
inner join
tblWorkOrder on tblTimeCharge.IDWorkOrder = tblWorkOrder.IDWorkOrder
GROUP BY tblEmployee.IDEmployee, tblEmployee.FirstName
Having tblWorkOrder.DateCompleted between '01/01/2018' and '12/31/2018';
Use LEFT join like this:
SELECT e.IDEmployee, e.FirstName, e.LastName,
COUNT(t.IDWorkOrder)
FROM tblEmployee e
LEFT JOIN tblTimeCharge t ON t.IDEmployee = e.IDEmployee
LEFT JOIN tblWorkOrder w on w.IDWorkOrder = t.IDWorkOrder AND w.DateCompleted BETWEEN '2018-01-01' AND '2018-12-31'
GROUP BY e.IDEmployee, e.FirstName, e.LastName
I assume the dates in your table have the format 'YYYY-MM-DD' which is comparable, if not then you should change it.

Joins on multiple tables in Postgresql

I'm practising for an upcoming database exam and I'm trying to get my head around nested and multiple joins in SQL, specifically the Postgresql syntax. I want to return all the student names and department names of all students that achieved grade A.
Here's my schema.
CREATE TABLE student1 (
student_number INTEGER NOT NULL PRIMARY KEY,
name TEXT NOT NULL,
class INTEGER NOT NULL,
major TEXT NOT NULL
);
CREATE TABLE course1 (
course_name TEXT NOT NULL,
course_number TEXT NOT NULL PRIMARY KEY,
credit_hours INTEGER NOT NULL,
department TEXT NOT NULL
);
CREATE TABLE section1 (
section_identifer INTEGER NOT NULL PRIMARY KEY,
course_number TEXT NOT NULL,
semester TEXT NOT NULL,
year INTEGER NOT NULL,
instructor TEXT NOT NULL,
FOREIGN KEY (course_number) REFERENCES course1(course_number) ON DELETE CASCADE
);
CREATE TABLE grade_report1 (
id SERIAL NOT NULL PRIMARY KEY,
student_number INTEGER NOT NULL,
section_identifer INTEGER NOT NULL,
grade TEXT NOT NULL,
FOREIGN KEY (student_number) REFERENCES student1(student_number) ON DELETE CASCADE,
FOREIGN KEY (section_identifer) REFERENCES section1(section_identifer) ON DELETE CASCADE
);
I put together a nested statement that I thought would work:
SELECT t1.name, t3.department
FROM (student1 t1 INNER JOIN grade_report1 t2 ON t1.student_number = t2.student_number) t5
INNER JOIN (course1 t3 INNER JOIN section1 t4 ON t3.course_number = t4.course_number) t6
ON t5.section_identifer = t6.section_identifer
WHERE t2.grade = 'A';
However, this gives me the error invalid reference to FROM-clause entry for table "t1". I'm guessing it is because that is not how you are supposed to name/reference JOINS. I would like a way to JOIN all of these tables together. Thanks!
Remove the parentheses and fix the aliases:
SELECT s.name, c.department
FROM student1 s INNER JOIN
grade_report1 gr
ON gr.student_number = s.student_number INNER JOIN
section1 sec
ON sec.section_identifer = gr.section_identifer INNER JOIN
course1 c
ON sec.course_number = c.course_number
WHERE gr.grade = 'A';
The parentheses are allowed, but they are not needed. When using parentheses (which is very, very rarely needed), they do not get separate aliases.

SQL Query to check if a record does not exist in another table

I have a table which holds details of all Students currently enrolled in classes which looks like this:
CREATE TABLE studentInClass(
studentID int,
classID int,
FOREIGN KEY(studentID) references students(studentID),
foreign key(classID) references class(classID)
);
And another table which contains details of students who have paid for classes:
CREATE TABLE fees(
feesID INTEGER PRIMARY KEY AUTOINCREMENT,
StudentID INTEGER,
AmountPaid INT,
Date DATE,
FOREIGN KEY(StudentID) REFERENCES students(StudentID));
What I want to do is check whether a student who is in a class has not paid for that class. I am struggling to write a SQL query which does so. I have tried multiple queries such as:
Select studentInClass.StudentID
from fees, studentInClass
where fees.StudentID = studentInClass.StudentID;
But this returns no data. I'm not sure how to proceed from here. Any help will be appreciated.
You want outer join :
select s.StudentID, (case when f.AmountPaid is not null
then 'Yes'
else 'No'
end) as Is_fees_paid
from studentInClass s left join
fees f
on f.StudentID = s.StudentID;
With NOT EXISTS:
select s.*
from studentInClass s
where not exists (
select 1 from fees
where studentid = s.studentid
)
with this you get all the rows from the table studentInClass for which there is not the studentid in the table fees.
It's not clear if you also need to check the date.
check it please:
select studentInClass.StudentID
from studentInClass inner join fees ON fees.StudentID = studentInClass.StudentID

Selecting a subset of columns

I am using sqllite to create two tables
CREATE TABLE NAMES(Id integer PRIMARY KEY, Name text);
CREATE TABLE PHONE(Id inetegr PRIMARY KEY, PersonID integer, Number integer,
FOREIGN KEY (PersonID) REFERENCES NAMES(Id)
);
Then, I have written a qry to slect all people having more than 2 phones
select count(PersonID) as counts,Name
from PHONE
INNER JOIN NAMES on NAMES.Id=PHONE.PersonID
group by PersonID
having counts>=2;
The output is like below
2|Tom
However, I dont want to print the count and just need the names, how to change my query to just print the name..
you can try like below
select Name from PHONE
INNER JOIN NAMES on NAMES.Id=PHONE.PersonID
group by Name
having count(distinct Number)>=2;
You can try below
select Name from PHONE
INNER JOIN NAMES on NAMES.Id=PHONE.PersonID
group by Name having count(PersonID)>=2;