Using UDF for value of a column postgresql - sql

Let's say I already have 2 table with movies and their ratings.
CREATE TABLE public.movies (
movieid int4 NOT NULL,
averagerating float4 NULL,
title varchar(255) NOT NULL,
"year" int4 NULL,
CONSTRAINT movies_pkey PRIMARY KEY (movieid)
);
CREATE TABLE public.ratings (
movie_id int4 NOT NULL,
user_id int4 NOT NULL,
rating float4 NOT NULL,
timestmp varchar(255) NOT NULL,
CONSTRAINT ratings_pkey PRIMARY KEY (movie_id, user_id),
CONSTRAINT fk44trpo3u915t27ybt03ib4h0o FOREIGN KEY (movie_id) REFERENCES movies(movieid),
CONSTRAINT fk7ymub8kd95i2xlklgole3i684 FOREIGN KEY (user_id) REFERENCES usrs(userid)
);
For now column averagerating in movies is empty everywhere. I want to attach my UDF average_movie_rating to this column
Something like update movies set averagerating = average_movie_rating(movies.movieid)
This function:
create or replace function average_movie_rating(movieid integer)
returns float8 as $averagerating$
declare
averagerating float8;
begin
select avg(r.rating) into averagerating from ratings r where r.movie_id = $1;
return averagerating;
end
$averagerating$ language plpgsql;
Would it be possible and if yes, how do I pass this movieid to the function?

Unless required for performance reasons, you should calculate that value when you select from the database. If you have to persist it, use a trigger on ratings that modifies movies.

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

How to find records with same values within a subquery that i need to order

I need to write a SQL query to list all movies released in 2010 and their ratings, in descending order by rating. For movies with the same rating i need to order them alphabetically by title. I`m stuck at the part with the movies with the same rating. Thanks !
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)
);

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 not exists in another table, but count is higher than 3 in another table

I have the following tables that have profiles and list of photos (in the photo table) for each profile. I also have a service table and a used table, I want a query that would return the profile id which does not belong in the used service and also has more than 3 photos in the photo table
The profile table
CREATE TABLE public.profile
(
id integer NOT NULL DEFAULT nextval('profile_id_seq'::regclass),
name text COLLATE pg_catalog."default" NOT NULL,
birthday timestamp with time zone NOT NULL,
CONSTRAINT profile_id PRIMARY KEY (id)
)
The photo table
CREATE TABLE public.photo
(
id integer NOT NULL DEFAULT nextval('photo_id_seq'::regclass),
image bytea NOT NULL,
image_id text COLLATE pg_catalog."default" NOT NULL,
order_count smallint NOT NULL,
profile_id bigint NOT NULL,
CONSTRAINT photo_id PRIMARY KEY (id),
CONSTRAINT photo_profile_id_fkey FOREIGN KEY (profile_id)
REFERENCES public.profile (id) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE CASCADE
)
the service table
CREATE TABLE public.service
(
id integer NOT NULL DEFAULT nextval('service_id_seq'::regclass),
name text COLLATE pg_catalog."default" NOT NULL,
CONSTRAINT service_id PRIMARY KEY (id)
)
the used table
CREATE TABLE public.used
(
id integer NOT NULL DEFAULT nextval('used_id_seq'::regclass),
service_id bigint NOT NULL,
profile_id bigint NOT NULL,
insert_timestamp timestamp with time zone NOT NULL DEFAULT now(),
CONSTRAINT used_id PRIMARY KEY (id),
CONSTRAINT used_profile_id_fkey FOREIGN KEY (profile_id)
REFERENCES public.profile (id) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE CASCADE,
CONSTRAINT used_service_id_fkey FOREIGN KEY (service_id)
REFERENCES public.service (id) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE CASCADE
)
use exists and not exists
select p.* from profile p
where exists ( select 1 from photo ph where ph.profile_id =p.id
having count (distinct image_id )=3
)
and not exists ( select 1 from used u where u.profile_id =p.id)
I would go for:
select p.profile_id
from photo p
where not exists (select 1
from used u
where u.profile_id = p.profile_id
)
group by p.profile_id
having count(*) >= 3;
If you just need the profile_id, then the profiles table is not needed.

SQL Logic and Aggregate Issue

I have the following problem to solve in SQL :
d) A query that provides management information on take up of the various types of activities on offer. For each type of activity, the query should show the total number of individuals who took that type of activity and the average number of individuals taking each type of activity.
Here are my tables :
CREATE TABLE accommodations
(
chalet_number int PRIMARY KEY,
chalet_name varchar(40) NOT NULL,
no_it_sleeps number(2) NOT NULL,
indivppw number(4) NOT NULL
)
CREATE TABLE supervisors
(
supervisor_number int PRIMARY KEY,
supervisor_forename varchar(30) NOT NULL,
supervisor_surname varchar(30) NOT NULL,
mobile_number varchar(11) NOT NULL
)
CREATE TABLE visitors
(
visitor_ID int PRIMARY KEY,
group_ID int NOT NULL,
forename varchar(20) NOT NULL,
surname varchar(20) NOT NULL,
dob date NOT NULL,
gender varchar(1) NOT NULL
)
CREATE TABLE activities
(
activity_code varchar(10) PRIMARY KEY,
activity_title varchar(20) NOT NULL,
"type" varchar(20) NOT NULL
)
CREATE TABLE "groups"
(
group_ID int PRIMARY KEY,
group_leader varchar(20) NOT NULL,
group_name varchar(30)
number_in_group number(2) NOT NULL
)
CREATE TABLE bookings
(
group_ID int NOT NULL,
start_date date NOT NULL,
chalet_number int NOT NULL,
no_in_chalet number(2) NOT NULL,
start_date date NOT NULL,
end_date date NOT NULL,
CONSTRAINT bookings_pk PRIMARY KEY(group_ID, chalet_number));
CREATE TABLE schedule
(
schedule_ID int PRIMARY KEY,
activity_code varchar(10) NOT NULL,
time_of_activity number(4,2) NOT NULL,
am_pm varchar(2) NOT NULL,
"date" date NOT NULL
)
CREATE TABLE activity_bookings
(
visitor_ID int NOT NULL,
schedule_ID int NOT NULL,
supervisor_number int NOT NULL,
comments varchar(200),
CONSTRAINT event_booking_pk PRIMARY KEY(visitor_ID, schedule_ID));
ALTER TABLE visitors
ADD FOREIGN KEY (group_ID)
REFERENCES "groups"(group_ID)
ALTER TABLE Schedule
ADD FOREIGN KEY (activity_code)
REFERENCES activities(activity_code)
ALTER TABLE bookings
ADD FOREIGN KEY (group_ID)
REFERENCES "groups"(group_ID)
ALTER TABLE bookings
ADD FOREIGN KEY (chalet_number)
REFERENCES accommodations(chalet_number)
ALTER TABLE activity_bookings
ADD FOREIGN KEY (visitor_ID)
REFERENCES visitors(visitor_ID)
ALTER TABLE activity_bookings
ADD FOREIGN KEY (schedule_ID)
REFERENCES schedule(schedule_ID)
ALTER TABLE activity_bookings
ADD FOREIGN KEY (supervisor_number)
REFERENCES supervisors(supervisor_number)
I have the following solution:
SELECT activities."type", 'overalltotal' AS OT, ('overalltotal' / 'activities') AS AVG
FROM activities, schedule
WHERE 'overalltotal' = (SELECT SUM(COUNT(schedule_ID))
FROM activities, schedule
WHERE schedule.activity_code = activities.activity_code
GROUP BY activities."type"
)
AND 'activities' = (SELECT COUNT(DISTINCT activities."type")
FROM activities
)
AND schedule.activity_code = activities.activity_code
GROUP BY activities."type";
I have implemented sample data and code to check the variables above:
SELECT SUM(COUNT(schedule_ID))
FROM activities, schedule
WHERE schedule.activity_code = activities.activity_code
GROUP BY activities."type";
Result : 20
SELECT COUNT(DISTINCT activities."type")
FROM activities;
Result : 5
However when running the code :
ORA-01722: invalid number
01722. 00000 - "invalid number"
*Cause:
*Action:
EDIT:
Using Dave's Code i have the following output:
Snowboarding 15
sledding 19
Snowmobiling 6
Ice Skating 5
Skiing 24
How would i do the final part of the question?
and the average number of individuals taking each type of activity.
You must use double quotes around column names in Oracle, not single quotes. For example, "overalltotal". Single quotes are for text strings, which is why you're getting an invalid number error.
EDIT: This is probably the type of query you want to use:
SELECT activities."type", COUNT(*) AS total, COUNT(*)/(COUNT(*) OVER ()) AS "avg"
FROM activities a
JOIN schedule s ON a.activity_code=s.activity_code
JOIN activity_bookings ab ON s.schedule_ID=ab.schedule_ID
GROUP BY activities."type";
Basically, because each activity booking has one visitor id, we want to get all the activity bookings for each activity. We have to go through schedule to do that. They we group the rows by the activity type and count how many activity bookings we have for each type.