How to simplify nested select in where clause? - sql

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

Related

Adding a column with a default value set to the number of occurence in another table thats already filled

Let's say i have these two tables which are already filled, and i would like to add a column nb_departments to the table Locations, which is initialized with the amount of departments in the location.
CREATE TABLE departments
(
department_id NUMBER(4) PRIMARY KEY,
department_name VARCHAR2(30) NOT NULL,
manager_id NUMBER(6),
location_id NUMBER(4) NOT NULL
);
CREATE TABLE locations
(
location_id NUMBER(4) PRIMARY KEY,
street_address VARCHAR2(40),
postal_code VARCHAR2(12),
city VARCHAR2(30),
state_province VARCHAR2(25),
country_id CHAR(2) NOT NULL
);
Could it be done using a trigger, or using alter table add column default value?
You could create a view:
CREATE VIEW vwlocations
(
location_id,
street_address,
postal_code,
city ,
state_province,
country_id,
nb_departments
) AS
SELECT l.location_id,
l.street_address,
l.postal_code,
l.city ,
l.state_province,
l.country_id,
COUNT(d.location_id) AS nb_departments
FROM locations l
JOIN departments d
ON l.location_id = d.location_id
GROUP BY l.location_id,
l.street_address,
l.postal_code,
l.city ,
l.state_province,
l.country_id
If you did want to add a column to the existing table, you can initialize your column values this way and add a trigger to manage updates / inserts / deletes thereafter:
UPDATE locations SET locations.nb_departments = (SELECT COUNT(*) FROM departments WHERE locations.location_id = departments.location_id)

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 can I SELECT multiple values from 2 tables? SQL Developer

I want to SELECT STAFF_ID, STAFF_NAME and PROJECT_ID. STAFF_ID and PROJECT_ID are linked in a table ASSIGNMENTS, but I want to show that link while also showing the STAFF_NAME for each STAFF_ID from the STAFF table. Here is my code:
CREATE TABLE PROJECT
(PROJECT_ID CHAR(5) NOT NULL,
PROJECT_NAME CHAR(20),
PROJECT_TYPE CHAR(20),
START_DATE DATE,
END_DATE DATE,
PRIMARY KEY (PROJECT_ID));
CREATE TABLE STAFF
(STAFF_ID CHAR(5) NOT NULL,
STAFF_NAME CHAR(20),
JOB_TYPE CHAR(20),
JOB_GRADE CHAR(20),
PRIMARY KEY (STAFF_ID));
CREATE TABLE ASSIGNMENTS
(ASSIGNMENT_ID CHAR(5) NOT NULL,
PROJECT_ID CHAR(5),
STAFF_ID CHAR(5),
PRIMARY KEY (ASSIGNMENT_ID),
FOREIGN KEY (PROJECT_ID) REFERENCES PROJECT(PROJECT_ID),
FOREIGN KEY (STAFF_ID) REFERENCES STAFF(STAFF_ID));
All the tables have been populated with data, and this is the query I've used so far which shows which employees work on a given project (but only with STAFF_ID, not with STAFF_NAME included):
SELECT STAFF_ID, PROJECT_ID
FROM ASSIGNMENTS
WHERE PROJECT_ID = 'B0005';
How can I show the links like this while also including the STAFF_NAME linked to the STAFF_ID from the STAFF table?
Here is some data for a row of each table:
INSERT INTO PROJECT (PROJECT_ID, PROJECT_NAME, PROJECT_TYPE, START_DATE, END_DATE)
VALUES ('B0001','BIKESHOP.COM','WEB DEVELOPMENT',TO_DATE('15/01/17','DD/MM/YY'),TO_DATE('15/02/17','DD/MM/YY'));
INSERT INTO STAFF (STAFF_ID, STAFF_NAME, JOB_TYPE, JOB_GRADE)
VALUES ('ST001','JOHN MASON','WEB DEVELOPER','1');
INSERT INTO ASSIGNMENTS (ASSIGNMENT_ID, PROJECT_ID, STAFF_ID, HARDWARE_ID, SOFTWARE_ID)
VALUES ('A0001','B0001','ST001','H0001','S0001');
What I want to show is PROJECT_ID, STAFF_ID, STAFF_NAME. Just three columns showing those 3 values.
You just need to add join to STAFF table. Something like this
SELECT STAFF.STAFF_ID, STAFF.STAFF_NAME, ASSIGNMENTS.PROJECT_ID
FROM ASSIGNMENTS
INNER JOIN STAFF ON ASSIGNMENTS.STAFF_ID = STAFF.STAFF_ID
WHERE ASSIGNMENTS.PROJECT_ID = 'B0005';
i think this select can solve your problem
select PROJECT_ID, STAFF_ID, STAFF_NAME from ASSIGNMENTS
inner join STAFF on STAFF.STAFF_ID=ASSIGNMENTS.STAFF_ID
For achieving this need, Join the three tables as next approach:
Generic Syntax:
select *
from
tableA a
inner join
tableB b
on a.common = b.common
inner join
TableC c
on b.common = c.common
so follow the next query:
select a.PROJECT_ID, b.STAFF_ID, c.STAFF_NAME
from PROJECT a inner join ASSIGNMENTS b
on a.PROJECT_ID = b.PROJECT_ID
inner join STAFF c
on c.STAFF_ID = b.STAFF_ID
Result:

SQL Queries for a database

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.