Need help with some Oracle SQL Queries - sql

Here is the question posed by a professor: Find the minimum and maximum length of movies playing in each city.
And here is how I have my tables structured:
CREATE TABLE Theatres (
Name varchar2(50) not null,
City varchar2(50) not null,
State varchar2(50) not null,
Zip number not null,
Phone varchar2(50) not null,
PRIMARY KEY (Name)
);
CREATE TABLE Movies (
Title varchar2(100) not null,
Rating NUMBER not null,
Length NUMBER not null,
ReleaseDate date not null,
PRIMARY KEY (Title),
CHECK (Rating BETWEEN 0 AND 10),
CHECK (Length > 0),
CHECK (ReleaseDate > to_date('1/January/1900', 'DD/MONTH/YYYY'))
);
CREATE TABLE ShownAt (
TheatreName varchar2(50) not null,
MovieTitle varchar2(100) not null,
PRIMARY KEY (TheatreName, MovieTitle),
FOREIGN KEY (TheatreName) REFERENCES Theatres(Name),
FOREIGN KEY (MovieTitle) REFERENCES Movies(Title)
);
I've tried a few different queries, but keep getting issues. Here is what I have:
SELECT MIN(Movies.Length), MAX(Movies.Length), Theatres.Name
FROM Theatres, Movies, ShownAt
WHERE ShownAt.TheatreName = Theatres.Name AND
ShownAt.MovieTitle = Movies.Title AND
Theatres.City IN (SELECT UNIQUE City FROM Theatres);
Anybody see anything wrong? Thanks for the help!

You were pretty close I think. Just missing GROUP BY
SELECT
MIN(Movies.Length) AS Shortest,
MAX(Movies.Length) AS Longest,
Theatres.City
FROM Theatres
JOIN ShownAt ON ShownAt.TheatreName = Theatres.Name
JOIN Movies ON ShownAt.MovieTitle = Movies.Title
GROUP BY Theatres.City

I believe this would do the trick:
SELECT T.City, MIN(M.Length) AS MinLength, MAX(M.Length) AS MaxLength
FROM Movies AS M
JOIN ShownAt AS S ON S.MovieTitle = M.Title
JOIN Theatres AS T ON S.TheatreName = T.Name
GROUP BY T.City

Related

cs50 - pset7 - movies - 10.sql failed check50 test

I'm currently working on 10.sql which asked me to find all people who directed a movie that received a rating of at least 9.0
Here's the schema:
CREATE TABLE movies (
id INTEGER,
title TEXT NOT NULL,
year NUMERIC,
PRIMARY KEY(id)
);
CREATE TABLE stars (
movie_id INTEGER NOT NULL,
person_id INTEGER NOT NULL,
FOREIGN KEY(movie_id) REFERENCES movies(id),
FOREIGN KEY(person_id) REFERENCES people(id)
);
CREATE TABLE directors (
movie_id INTEGER NOT NULL,
person_id INTEGER NOT NULL,
FOREIGN KEY(movie_id) REFERENCES movies(id),
FOREIGN KEY(person_id) REFERENCES people(id)
);
CREATE TABLE ratings (
movie_id INTEGER NOT NULL,
rating REAL NOT NULL,
votes INTEGER NOT NULL,
FOREIGN KEY(movie_id) REFERENCES movies(id)
);
CREATE TABLE people (
id INTEGER,
name TEXT NOT NULL,
birth NUMERIC,
PRIMARY KEY(id)
);
Here's the code I wrote:
SELECT DISTINCT name FROM people
JOIN directors ON directors.person_id = people.id
JOIN movies ON movies.id = directors.person_id
JOIN ratings ON ratings.movie_id = movies.id
WHERE ratings.rating >= 9.0;
When I worked on sqlite3, it returned me a list of names. But it failed the check50 test with error message of 'Query did not return results'. I couldn't figure out why. Can anyone tell me what I did wrong? Many thanks
Why do you need to join on movies? Try:
SELECT
name
FROM
people
JOIN directors ON people.id = directors.person_id
JOIN ratings ON directors.movie_id = ratings.movie_id
WHERE
ratings.rating >= 9.0

Select from 3 tables with IN clause

Need to display next: all fNAME and lNAME from table username that have position of "Admin" and work in 'ABC' company (NAME_COMPANY), with IN clause.
create table company
(
CODE_COMPANY char(30),
NAME_COMPANY varchar2(30) not null,
MAIL_COMPANY varchar2(30) null,
constraint PK_CODE_COMPANY primary key (CODE_COMPANY),
);
create table USERNAME
(
NAME_USERNAME varchar2(30),
USER_LOCATION number,
fNAME varchar2 (30) not null,
lNAME varchar2 (30) not null,
PHONE_USER char(13) null,
USER_POSITION varchar2 (30),
check (USER_POSITION in('Admin', 'Superadmin', 'Technician', 'Student')),
constraint PK_NAME_USERNAME primary key (NAME_USERNAME),
constraint FK_USER_LOCATION foreign key (USER_LOCATION) references uLOCATION (LOCATION)
);
create table uLOCATION
(
LOCATION number,
CODE_COMPANY char(30),
NAME_LOCATION varchar2(30) not null,
FLOOR_LOCATION varchar2(10),
check (FLOOR_LOCATION in ('MAIN_FLOOR', '1ST FLOOR', '2ND FLOOR', '3RD FLOOR')),
constraint PK_LOCATION primary key (LOCATION),
constraint FK_CODE_COMPANY_L foreign key (CODE_COMPANY) references company (CODE_COMPANY),
);
If I understand your question correctly, you want the following query. Not sure why you would use an IN instead of a standard = however I've included both with one commented.
select
user.fName,
user.lName
from
username as user
inner join ulocation as location
on location.location = user.user_location
inner join company as company
on company.code_company = location.code_company
where
user.user_position = 'Admin'
and name_company in ('A','B','C') -- not needed if only checking for one company
-- if only one company, change to: name_company = 'ABC'
I think you are looking for:
select u.*
from username u
where u.user_position = 'admin' and
u.ulocation in (select l.location
from ulocation l join
ucompany c
on l.code_company = c.code_company
where c.name_company = 'ABC'
);

How to get the highest appearance value in one table, from another table?

So I have 3 tables referencing cars, assurance and accident.
I want to know the brand of vehicles who had the most accidents, compared to others.
I have tried a lot of ways to that, but mostly i only get or all the brands returned or the brand of the car that was registered the most, not the one that had most accidents
These are my tables
create table car(
n_veic bigint not null,
matric varchar(15) not null,
pais_matric text not null,
n_pess bigint not null,
tipo text not null,
cor text not null,
brand text not null,
modelo varchar(15),
primary key(n_veic),
unique(matric),
foreign key (n_pess) references pessoa(n_pess)
);
create table ensurance(
apolice bigint not null,
segurado bigint not null,
car bigint not null,
datai date not null,
dataf date not null,
cobertura numeric(10,2) not null,
primary key(apolice),
unique(segurado, veiculo),
foreign key (segurado) references pessoa(n_pess),
foreign key (car) references car(n_veic)
);
create table accident(
n_acid bigint not null,
pess_segura bigint not null,
veic_seguro bigint not null,
data date not null,
local varchar(255) not null,
descr text not null,
primary key(n_acid),
unique(n_acid, veic_seguro),
foreign key (pess_segura,veic_seguro) references ensurance(segurado, car)
This is what i tried
SELECT marca
FROM veiculo NATURAL JOIN acidente
GROUP BY marca
HAVING count (distinct n_veic)>=ALL
(SELECT count (distinct n_veic)
FROM veiculo NATURAL JOIN acidente
GROUP BY marca);
I think the logic is:
select c.marca, count(*) as num_acidentes
from acidente a join
car c
on a.veic_seguro = c.n_veic
group by c.marca
order by num_acidentes desc;
You can use fetch first 1 row only -- or whatever is appropriate for your database -- to get only one row.
Try this-
Note:
1. Try to avoid NATURAL JOIN and use specific column reference.
2. Rethink DISTINCT for count is really necessary or not.
SELECT TOP 1 marca, COUNT(DISTINCT n_veic)
FROM veiculo
NATURAL JOIN acidente
GROUP BY marca
ORDER BY COUNT(DISTINCT n_veic) DESC

SQL Outer Join -- Join requires 3 tables

I'm using SQL Server query designer to try and form an outer query that will return the full name and address of each insured with home policies and those without policies. My create statements are the following:
CREATE TABLE Address (
AddressID integer NOT NULL,
HouseNumber Integer NOT NULL,
Street varchar(20) NOT NULL,
CityCounty varchar(20) NOT NULL,
StateAbb char(2),
CountryAbb char(2) NOT NULL,
Zip char(5) NOT NULL,
LastUpdatedBy varchar(20) NOT NULL,
LastUpdated date NOT NULL,
CONSTRAINT PK_Address PRIMARY KEY (AddressID));
CREATE TABLE Insured(
InsuredID integer NOT NULL,
FirstName varchar(15) NOT NULL,
LastName varchar(15) NOT NULL,
MI char(1),
DateOfBirth date NOT NULL,
CreditScore integer NOT NULL,
AddressID integer NOT NULL,
DriversLicenseNumber varchar(35),
LastUpdatedBy varchar(20) NOT NULL,
LastUpdated date NOT NULL,
CONSTRAINT PK_Insured PRIMARY KEY (InsuredID),
CONSTRAINT FK_InsuredAddress FOREIGN KEY (AddressID) references Address);
CREATE TABLE Policy(
PolicyID integer NOT NULL,
EffectiveDate date NOT NULL,
TerminationDate date NOT NULL,
Amount Numeric (8,2) NOT NULL,
PolicyYear integer NOT NULL,
PolicyType char(1) NOT NULL,
InsuredID integer NOT NULL,
AddressID integer NOT NULL,
LastUpdatedBy varchar(20) NOT NULL,
LastUpdated date NOT NULL,
CONSTRAINT PK_Policy PRIMARY KEY (PolicyID),
CONSTRAINT FK_PolicyAddress FOREIGN KEY (AddressID) references Address,
CONSTRAINT FK_PolicyInsured FOREIGN KEY (InsuredID) references Insured);
CREATE TABLE Home(
PolicyID integer NOT NULL,
ExteriorType varchar(30) NOT NULL,
Alarm char(3) NOT NULL,
DistanceToFireStation integer NOT NULL,
LastUpdatedBy varchar(20) NOT NULL,
LastUpdated date NOT NULL,
CONSTRAINT PK_Home PRIMARY KEY (PolicyID),
CONSTRAINT FK_HomePolicy FOREIGN KEY (PolicyID) references Policy);
CREATE TABLE Auto(
PolicyID integer NOT NULL,
VinNumber varchar(30) NOT NULL,
Make varchar(15) NOT NULL,
Model varchar(20) NOT NULL,
MilesPerYear integer NOT NULL,
LastUpdatedBy varchar(20) NOT NULL,
LastUpdated date NOT NULL,
CONSTRAINT PK_Auto PRIMARY KEY (PolicyID),
CONSTRAINT FK_AutoPolicy FOREIGN KEY (PolicyID) references Policy);
I believe that the query requires tables address, insured, policy and an outer right or left join but I cant get SQL server to recognize this as it keeps forming an inner join and cross join. What do I need for a query that returns insureds with home policies and their addresses and insureds with no policy and their addresses?
What I've tried so far:
SELECT Insured.InsuredID, Insured.FirstName,
Insured.LastName, Address.HouseNumber,
Policy.PolicyID
FROM Address RIGHT JOIN Policy
ON Address.AddressID = Policy.AddressID
RIGHT JOIN Insured ON Policy.AddressID = Insured.AddressID
ORDER BY Insured.InsuredID
This is the most recent query that returns what I need for insureds with a home policy but for the insureds without a policy I get nulls in the address.
SELECT i.InsuredID, i.FirstName, i.MI, i.LastName,
a.HouseNumber, a.Street, a.CityCounty, a.StateAbb, a.CountryAbb, a.Zip
FROM INSURED i
LEFT JOIN (SELECT * FROM Policy WHERE PolicyType = 'H') HomePolicy on
i.InsuredID = HomePolicy.InsuredID
LEFT JOIN Address a on HomePolicy.AddressID = a.AddressID;
Could you try this query:
SELECT i.InsuredID,
i.FirstName,
i.LastName,
a.HouseNumber,
p.PolicyID
FROM insured i
LEFT JOIN policy p ON i.AddressID = p.AddressID AND p.PolicyType = 'H'
LEFT JOIN address a ON i.AddressID = a.AddressID
ORDER BY i.InsuredID;
I think the joins were in the wrong order. Does this give you what you need?
Update: Joining the Insured table to the Address table will show you addresses regardless of if they have a policy or not.
Database design seem good.I think we have minor doubts regarding "PolicyType" column. what value it hold and what is the purpose of this column.
Say PolicyType='H' it mean that it is home policy . Or other way of finding same query is to check if that policyid exists in home table.
Is this correct ?
Main query,
What do I need for a query that returns insureds with home policies
and their addresses and insureds with no policy and their addresses?
Check this script,
--insureds with home policies and their addresses
select i.InsuredID,
i.FirstName,
i.LastName
A.HouseNumber
,1 INDICATE
from Insured i
INNER JOIN policy p ON i.InsuredID = p.InsuredID
INNER JOIN [Address] A ON A.ADDRESSID=I.ADDRESSID
WHERE EXISTS(SELECT PolicyID FROM Home H WHERE h.PolicyID=P.PolicyID)
AND NOT EXISTS(SELECT PolicyID FROM [Auto] a WHERE A.PolicyID=P.PolicyID)
UNION ALL
--insureds with no policy and their addresses
select i.InsuredID,
i.FirstName,
i.LastName
,A.HouseNumber
,0 INDICATE
from Insured i
INNER JOIN [Address] A ON A.ADDRESSID=I.ADDRESSID
WHERE EXISTS(SELECT InsuredID FROM policy p WHERE i.InsuredID = p.InsuredID )
I have use "EXISTS clause" because that table column is not require in your output.

Restricting values in a table used in a FULL OUTER JOIN

This concerns Oracle SQL.
Considering the following 3 tables:
TRIP_SEGMENT:
CREATE TABLE TRIP_SEGMENT(
SEG_ID NUMBER(6) NOT NULL,
DIRECTION VARCHAR(8) NOT NULL,
DEPARTURE_LOCATION VARCHAR(50) NOT NULL,
DESTINATION VARCHAR(50) NOT NULL,
SEGMENT_PRICE NUMBER(5,2) NULL,
TRIP_ID NUMBER(6) NOT NULL,
CONSTRAINT chk_SEG_DIRECTION CHECK (DIRECTION IN ('Outbound','Inbound')),
CONSTRAINT pk_SEGMENT PRIMARY KEY (SEG_ID),
CONSTRAINT fk_TRIP_ID FOREIGN KEY (TRIP_ID) REFERENCES TRIP(TRIP_ID)
);
TRANSPORTATION:
CREATE TABLE TRANSPORTATION(
TRANSP_BOOK_ID NUMBER(6) NOT NULL,
TRANSP_PRICE NUMBER(5,2) NULL,
DEPARTURE_DATE DATE NULL,
ARRIVAL_DATE DATE NULL,
EXT_BOOK_ID NUMBER(6) NULL,
SEG_ID NUMBER(6) NOT NULL,
SERV_TYPE_ID NUMBER(3) NOT NULL,
PARTNER_ID NUMBER(3) NULL,
CONSTRAINT pk_TRANSP_BOOK_ID PRIMARY KEY (TRANSP_BOOK_ID),
CONSTRAINT fk_TRANSP_SEG_ID FOREIGN KEY (SEG_ID) REFERENCES TRIP_SEGMENT(SEG_ID),
CONSTRAINT fk_TRANSP_SERV_ID FOREIGN KEY (SERV_TYPE_ID) REFERENCES SERVICE_TYPE(SERV_TYPE_ID),
CONSTRAINT fk_TRANSP_PARTNER_ID FOREIGN KEY (PARTNER_ID) REFERENCES PARTNER(PARTNER_ID)
);
PARTNER:
CREATE TABLE PARTNER(
PARTNER_ID NUMBER(3) NOT NULL,
PARTNER_NAME VARCHAR(50) NOT NULL,
CONTACT_FNAME VARCHAR(20) NOT NULL,
CONTACT_LNAME VARCHAR(20) NOT NULL,
ADDRESS VARCHAR(100) NULL,
PHONE_NO NUMBER(20) NOT NULL,
EMAIL VARCHAR(50) NULL,
SERV_TYPE_ID NUMBER(3) NOT NULL,
CONSTRAINT pk_PARTNER_ID PRIMARY KEY (PARTNER_ID),
CONSTRAINT fk_PART_SERV_ID FOREIGN KEY (SERV_TYPE_ID) REFERENCES SERVICE_TYPE(SERV_TYPE_ID)
);
I wanted to create a FULL OUTER JOIN like this:
SELECT TS.SEG_ID, TS.DEPARTURE_LOCATION, TS.DESTINATION, P.PARTNER_NAME
FROM TRIP_SEGMENT TS
FULL OUTER JOIN TRANSPORTATION T
ON TS.SEG_ID = T.SEG_ID
FULL OUTER JOIN PARTNER P
ON T.PARTNER_ID = P.PARTNER_ID;
...but have it restricted to PARTNER.PARTNER_ID < 6. If I just add a WHERE clause at the end of the JOIN, this will restrict all the values to those where there is an association with PARTNER.PARTNER_ID < 6, therefore defeating the purpose of the FULL OUTER JOIN.
So far, I've come up with this solution:
First, create a table that only contains PARTNER.PARTNER_ID < 6 :
CREATE TABLE TRANSPORTATION_PARTNER AS SELECT * FROM PARTNER WHERE PARTNER_ID < 6;
Then, use that table in the FULL OUTER JOIN instead:
SELECT TS.SEG_ID, TS.DEPARTURE_LOCATION, TS.DESTINATION, TP.PARTNER_NAME
FROM TRIP_SEGMENT TS
FULL OUTER JOIN TRANSPORTATION T
ON TS.SEG_ID = T.SEG_ID
FULL OUTER JOIN TRANSPORTATION_PARTNER TP
ON T.PARTNER_ID = TP.PARTNER_ID;
This works fine and it demonstrates what I'm trying to achieve, however I was wondering if there is a way of doing in one single query AND using a subquery.
Thank you!
YES, just do something like :
SELECT TS.SEG_ID, TS.DEPARTURE_LOCATION, TS.DESTINATION, P.PARTNER_NAME
FROM TRIP_SEGMENT TS
FULL OUTER JOIN TRANSPORTATION T
ON TS.SEG_ID = T.SEG_ID
FULL OUTER JOIN (SELECT *
FROM PARTNER
WHERE PARTNER.PARTNER_ID < 6) P
ON T.PARTNER_ID = P.PARTNER_ID;
You are placing restrictions on the rows to be selected so you can use LEFT JOIN instead of the FULL OUTER JOIN. Try something like
SELECT TS.SEG_ID, TS.DEPARTURE_LOCATION, TS.DESTINATION, P.PARTNER_NAME
FROM TRIP_SEGMENT TS
FULL OUTER JOIN TRANSPORTATION T
ON TS.SEG_ID = T.SEG_ID
LEFT OUTER JOIN PARTNER P
ON T.PARTNER_ID = P.PARTNER_ID AND P.Partner_ID = 6;