I cannot execute this query SQL - sql

Show how to define the view student grades (ID, GPA) giving the grade-point average of each student; recall that we used a relation grade_points (grade, points) to get the numeric points associated with a letter grade. Make sure your view definition correctly handles the case of null values for the grade attribute of the takes relation.
create view student_grades(ID, GPA) as
select ID, credit_ points / decode(credit sum, 0, NULL, credit_sum)
from ((select ID, sum(decode(grade, NULL, 0, credits)) as credit_sum,
sum(decode(grade, NULL, 0, credits*points)) as credit_points
from(takes natural join course) natural left outer join grade points group by ID)
union
select ID, NULL
from student
where ID not in (select ID from takes));
Can someone please correct this code?

I think you decode() and sum() parameters a mixed up !! Decode ask for 2 arguments
https://www.w3resource.com/mysql/encryption-and-compression-functions/decode().php

Related

How to join Views with aggregate functions?

My problem:
In #4, I'm having trouble joining two Views because the other has an aggregate function. Same with #5
Question:
Create a view name it as studentDetails, that would should show the student name, enrollment date, total price per unit and subject description of students who are enrolled on the subject Science or History.
Create a view, name it as BiggestPrice, that will show the subject id and highest total price per unit of all the subjects. The view should show only the highest total price per unit that are greater than 1000.
--4.) Create a view name it as studentDetails, that would should show the student name,
-- enrollment date the total price per unit and subject description of students who are
-- enrolled on the subject Science or History.
CREATE VIEW StudentDetails AS
SELECT StudName, EnrollmentDate
--5.) Create a view, name it as BiggestPrice, that will show the subject id and highest total
-- price per unit of all the subjects. The view should show only the highest total price per unit
-- that are greater than 1000.
CREATE VIEW BiggestPrice AS
SELECT SubjId, SUM(Max(Priceperunit)) FROM Student, Subject
GROUP BY Priceperunit
Here is my table:
CREATE TABLE Student(
StudentId char(5) not null,
StudName varchar2(50) not null,
Age NUMBER(3,0),
CONSTRAINT Student_StudentId PRIMARY KEY (StudentId)
);
CREATE table Enrollment(
EnrollmentId varchar2(10) not null,
EnrollmentDate date not null,
StudentId char(5) not null,
SubjId Number(5) not null,
constraint Enrollment_EnrollmentId primary key (EnrollmentId),
constraint Enrollment_StudentId_FK foreign key (StudentId) references Student(StudentId),
constraint Enrollment_SubjId_Fk foreign key (SubjId) references Subject(SubjId)
);
Create table Subject(
SubjId number(5,0) not null,
SubjDescription varchar2(200) not null,
Units number(3,0) not null,
Priceperunit number(9,0) not null,
Constraint Subject_SubjId_PK primary key (SubjId)
);
Since this appears to be a homework question.
You need to use JOINs. Your current query:
CREATE VIEW StudentDetails AS
SELECT StudName, EnrollmentDate
Does not have a FROM clause and the query you have for question 5 uses the legacy comma join syntax with no WHERE filter; this is the same as a CROSS JOIN and will connect every student to every subject and is not what you want.
Don't use the legacy comma join syntax and use ANSI joins and explicitly state the join condition.
SELECT <expression list>
FROM student s
INNER JOIN enrollment e ON ...
INNER JOIN subject j ON ...
Then you can fill in the ... based on the relationships between the tables (typically the primary key of one table = the foreign key of another table).
Then for the <expression list> you need to include the columns asked for in the question: student name and enrolment date and subject name would just be those columns from the appropriate tables; and total price-per-unit (which I assume is actually total-price-per-subject) would be a calculation.
Then for the last part of question 4.
who are enrolled on the subject Science or History.
Add a WHERE filter to only include rows for those subjects.
For question 5, you do not need any JOINS as the question only asks about details in the SUBJECT table.
You need to add a WHERE filter to show "only the highest total price per unit that are greater than 1000". This is a simple multiplication and then you can filter by comparing if it is > 1000.
Then you need to limit the query to return only the row with the "highest total price per unit of all the subjects". From Oracle 12, this would be done with an ORDER BY clause in descending order of total price and then using FETCH FIRST ROW ONLY or FETCH FIRST ROW WITH TIES.
Not sure if i get it fully, but i think its this :
Notes:
Always use Id's to filter records:
where su.SubjId in (1,2)
You can find max record using max() at subquery and join it with main query like this :
where su2.SubjId = su.SubjId
You cannot use alias as filter so you can filter it like:
( su.Units * su.Priceperunit ) > 1000
CREATE VIEW StudentDetails AS
select s.StudName,
e.EnrollmentDate,
su.SubjDescription,
su.Units * su.Priceperunit TotalPrice
from student s
inner join Enrollment e
on e.StudentId = s.StudentId
inner join Subject su
on su.SubjId = e.SubjId
where su.SubjId in (1,2)
CREATE VIEW BiggestPrice AS
select su.SubjId, ( su.Units * su.Priceperunit ) TotalPrice
from Subject su
where ( su.Units * su.Priceperunit ) =
(
select max(su2.Units * su2.Priceperunit)
from Subject su2
where su2.SubjId = su.SubjId
)
and ( su.Units * su.Priceperunit ) > 1000

Accessing to total number in each second level(Postgres Hierarchical Query Practice)

I was practicing on Postgres and stuck on a point that I couldn't find a way to achieve. I have a simple database which are the attributes:
CREATE TABLE public.department
(
"deptId" integer NOT NULL PRIMARY KEY,
name character varying(30) COLLATE pg_catalog."default" NOT NULL,
"parentId" integer,
"numEmpl" integer NOT NULL,
CONSTRAINT "department_parentId_fkey" FOREIGN KEY ("parentId")
REFERENCES public.department ("deptId") MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION
)
and then I have some data in the table. Short example is
insert into department values (1, 'Headquarter', 1, 10);
insert into department values (2, 'Sales', 1, 15);
insert into department values (3, 'Logistics', 1, 25);
...
I was trying to get the total number of people who are employeed in each second level department.
I am able to get the total number of employeed people in each department but according to my search in the internet this is possible with "Hierarchical Queries". Currently, I am using
parentId=1
while querying.
Any solutions for this? Thank you.
Here is one option:
with recursive cte as (
select deptid as rootid, deptid from department where parentid = 1 and deptid <> 1
union all
select c.rootid, d.deptid
from cte c
inner join department d on d.parentid = c.deptid and d.deptid <> 1
)
select rootid, count(*) cnt from cte group by rootid

Flattening columns into rows

I am selecting some columns from a table and adding some new columns (such as birthday and school address) with default values l
select
s.id,
s.address,
Birthday as null,
School_Address as 'Mumbai'
from student s;
But I am getting an error. Can someone tell me the right approach to assign these values.
You're doing it the wrong way round. First comes the value then the alias not the other way.
SELECT s.id,
s.address,
NULL birthday,
'Mumbai' school_address
FROM student s;

Is it possible to just have 'SELECT' clause with subqueries WITHOUT 'FROM' clause?

Is it possible to have the following SQL form:
SELECT
(
(SUBQUERY1) -
(SUBQUERY2)
)
Assume that these two subqueries are correct. It looks wired to me since there is no FROM in the first query.
Thanks!
Just a simple example from a university class:
Given relations:
Students (id, gpa) where id is the key
Enrolls (id, number, term) where all three attributes make up the key
Find the difference between the average GPA of the students who take Class1 and that of the students who take Class2. Assume that there are students who took either more than once.
Is the following SQL query OK?
SELECT
(SELECT AVG(gpa)
FROM Students
WHERE id IN (SELECT id
FROM Enrolls
WHERE number = "CLASS1")
)
-
(SELECT AVG(gpa)
FROM Students
WHERE id IN (SELECT id
FROM Enrolls
WHERE number = "CLASS2")
)
This depends on the DBMS you use. On SQL Server, this is valid. On Oracle, a SELECT needs to have a FROM, but you can use dual which is a readonly one row one column table provided by the system if your SELECT works without a FROM clause (like in your example) and only needs the FROM clause to make the SQL parser happy.
Yes, it is possible to have this construct. Consider this example (MySQL)
mysql> select ((select length('abcde')) - (select length('xyz')));
+-----------------------------------------------------+
| ((select length('abcde')) - (select length('xyz'))) |
+-----------------------------------------------------+
| 2 |
+-----------------------------------------------------+
Yes, as long as the subqueries return one value each.
Compare:
Select (select 1), (select 2)
or
Select (1), (2)
, both of which work,
to:
Select (select 1, 3), (select 2, 5)
which returns the following error in SQL Server:
Msg 116, Level 16, State 1, Line 1 Only one expression can be
specified in the select list when the subquery is not introduced with
EXISTS.

Calculating average for each student

Suppose I have the following tables:
-- table student
create table Student(
num int primary key identity,
firstName varchar(30) not null,
lastName varchar(30)
)
-- table module
create table Module(
code int primary key identity,
name varchar(30) not null,
coefficient int not null)
-- table notation
create table Notation(
stud int references student,
Mod int references Module,
DateExam datetime default getdate(),
Note float check (Note between 0 and 20)
primary key(stud , Mod ))
What I want is to display student names, the student num and averages, ranked from best to worst.
Update:
average = sum (ni*ci)/ sum (ci); c: coefficient. n: note
Don't think you need module. and this assumes note is the field you want to average.
SELECT FirstName, LastName, Note, avg(note) over (partition by s.Num) AvgNote
FROM Student S
LEFT JOIN Notation N
on S.Num = N.Stud
GROUP BY FirstName, LastName, S.Num
ORDER BY as AvgNote Desc
Also, float as a data type when dealing with grades is a bad idea. Float is imprecise by design to support a smaller datastorage footprint. This doesn't matter when you're dealing with scientific notation and precision isn't necessary, but in this case I think decimal would be a better choice.
This gets both the note for each module for each student, and their average across all modules
select s1.FirstName, s1.LastName, m2.name as module_name, n3.Note, x1.av_note
from student s1
left join notation n3
on n3.stud = s1.num
left join module m2
on m2.code = n3.mod
left join
(
select stud, avg(note) as av_note
from notation
group by stud
) x1
on s1.num = x1.stud
order by av_note, lastname desc
Here is my solution:
select tab.num,tab.firstName, sum(noteMultiCoef)/sum(coefficient) as average from
(select st.num,st.firstName, coefficient, note*coefficient as noteMultiCoef
from notation nt, student st, Module md
where st.num= nt.stud
and nt.code = md.mod) tab
group by tab.num, tab.firstName
order by average desc