SQL command not properly ended - don't know why - sql

I'm trying to work through this SQL query, and keep getting stuck with a
SQL command not properly ended
statement.
The exact error is:
AND NOT PROJECT.dnum = EMPLOYEE.dno)) > 10
*
ERROR at line 4:
ORA-00933: SQL command not properly ended
Here is the schema:
CREATE TABLE EMPLOYEE (
fname VARCHAR(20),
minit CHAR(1),
lname VARCHAR(20),
ssn INT NOT NULL,
bdate VARCHAR(20),
address VARCHAR(40),
sex CHAR(1) check (sex IN ('M', 'F')),
salary INT CHECK (salary > 20000),
superssn INT,
dno INT,
PRIMARY KEY (ssn)
);
CREATE TABLE DEPARTMENT (
dname VARCHAR(20),
dnumber INT NOT NULL,
mgr_ssn INT,
mgr_start_date VARCHAR(20),
PRIMARY KEY (dnumber)
);
CREATE TABLE DEPTLOCATIONS (
dnumber INT NOT NULL,
dlocation VARCHAR(20) NOT NULL check (dlocation IN ('BELLAIRE', 'SUGARLAND', 'HOUSTON', 'STAFFORD')),
PRIMARY KEY (dnumber, dlocation)
);
CREATE TABLE PROJECT (
pname VARCHAR(20) NOT NULL,
pnumber INT NOT NULL,
plocation VARCHAR(20) CHECK (plocation IN ('BELLAIRE', 'SUGARLAND', 'HOUSTON', 'STAFFORD')),
dnum INT NOT NULL,
PRIMARY KEY (pnumber)
);
CREATE TABLE WORKS_ON (
essn INT NOT NULL,
pno INT NOT NULL,
hours FLOAT check (hours >= 5 AND hours <= 40),
PRIMARY KEY (essn, pno)
);
CREATE TABLE DEPENDENT (
essn INT NOT NULL,
dependent_name VARCHAR(40) NOT NULL,
sex CHAR(1) check (sex IN ('M', 'F')),
bdate VARCHAR(20),
relationship VARCHAR(20) CHECK (relationship IN ('SPOUSE', 'SON', 'DAUGHTER')),
PRIMARY KEY (essn, dependent_name)
);
I'm attempting to find employees whose salaries are
higher than the average salary of all the employees in the same department and who have two
or more dependents; then, for each found employee, I need to check if the employee works more than
a total of 10 hours a week on projects not controlled by their home department and
print the employee’s full name if so, along with salary, the number of dependents, the
total number of hours on projects controlled by the home department, and the total number of
hours on projects controlled by the other (i.e., non-home) departments
Here is my SQL query:
SELECT concat(
concat
(EMPLOYEE.fname, ' '|| EMPLOYEE.minit),
' '||EMPLOYEE.lname) as NAME,
EMPLOYEE.ssn,
(select count(*) from dependent where Employee.ssn=dependent.essn) AS Num_Dependents,
(SELECT SUM(hours) FROM EMPLOYEE
JOIN WORKS_ON ON (EMPLOYEE.ssn = WORKS_ON.essn)
JOIN PROJECT ON (PROJECT.pnumber=WORKS_ON.pno)
WHERE PROJECT.dnum = EMPLOYEE.dno)
AS HOME_HOURS,
(SELECT SUM(hours) FROM EMPLOYEE
JOIN WORKS_ON ON (EMPLOYEE.ssn = WORKS_ON.essn)
JOIN PROJECT ON (PROJECT.pnumber=WORKS_ON.pno)
WHERE NOT PROJECT.dnum = EMPLOYEE.dno) AS NOT_HOME_HOURS,
(SELECT EMPLOYEE.salary FROM EMPLOYEE WHERE
EMPLOYEE.salary > (SELECT AVG(salary) FROM EMPLOYEE WHERE EMPLOYEE.dno = dno))
AND
(SELECT SUM(hours) FROM EMPLOYEE
JOIN WORKS_ON ON (EMPLOYEE.ssn = WORKS_ON.essn)
JOIN PROJECT ON (PROJECT.pnumber=WORKS_ON.pno)
AND NOT PROJECT.dnum = EMPLOYEE.dno)) > 10
AND (SELECT COUNT(*) FROM DEPENDENT WHERE essn = ssn) >= 2;
I know it probably isn't formatted as nicely as it could be, but I've been at this for hours and nothing seems to get rid of that
SQL command not properly ended
error which gets thrown when I run the query. Any help or tips would be much appreciate! Thanks all.

Your sql looks weird. Boiling it down to a simple representation, its structure appears to be something like (hopefully I understood the parentheses layout correctly) this:
SELECT
(Query),
(Query),
(Query) AND (Query)
) dangling unrelated fragment
Oracle needs a FROM, and it can't cope with a select list that contains an AND like that..
I know it probably isn't formatted as nicely as it could be
Absolutely 100% the problem.. By not laying out your SQL nicely you've become completely lost in the spaghetti, and you've ended up with an SQL that doesn't have a full set of matched brackets and is hence suffering from syntax errors
Here's what your sql looks like if I put it into a text editor and balance the parentheses up so each line that looks like (query here) as alias, occupies a single line:
You can see at the end an extra bracket creeps in and ends up wrecking things?
(Note for the "don't screenshot code" purists - did it deliberately to highlight the problem, as if it had been posted as text it would probably wrap and lose the impact)

In your code here:
( SELECT EMPLOYEE.salary FROM EMPLOYEE WHERE
EMPLOYEE.salary > (SELECT AVG(salary) FROM EMPLOYEE WHERE EMPLOYEE.dno = dno) )
AND
(SELECT SUM(hours) FROM EMPLOYEE
JOIN WORKS_ON ON (EMPLOYEE.ssn = WORKS_ON.essn)
JOIN PROJECT ON (PROJECT.pnumber=WORKS_ON.pno)
AND NOT PROJECT.dnum = EMPLOYEE.dno)) > 10
AND (SELECT COUNT(*) FROM DEPENDENT WHERE essn = ssn) >= 2;
The opening and closing brackets (in bold) completes this Select statement without considering the select statement where you're summing up the hours. Hence, the overall structure while using a WHERE clause is affected.

Related

Update and renew data based on data in other tables

There are 3 tables student, course, and takes as following
CREATE TABLE student
(
ID varchar(5),
name varchar(20) NOT NULL,
dept_name varchar(20),
tot_cred numeric(3,0) CHECK (tot_cred >= 0),
PRIMARY KEY (ID),
FOREIGN KEY (dept_name) REFERENCES department
ON DELETE SET NULL
)
CREATE TABLE takes
(
ID varchar(5),
course_id varchar(8),
sec_id varchar(8),
semester varchar(6),
year numeric(4,0),
grade varchar(2),
PRIMARY KEY (ID, course_id, sec_id, semester, year),
FOREIGN KEY (course_id, sec_id, semester, year) REFERENCES section
ON DELETE CASCADE,
FOREIGN KEY (ID) REFERENCES student
ON DELETE CASCADE
)
CREATE TABLE course
(
course_id varchar(8),
title varchar(50),
dept_name varchar(20),
credits numeric(2,0) CHECK (credits > 0),
PRIMARY KEY (course_id),
FOREIGN KEY (dept_name) REFERENCES department
ON DELETE SET NULL
)
tot_cred column data in the student table now is assigned with random values (not correct), I want to perform the query that updates and renews those data based on the course's grade each student has taken. For those students who received F grade will be excluded and those who didn't take any course will be assigned 0 as tot_cred.
I came up with two approaches, one is
UPDATE student
SET tot_cred = (SELECT SUM(credits)
FROM takes, course
WHERE takes.course_id = course.course_id
AND student.ID = takes.ID
AND takes.grade <> 'F'
AND takes.grade IS NOT NULL)
This query meets all my needs, but for those students who didn't take any course, it does assign NULL value instead of 0.
The second is using case when
UPDATE student
SET tot_cred = (select sum(credits)
case
when sum(credits) IS NOT NULL then sum(credits)
else 0 end
FROM takes as t, course as c
WHERE t.course_id = c.course_id
AND t.grade<>'F' and t.grade IS NOT NULL
)
But it assigned 0 to all students. Is any way to achieve the above requirement?
If the 1st query meets your requirement and the only problem is that it returns NULL for the students that did not take any course then the easiest solution would be to use instead of SUM() aggregate function the function TOTAL() which will return 0 instead of NULL:
UPDATE student AS s
SET tot_cred = (
SELECT TOTAL(c.credits)
FROM takes t INNER JOIN course c
ON t.course_id = c.course_id
WHERE t.ID = s.ID AND t.grade <> 'F' AND t.grade IS NOT NULL
);
The same could be done with COALESCE():
SELECT COALESCE(SUM(credits), 0)...
Also, use a proper join with an ON clause and aliases for the tables to improve readability.

For each ‘CptS’ course, find the percentage of the students who failed the course. Assume a passing grade is 2 or above

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) );
So far I've been able to create two seperate queries, one that counts the number of people who failes. And the other counts the number of people who passed. I'm having trouble combining these to produce the number of people passed / number of people failed. For each course.
SELECT course.courseno, COUNT(*) FROM course inner join enroll on enroll.courseno = course.courseno
WHERE course.courseno LIKE 'CptS%' and enroll.grade < 2
GROUP BY course.courseno;
SELECT course.courseno, COUNT(*) FROM course inner join enroll on enroll.courseno = course.courseno
WHERE course.courseno LIKE 'CptS%' and enroll.grade > 2
GROUP BY course.courseno;
The end result should look something like
courseno passrate
CptS451 100
CptS323 100
CptS423 66
You can do a conditional average for this:
select
courseno,
avg(case when grade > 2 then 100.0 else 0 end) passrate
from enroll
where courseno like 'CptS%'

Update table column using sum()

I am trying to do two thing with my SQL code. I want it to copy data from another column (hours) , that’s located in another table (works- table), and place it into a new column (numHours) in a different table (Employee- table). However, I want it to use the sum of the hours worked for each employee id (eid) and then place that sum into the new column.
This is what I wrote, but two things are wrong. When I execute just the select statement every employee id has the same number of hours worked. When I run the whole statement I get this error
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
The statement has been terminated.
UPDATE Employee
SET numHours= (SELECT sum(w.hours) AS totalHours From works w, Employee e WHERE
numHours IS NULL AND e.eid = w.eid Group by w.eid);
These are my tables
CREATE TABLE Employee(
eid INT,
ename VARCHAR(30),
age INT,
salary INT,
CONSTRAINT Pk_key_eid PRIMARY KEY (eid)
);
CREATE TABLE Department(
did INT,
dname VARCHAR(30),
budget int,
managerid INT,
CONSTRAINT Validate_managerid CHECK(managerid < 1000),
CONSTRAINT Pk_key_did PRIMARY KEY (did)
);
CREATE TABLE Works(
eid INT,
did INT,
hours INT,
CONSTRAINT fk_key_eid FOREIGN KEY (eid) REFERENCES Employee (eid) ON DELETE CASCADE,
CONSTRAINT fk_key_Did FOREIGN KEY (did) REFERENCES Department (did) ON DELETE CASCADE
);
How would I properly code this?
I believe you want this:
UPDATE Employee
SET numHours= (SELECT sum(w.hours) From works w WHERE employee.eid = w.eid)
WHERE numHours IS NULL;
Notes:
The condition on numHours belongs in the UPDATE, not the subquery.
You want a correlated query, not a full join (and don't use commas in the FROM clause ever!).
There is no need for a column alias in the subquery.
There is no need for GROUP BY in the subquery.
Use JOIN and UPDATE:
UPDATE E
SET E.numHours=H.totalHours
FROM Employee E JOIN
(Select sum(hours) AS totalHours,eid
From works
Group by eid)H on H.eid=E.eid
WHERE E.numHours IS NULL
Explanation:
Inner query will select Total hours for each employee. Then use it to update Employee table.

SQL DML Query AVG and COUNT

I am beginner at SQL and I am trying to create a query.
I have these tables:
CREATE TABLE Hospital (
hid INT PRIMARY KEY,
name VARCHAR(127) UNIQUE,
country VARCHAR(127),
area INT
);
CREATE TABLE Doctor (
ic INT PRIMARY KEY,
name VARCHAR(127),
date_of_birth INT,
);
CREATE TABLE Work (
hid INT,
ic INT,
since INT,
FOREIGN KEY (hid) REFERENCES Hospital (hid),
FOREIGN KEY (ic) REFERENCES Doctor (ic),
PRIMARY KEY (hid,ic)
);
The query is: What is the average in each country of the number of doctors working in hospitals of that country (1st column: each country, 2nd column: average)? Thanks.
You first need to write a query that counts the doctors per hospital
select w.hid, count(w.ic)
from work w
group by w.hid;
Based on that query, you can retrieve the average number of doctors per country:
with doctor_count as (
select w.hid, count(w.ic) as cnt
from work w
group by w.hid
)
select h.country, avg(dc.cnt)
from hospital h
join doctor_count dc on h.hid = dc.hid
group by h.country;
If you have an old DBMS that does not support common table expressions the above can be rewritten as:
select h.country, avg(dc.cnt)
from hospital h
join (
select w.hid, count(w.ic) as cnt
from work
group by w.hid
) dc on h.hid = dc.hid;
Here is an SQLFiddle demo: http://sqlfiddle.com/#!12/9ff79/1
Btw: storing date_of_birth as an integer is a bad choice. You should use a real DATE column.
And work is a reserved word in SQL. You shouldn't use that for a table name.

Join multiple tables, including one table twice, and sort by counting a group

I am an amateur just trying to finish his last question of his assignment (it is past due at this point, just looking for understanding) I sat and shot attempts at this for almost 5 hours now across two days, and have had no success.
I have tried looking through all the different types of joins, couldn't get grouping to work (ever) and have had little luck with the sorting as well. I can do all of these things one at a time, but the difficulty here was getting all of these things to work in union.
This is the question:
Write a SQL query to retrieve a list that has (source city, source code, destination city,
destination code, and number-of-flights) for all source-dest pairs with at least 2 flights. Order
by the number_of_flights. Note that the “dest”, and “source” attributes in the “flights” table
are both referenced to the “airportid” in the “airports” table.
Here are the tables I have to work with (also came with about 3000 lines of dummy entries)
create table airports (
airportid char(3) primary key,
city varchar(20)
);
create table airlines (
airlineid char(2) primary key,
name varchar(20),
hub char(3) references airports(airportid)
);
create table customers (
customerid char(10) primary key,
name varchar(25),
birthdate date,
frequentflieron char(2) references airlines(airlineid)
);
create table flights (
flightid char(6) primary key,
source char(3) references airports(airportid),
dest char(3) references airports(airportid),
airlineid char(2) references airlines(airlineid),
local_departing_time date,
local_arrival_time date
);
create table flown (
flightid char(6) references flights(flightid),
customerid char(10) references customers,
flightdate date
);
The first problem I ran in to was outputting airports.city twice in the same query but with different results. Not only that, but no matter what I tried when grouping I would always get the same result:
Not a GROUP BY expression
Normally I have fun trying to piece these together, but this has been frustrating. Help!
select source.airportid as source_airportid,
source.city source_city,
dest.airportid as dest_airportid,
dest.city as dest_city,
count(*) as flights
from flights
inner join airports source on source.airportid = flights.source
inner join airports dest on dest.airportid = flights.dest
group by
source.airportid,
source.city,
dest.airportid,
dest.city
having count(*) >= 2
order by 5;
Have you tried a subquery?
SELECT source_airports.city,
source_airports.airportid,
dest_airports.city,
dest_airports.airportid,
x.number_of_flights
FROM
(
SELECT source, dest, COUNT(*) as number_of_flights
FROM flights
GROUP BY source, dest
HAVING COUNT(*) > 1
) as x
INNER JOIN airports as dest_airports
ON dest_airports.airportid = x.dest
INNER JOIN airports as source_airports
ON source_airports.airportid = x.source
ORDER BY x.number_of_flights ASC