SQL Queries for a database - sql

I have an assignment on DB. I am having some problems with my queries. I am new to SQL.
Here's my tables:
CREATE TABLE Employee (
employee_name varchar(20) NOT NULL PRIMARY KEY,
street varchar(30) NOT NULL,
city varchar(20) NOT NULL
)
CREATE TABLE Works
(
employee_name varchar(20) NOT NULL,
department_name varchar(20) NOT NULL,
title varchar(10) NOT NULL,
salary int NOT NULL,
PRIMARY KEY (employee_name),
FOREIGN KEY (employee_name) REFERENCES Employee,
FOREIGN KEY (department_name) REFERENCES Department
)
CREATE TABLE Department
(
department_name varchar(20),
cityvarchar(20),
PRIMARY KEY (department_name)
)
CREATE table Manages(
employee_name varchar(20) not null,
manager_name varchar (20) not null,
PRIMARY KEY(employee_name),
FOREIGN KEY(employee_name) REFERENCES Employee
)
Find employee names who live in different cities from their department.
Here's my code:
SELECT E.employee_name
FROM Employee E
JOIN Department D
ON E.city = D.city
WHERE E.city <> 'Roma';
Find employee names who live in the same city and in the same street with their managers.
Here's my code:
SELECT E.employee_name
FROM Employee E
INNER JOIN Department D
ON E.city = D.city
INNER JOIN Manages M
ON E.employee_name = M.employee_name
WHERE city='Roma' AND street='Main street';
What am I doing wrong?
Thank you in advance! :)

Here's how you might accomplish the first task (employees who live in a city other than that of their department):
SELECT E.employee_name
FROM Employee E JOIN Works W
ON E.employee_name = W.employee_name
JOIN Department D
ON W.departent_name = D.department_name
WHERE D.city <> E.city;
In order to determine the department in which a given employee works, you need to join to the Works table.
I have to admit that the vertical partition of employee data into Employee and Works tables is a bit odd - there is no real reason for these two to be separate tables (it's a 1-1 relationship). For that matter Manages need not be a separate table either.
Hopefully this can get you on the right path for the second query.

Related

Oracle - Create A View from Multiple Tables

I am new to SQL and am trying to teach myself different aspects of Oracle as a side interest. While working on a project, it asks to create a view based on the input data from other tables. How would this normally look?
I currently have four tables:
CREATE TABLE Engineers (
EID Integer Primary Key,
LastName varchar(25),
FirstName varchar(25),
Email varchar(255),
Graddate date
);
CREATE TABLE Faculty (
FID Integer Primary Key,
LastName varchar(25),
FirstName varchar(25),
Email varchar(255),
Hiredate date
);
CREATE TABLE Classes (
CID Integer Primary Key,
Subject varchar(6),
Catalognbr varchar(6),
Title varchar(120)
);
CREATE TABLE ClassEnrollments (
EnID Integer Primary Key,
CID Integer,
FID Integer,
EID Integer,
FOREIGN KEY (CID) REFERENCES CLASSES(CID),
FOREIGN KEY (FID) REFERENCES FACULTY(FID),
FOREIGN KEY (EID) REFERENCES ENGINEERS(EID)
);
I am trying to make a view that contains the first and last name of the engineer, last name and email of the faculty member and the subject of a class for a set of inserted data into the ClassEnrollment table which looks like:
INSERT INTO ClassEnrollments
(EnID, CID, FID, EID)
Values (EnID_inc.NEXTVAL, 1, 1, 4);
What I have tried to do was:
CREATE VIEW Testers AS
SELECT e.Lastname, e.Firstname, f.Lastname, f.Email, c.Subject, c.title, en.EnID
FROM Engineers e, Faculty f, Classes c, ClassEnrollments en
WHERE e.EID = en.EID AND f.FID = en.FID AND c.CID = en.CID
ORDER By en.ENID;
However, with this I get a duplicate column name error.
Well, you are not allowed to have duplicate column names in the view. You can use the AS to rename the duplicate columns.
CREATE VIEW Testers AS
SELECT e.Lastname as EngLastname, EngFirstname as FacLastname, f.Lastname as fac_lastname, f.Email, c.Subject, c.title, en.EnID
FROM Engineers e, Faculty f, Classes c, ClassEnrollments en
WHERE e.EID = en.EID AND f.FID = en.FID AND c.CID = en.CID
ORDER By en.ENID;
Your base query would look better if you use a JOIN clause:
SELECT e.Lastname as EngLastname
,e.Firstname as EngFirstname
,f.Lastname as FacLastname
,f.Email
,c.Subject
,c.title
,en.EnID
FROM Engineers e
JOIN Faculty f
ON Faculty.FID = ClassEnrollments.FID
JOIN Classes C
ClassEnrollments.CID = Classes.CID
JOIN ClassEnrollments en
ON ClassEnrollments.EID = Engineers.EID

Insert a column into a table which tells me the count of amount of employees in each department -- postgres

I am stuck on a problem and cannot seem to understand why it is not working. I have some tables, the tables of importance are as follows:
CREATE TABLE departments
(
department_id SERIAL PRIMARY KEY,
department_name CHARACTER VARYING (30) NOT NULL,
location_id INTEGER,
FOREIGN KEY (location_id)
REFERENCES locations (location_id)
ON UPDATE CASCADE ON DELETE CASCADE
);
and
CREATE TABLE employees
(
employee_id SERIAL PRIMARY KEY,
first_name CHARACTER VARYING (20),
last_name CHARACTER VARYING (25) NOT NULL,
email CHARACTER VARYING (100) NOT NULL,
phone_number CHARACTER VARYING (20),
hire_date DATE NOT NULL,
job_id INTEGER NOT NULL,
salary NUMERIC (8, 2) NOT NULL,
manager_id INTEGER,
department_id INTEGER,
FOREIGN KEY (job_id) REFERENCES jobs (job_id)
ON UPDATE CASCADE ON DELETE CASCADE,
FOREIGN KEY (department_id) REFERENCES departments (department_id)
ON UPDATE CASCADE ON DELETE CASCADE,
FOREIGN KEY (manager_id) REFERENCES employees (employee_id)
ON UPDATE CASCADE ON DELETE CASCADE
);
First I updated the table and inserted the necessary column which was :
ALTER TABLE departments
ADD COLUMN no_of_employees numeric;
and then tried to get the count of employees in each department using this query:
SELECT department_name, COUNT(*)
FROM employees
JOIN departments ON departments.department_id = employees.department_id
GROUP BY employees.department_id, departments.department_name;
Which forks fine and produces this output:
Output
But when I try add this column into my no_of_employees column it says I am returning to many columns and is just giving me the complete wrong answers so I would appreciate any help if possible. I can't find any similar threads online.
UPDATE departments
SET no_of_employees = (SELECT COUNT(*) AS empCount
FROM employees e
JOIN departments d ON e.department_id = d.department_id
WHERE e.department_id = d.department_id
GROUP BY e.department_id)
Error: more than one row returned by a subquery used as an expression
Error Code: 21000
I always find this kind of mass update easier to do with the newer Merge statement than with Update, and this particular update case implies you will run your Count query once for each department unless the query optimizer is quite good.
Merge Into departments d
Using (Select department_id, count(*) as empcount
From employees
Group By department_id) e
On d.department_id=e.department_id
When Matched Then Update Set d.no_of_employees=e.empcount
When Not Matched Then Update Set d.no_of_employees=0
A little verbose, but it's easier to see what is going on, the data retrieval does not default to a correlated subquery, and it automatically handles the no employees case correctly.

Add join to display items based on key

I have these Postgres tables:
create table employees
(
id bigint primary key,
account_id number,
first_name varchar(150),
last_name varchar(150)
);
create table accounts
(
id bigint primary key,
account_name varchar(150) not null
);
I need to search in table employees by account_id and print result the rows which match in table accounts.id. How I can do this using JOIN?
I'm pretty sure this is what you're looking for.
SELECT a.id, a.account_name, e.first_name, e.last_name
FROM employees as e
JOIN accounts as a on a.id = e.account_id
WHERE e.account_id = 3
This will allow you to search for specific account IDs in the employee table and bring back their corresponding account table information.
You can check this with my dbfiddle here - https://www.db-fiddle.com/f/pwzwQTsHuP27UDF17eAQy4/0

SQL Query with a count condition

Here is my table structure:
CREATE TABLE CITY(
CITY_ID NUMBER(3) CONSTRAINT CITY_ID_PK PRIMARY KEY,
CITY_NAME VARCHAR2(20) CONSTRAINT CITY_NAME_NN NOT NULL);
CREATE TABLE PILOT(
PILOT_ID NUMBER(3) CONSTRAINT PILOT_ID_PK PRIMARY KEY,
LAST_NAME VARCHAR2(20) CONSTRAINT LAST_NAME_NN NOT NULL,
FIRST_NAME VARCHAR2(20) CONSTRAINT FIRST_NAME_NN NOT NULL,
CITY_ID NUMBER(3) CONSTRAINT CITY_ID_FK REFERENCES CITY(CITY_ID),
SALARY NUMBER(7,2) CONSTRAINT SALARY_CK CHECK (SALARY >= 5000 AND SALARY <= 7000));
CREATE TABLE PLANE(
PLA_ID NUMBER(2) CONSTRAINT PLANE_ID_PK PRIMARY KEY,
PLA_DESC VARCHAR2(20) CONSTRAINT PLANE_DESC_NN NOT NULL,
MAX_PASSENGER NUMBER(3),
CITY_ID NUMBER(3) CONSTRAINT PLANE_CITY_ID_FK REFERENCES CITY(CITY_ID),
CONSTRAINT MAX_PASSENGER_CK CHECK (MAX_PASSENGER <= 500));
CREATE TABLE FLIGHT(
FLIGHT_ID NUMBER(3) CONSTRAINT FLIGHT_ID_PK PRIMARY KEY,
PILOT_ID NUMBER(3) CONSTRAINT FLIGHT_PILOT_ID_FK REFERENCES PILOT(PILOT_ID),
PLA_ID NUMBER(2) CONSTRAINT FLIGHT_PLA_ID_FK REFERENCES PLANE(PLA_ID),
CITY_DEP NUMBER(3) CONSTRAINT FLIGHT_CITY_DEP_FK REFERENCES CITY(CITY_ID),
CITY_ARR NUMBER(3) CONSTRAINT FLIGHT_CITY_ARR_FK REFERENCES CITY(CITY_ID),
DEP_DATE DATE,
DEP_TIME NUMBER(4),
ARR_TIME NUMBER(4),
CONSTRAINT ARR_TIME_CK CHECK (ARR_TIME > DEP_TIME));
The question I have in this lab is to display pilots (ID and Name) who perform two or more flights out of Montreal (It is required that I use the city name in the query and not the ID)
Here is what I have come up with so far:
SELECT PILOT_ID, LAST_NAME, FIRST_NAME
FROM PILOT
JOIN FLIGHT USING (PILOT_ID)
WHERE CITY_DEP=(SELECT CITY_ID
FROM CITY
WHERE CITY_NAME='MONTREAL')
Obviously this gets me part of the answer, but it is not displaying exactly the information I need which is simply the pilots who make this fight >= 2 times.
You can use FETCH ROWS
SELECT PILOT_ID, LAST_NAME, FIRST_NAME
FROM PILOT
JOIN FLIGHT USING (PILOT_ID)
WHERE CITY_DEP=(SELECT CITY_ID
FROM CITY
WHERE CITY_NAME='MONTREAL')
FETCH FIRST 2 ROWS ONLY
Edited with new information on data structure
Understanding your goal
I believe I understand your goal to be querying pilot level data for pilots who have departed from Montreal at least twice in one day.
Query Solution
If my assumptions are true, I believe you can meet your needs by doing something similar to this:
CREATE GLOBAL TEMPORARY TABLE flight_per_day ON COMMIT PRESERVE ROWS AS
SELECT
p.pilot_id,
f.dep_date,
COUNT(CASE WHEN c.city_name = 'MONTREAL' THEN 1 ELSE NULL END) as
montreal_cnt
FROM flights f
LEFT JOIN pilot p ON p.pilot_id = f.pilot_id
LEFT JOIN city c on f.city_dep = c.city_id
GROUP BY 1, 2;
SELECT
p.pilot_id,
p.first_name,
p.last_name
FROM flight_per_day fp
LEFT JOIN pilot p ON p.pilot_id = fp.pilot_id
WHERE fp.montreal_cnt>=2
or without a temp table you could do
SELECT
p.pilot_id,
p.first_name,
p.last_name
FROM
(SELECT
p.pilot_id,
f.dep_date,
-- Find the total number of flights (COUNT) where (CASE WHEN) a flight departs from Montreal (THEN) count it otherwise (ELSE) ignore it (NULL)
COUNT(CASE WHEN c.city_name = 'MONTREAL' THEN 1 ELSE NULL END) as
montreal_cnt
FROM flights f
-- Join in pilot table to get the counts by pilot_id
LEFT JOIN pilot p ON p.pilot_id = f.pilot_id
-- Join in city table to get city_name instead of city_id
LEFT JOIN city c on f.city_dep = c.city_id
GROUP BY 1, 2) fp
LEFT JOIN pilot p ON p.pilot_id = fp.pilot_id
-- Only give me the data for pilots who have flown out of Montreal at least twice in one day
WHERE fp.montreal_cnt>=2
For each pilot, you need to count how many flights that pilot has from Montreal, and then retrieve the pilots that have 2 or more flights. This is a job for GROUP BY and HAVING.
SELECT PILOT_ID, LAST_NAME, FIRST_NAME
FROM PILOT
JOIN FLIGHT USING (PILOT_ID)
JOIN CITY ON (CITY_DEP = CITY_ID)
WHERE CITY_NAME='MONTREAL'
GROUP BY PILOT_ID, LAST_NAME, FIRST_NAME
HAVING COUNT(*) >= 2;

How to simplify nested select in where clause?

I have 4 tables EMPLOYEE, COMPANY, WORKS and MANAGES. The tables are defined as follows-
CREATE TABLE EMPLOYEE
(
EMPLOYEE_NAME VARCHAR2(50) NOT NULL PRIMARY KEY,
STREET VARCHAR2(50) NOT NULL,
CITY VARCHAR2(30) NOT NULL
);
CREATE TABLE COMPANY
(
COMPANY_NAME VARCHAR2(100) NOT NULL PRIMARY KEY,
CITY VARCHAR2(50) NOT NULL
);
CREATE TABLE WORKS
(
EMPLOYEE_NAME VARCHAR2(50) NOT NULL PRIMARY KEY REFERENCES EMPLOYEE(EMPLOYEE_NAME),
COMPANY_NAME VARCHAR2(100) NOT NULL REFERENCES COMPANY(COMPANY_NAME),
SALARY NUMBER(12,2) NOT NULL
);
CREATE TABLE MANAGES
(
EMPLOYEE_NAME VARCHAR2(50) NOT NULL PRIMARY KEY REFERENCES EMPLOYEE(EMPLOYEE_NAME),
MANAGER_NAME VARCHAR2(50) NOT NULL
);
I need to find all the employees who live in the same city as the company for which they work. So far I have done this.
SELECT EMPLOYEE_NAME AS Names
FROM EMPLOYEE
WHERE CITY = (
SELECT CITY
FROM COMPANY
WHERE COMPANY_NAME = (
SELECT COMPANY_NAME
FROM WORKS
WHERE WORKS.EMPLOYEE_NAME = EMPLOYEE.EMPLOYEE_NAME
)
);
It's working fine. But I want to know is there any simpler way to do this query?
You could use an explicit inner join instead of nested subselect
SELECT EMPLOYEE.EMPLOYEE_NAME AS Names
FROM EMPLOYEE
INNER JOIN WORKS ON WORKS.EMPLOYEE_NAME = EMPLOYEE.EMPLOYEE_NAME
INNER JOIN COMPANY ON EMPLOYEE.CITY = COMPANY.CITY
SELECT EMPLOYEE.EMPLOYEE_NAME As Names
FROM EMPLOYEE
INNER JOIN WORKS ON WORKS.EMPLOYEE_NAME = EMPLOYEE.EMPLOYEE_NAME
INNER JOIN COMPANY ON COMPANY.COMPANY_NAME = WORKS.COMPANY_NAME
WHERE COMPANY.CITY = EMPLOYEE.CITY
This is essentially what Ken White was suggesting using the inner join.
You mention a WHERE clause so you could do:
SELECT e.EMPLOYEE_NAME As Names
FROM EMPLOYEE e
WHERE e.CITY = (SELECT c.CITY
FROM Company c JOIN
Works w
ON c.COMPANY_NAME = w.COMPANY_NAME
WHERE w.EMPLOYEE_NAME = e.EMPLOYEE_NAME
);
Notes:
This assumes that employees only work for one company. Otherwise, the subquery could return multiple rows. You can handle this situation by changing the = to IN.
Names are a really bad key to use for foreign key relationships. Usually numeric ids are better. What happens if an employee or company changes names?
Table aliases make a query easier to write and to read.
select employee.employee_name
from company, employee, works
where company.company_name=works.company_name
and works.employee_name=employee.employee_name
and employee.city = company.city