I have two tables
create table AUTHORS
(
id integer primary key,
name varchar(30)
)
create table BOOKS
(
id integer primary key,
name varchar(30),
year integer,
sold_copies integer,
author_id integer references AUTHORS (id)
)
How would I create a table with authors in descending order per sold copies of books per year in pgAdmin4 (PostgreSQL) ?
im a beginner and I am not sure how to go about this
I don't see why you would need to create a new table. A simple SELECT will do (maybe stored inside a view)
select a.id, a.name, b.year, sum(b.sold_copies) as total_copies
from author a
join books b on b.author_id = a.id
group by a.id, a.name, b.year
order by sum(b.sold_copies) desc;
Related
I am trying to find max count of product. The result must only display the brands which have max number of products in it. Can anyone suggest a better way to display result.
Here are the table details:
create table Brand (
Id integer PRIMARY KEY,
Name VARCHAR(40) NOT NULL
)
create table Product (
ID integer PRIMARY KEY,
Name VARCHAR(40) NOT NULL,
Price integer NOT NULL,
BrandId integer NOT NULL,
CONSTRAINT BrandId FOREIGN KEY (BrandId)
REFERENCES Brand(Id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
I have used this SQL query below but I am seeing an error below. The result must display the list of the brands that have max number of products as compared to other brands.
select count(p.id) as count, b.name AS brand_name from product p join brand b on b.Id = p.BrandId
where count(p.id) = (select max(count) from product)
group by b.name
ERROR: aggregate functions are not allowed in WHERE
LINE 2: where count(p.id) = (select max(count) from product)
^
SQL state: 42803
Character: 105
In Postgres 13+, you can use FETCH WITH TIES:
select count(*) as count, b.name AS brand_name
from product p join
brand b
on b.Id = p.BrandId
group by b.name
order by count(*) desc
fetch first 1 row with ties;
In older versions you can use window functions.
create table books
(
id int,
title varchar(250),
year int,
author varchar(250)
);
create table reviewers
(
id int,
name varchar(250)
);
create table ratings
(
reviewer_id int,
book_id int,
rating int,
rating_date date
);
Select titles of all books without any rating
Now so far I have this
SELECT *
FROM Books
INNER JOIN Book.ID ON Reviewer.ID
WHERE Reviews.review = 'NULL'
In your case the Ratings table serves as a table connecting ´Books´ and ´Reviewers´.
So to check if there are no ratings you can simply:
SELECT b.*
FROM books b
WHERE NOT EXISTS (SELECT book_id FROM ratings r WHERE r.book_id = b.id)
Meaning that this is showing all the books that do not have any rating.
PS: You don't need INNER JOINs, since you don't need any information from the rating and reviewers table (since there is none).
The way your question is phrased, think "not exists": You want books where a review does not exist:
SELECT b.*
FROM Books b
WHERE NOT EXISTS (SELECT 1 FROM Ratings r WHERE r.BookId = b.id);
I have 2 tables
CREATE TABLE BOOKS
(
numbk INT PRIMARY KEY IDENTITY,
nombk NVARCHAR(60),
_numrub INT FOREIGN KEY REFERENCES CLASSIFICATION(numrub)
)
CREATE TABLE TARIFER
(
_numbk INT FOREIGN KEY REFERENCES BOOKS(numbk),
_nomed NVARCHAR(60) FOREIGN KEY REFERENCES EDITEURS(nomed),
_date DATE,
price DECIMAL(20,2),
PRIMARY KEY (_numouv, _nomed)
)
The question is: how do I list all titles of books (nombk) that have the max price?
PS: TRAFIER has the price columns, and a foreign key from BOOKS which is _numbk
I tried this:
select
o.nombk, max(prix)
from
TARIFER tr, books o
where
o.numbk = tr._numbk
group by
o.nombk
This lists all, but when I execute this:
select max(prix)
from TARIFER tr, books o
where o.numbk = tr._numbk
It returns only the max price. I don't know why. Could someone please explain?
In SQL Server, you can use TOP (1) WITH TIES:
select top (1) with ties b.nombk, t.prix
from books b join
TARIFER t
on b.numbk = t._numbk
order by t.prix desc;
Why not use just a subquery the get the max(prix) and then use that one to list all records with that prix:
select o.nombk ,prix
from TARIFER tr , books o
where o.numbk = tr._numbk
and tr.prix in (select max(prix) from TARIFER tr)
Both queries to aggregation, but not at the same level:
The first query has group by o.nombk, so it generate one record per book, and gives you the maximum price of this book accross all tarifers.
The second query has no group by clause, hence it gives you the maximum price of all books over all tarifers.
If you want the book with the higher price, there is no need to aggregate: you can join and sort the results by price:
select top (1) with ties b.*, t.*
from books b
inner join join tarifer t on b.numbk = t._numbk
order by t.prix desc;
top (1) with ties gives you the first record; if there are several records with the same, top price, the query returns them all.
I have three tables: groups and people and groups_people which forms a many-to-many relationship between groups and people.
Schema:
CREATE TABLE groups (
id SERIAL PRIMARY KEY,
name TEXT
);
CREATE TABLE people (
id SERIAL PRIMARY KEY,
name TEXT,
join_date TIMESTAMP
);
CREATE TABLE groups_people (
group_id INT REFERENCES groups(id),
person_id INT REFERENCES people(id)
);
When I want to query for the latest 10 people who recenlty joined the group which has id = 1:
WITH person_ids AS (SELECT person_id FROM groups_people WHERE group_id = 1)
SELECT * FROM people WHERE id = ANY(SELECT person_id FROM person_ids)
ORDER BY join_date DESC LIMIT 10;
The query needs to scan all of the joined people then ordering them before selecting. That would be slow if the group containing too many people.
Is there anyway to work around it?
Schema (re-)design to allow same person joining multiple group
Since you mentioned that the relationship between groups and people
is many-to-many, I think you may want to move join_date to groups_people
(from people) because the same person can join different groups and each
such event has its own join_date
So I would change the schema to
CREATE TABLE people (
id SERIAL PRIMARY KEY,
name TEXT --, -- change
-- join_date TIMESTAMP -- delete
);
CREATE TABLE groups_people (
group_id INT REFERENCES groups(id),
person_id INT REFERENCES people(id), -- change
join_date TIMESTAMP -- add
);
Query
select
p.id
, p.name
, gp.join_date
from
people as p
, groups_people as gp
where
p.id = gp.person_id
and gp.group_id=1
order by gp.join_date desc
limit 10
Disclaimer: The above query is in MySQL syntax (the question was originally tagged with MySQL)
This seems much easier to write as a simple join with order by and limit:
select p.*
from people p join
groups_people gp
on p.id = gp.person_id
where gp.group_id = 1
order by gp.join_date desc
limit 10; -- or fetch first 10 rows only
Try rewriting using EXISTS
SELECT *
FROM people p
WHERE EXISTS (SELECT 1
FROM groups_people ps
WHERE p.id = ps.person_id and group_id = 1)
ORDER BY join_date DESC
LIMIT 10;
I have two tables created with SQL code:
CREATE TABLE
TicketSales(
purchase# Number(10),
client# Integer CONSTRAINT fk1 REFERENCES Customers,
PRIMARY KEY(purchase#));
CREATE TABLE Customers(
client# Integer,
name Char(30),
Primary Key(client#);
Basically table TicketSales holds ticket sales data and client# is foreign key referenced in customers table. I would like to count names that are in TicketSales table. i tried below code with no success:
select Count(name)
From Customers
Where Customers.Client#=TicketSales.Client#
Group by Name;
Any help appreciated.
Thanks,
If you want a count by each name, then include name in the select and group by clauses
select c.Name, Count(*)
From Customers c
INNER JOIN TicketSales t ON c.Client# =t.Client#
Group by c.Name;
If you want just the count of names, not tickets, then use
select Count(*)
From Customers c
;
Or, for a count of individuals who have tickets recrded against them:
select Count(DISTINCT t.Client#)
From TicketSales t
;