Left outer joins aggregate first - sql

I have the following tables
CREATE TABLE categories(
id SERIAL,
);
CREATE TABLE category_translations(
id SERIAL,
name varchar not null,
locale varchar not null,
category_id integer not null
);
CREATE TABLE products(
id SERIAL,
category_id integer not null
);
CREATE TABLE line_items(
id SERIAL,
total_cents integer
product_id integer not null
);
What I'm trying to do is output a map of each category name to the sum of total of its associated line_items total_cents. Something like:
name
sum_total_cents
Fresh foods
100000
Dry products
532000
There is a uniqueness constraint that only one name for each locale will be stored. So a category will have one row for each locale stored in the category_translations table
What I currently have is
SELECT SUM(line_items.total_cents) AS sum_total_cents, ???
FROM line_items INNER JOIN products ON products.id = line_items.product_id
INNER JOIN categories ON categories.id = products.category_id
LEFT OUTER JOIN category_translations ON category_translations.category_id = categories.id
WHERE category_translations.locale ='en'
GROUP BY categories.id
I'm looking for an aggregate function to return the first name for the category. The only piece missing is that what to be written instead of the ??? as I've been facing a lot of must appear in the GROUP BY clause or be used in an aggregate function errors. In pseudo-code I'm looking for a FIRST() aggregate method in PostgreSQL that I can use

Assuming you want one random name from any locale, you can do:
select
c.id,
(select name from category_translations t
where t.category_id = c.id limit 1) as name,
sum(i.total_cents) as sum_total_cents
from categories c
left join products p on p.category_id = c.id
left join line_items i on i.product_id = p.id
group by c.id, name
Alternatively, if you want the category name for the locale 'en' then you can do:
select
c.id,
(select t.name from category_translations t
where t.category_id = c.id and t.locale ='en') as name,
sum(i.total_cents) as sum_total_cents
from categories c
left join products p on p.category_id = c.id
left join line_items i on i.product_id = p.id
group by c.id, name

Related

What is the most efficient way of selecting data from relational database?

I just started working with databases and
I have this data sample from PostgreSQL tutorial
https://www.postgresqltutorial.com/postgresql-sample-database/
Which diagram looks like this:
I want to find all film categories rented in for example Canada. Is there a way of doing it without using SELECT within SELECT.. statement like this:
SELECT * FROM category WHERE category_id IN (
SELECT category_id FROM film_category WHERE film_id IN (
SELECT film_id FROM film WHERE film_id IN (
SELECT film_id FROM inventory WHERE inventory_id IN (
SELECT inventory_id FROM rental WHERE staff_id IN (
SELECT staff_id FROM staff WHERE store_id IN (
SELECT store_id FROM store WHERE address_id IN (
SELECT address_id FROM address WHERE city_id IN (
SELECT city_id FROM city WHERE country_id IN (
SELECT country_id FROM country WHERE country IN ('Canada')
)
)
)
)
)
)
)
)
)
I'm sure there must be something that i'm missing.
The proper way is to use joins instead of all these nested subqueries:
select distinct c.category_id, c.name
from category c
inner join film_category fc on fc.category_id = c.category_id
inner join inventory i on i.film_id = fc.film_id
inner join rental r on r.inventory_id = i.inventory_id
inner join staff s on s.staff_id = r.staff_id
inner join store sr on sr.store_id = s.store_id
inner join address a on a.address_id = sr.address_id
inner join city ct on ct.city_id = a.city_id
inner join country cr on cr.country_id = ct.country_id
where cr.country = 'Canada'
For your requirement you must join 9 tables (1 less than your code because the table film is not really needed as the column film_id can link the tables film_category and inventory directly).
Notice the aliases for each table which shortens the code and makes it more readable and the ON clauses which are used to link each pair of tables.
Also the keyword DISTINCT is used so you don't get duplicates in the results because all these joins will return many rows for each category.

How to get the most expensive item in a category using 2 tables?

I'm trying to get to show only the category that have the most expensive item.
I tried and with the query I have I get all categories and the price of the most expensive item in every category, but I don't know how to do to just get just the one category that have the most expensive.
select categories.category ,max(purchase_price) as dyrast_bok
from categories
inner join books on categories.category_id = books.category_id
group by categories.category;
The tables:
CATEGORIES ( category_id (PK), category )
BOOKS ( book_id (PK), title, publisher_id (FK), published_year,
purchase_price, category_id (FK),purchase_date, pages,
book_type_id (FK) )
select categories.category ,purchase_price as dyrast_bok
from categories
inner join books on categories.category_id = books.category_id
where purchase_price in (select max(purchase_price) from books)
You can try to order by max(purchase_price) desc and take the first one:
select *
from (your query with order by max(purchase_price) desc)
limit 1
You just need to use aliases so you can reuse them in the GROUP BY part, and use top 1 to get only the highest result when you order by max(b.purchase_price).
This is how should be your query:
select top 1 c.category , max(b.purchase_price) as dyrast_bok
from categories c
inner join books b on categories.category_id = books.category_id
order by dyrast_bok
group by c.category;
Simple way: Have a sub-query that returns the max purchase_price.
select c.category, b.purchase_price as dyrast_bok
from categories c
inner join books b on c.category_id = b.category_id
where b.purchase_price = (select max(purchase_price) from books)
Will return both books in case of a tie!
Alternative solution:
select c.category, b.purchase_price as dyrast_bok
from categories c
inner join books b on c.category_id = b.category_id
order by b.purchase_price desc
fetch first 1 row
Will not include ties, only one row will be returned. (AFAIK Postgresql doesn't support FETCH FIRST WITH TIES.)

SQL INNER JOIN entity

I want to execute this query :
-- The most expensive item sold ever
SELECT
c.itemID, c.itemName
FROM
item AS c
JOIN
(SELECT
b.itemID as 'itemid', MAX(b.item_initialPrice) AS 'MaxPrice'
FROM
buyeritem AS a
INNER JOIN
item AS b ON a.item_ID = b.itemID) AS d ON c.itemID = d.itemid
GROUP BY
c.itemID, c.itemName;
My item table looks like this:
create table item
(
itemID int IDENTITY(1000, 1) NOT NULL,
itemName varchar(15) NOT NULL,
Item_desc varchar(255),
Item_initialPrice MONEY,
ItemQty int,
ownerID int NOT NULL,
condition varchar(20) NOT NULL,
PRIMARY KEY (itemID),
FOREIGN KEY (ownerID) REFERENCES seller (sellerID)
);
The problem is that column item.itemID is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause. I tried to add a group by clause at the end
group by c.itemID, c.itemName
but I still get the same error? I don't really know where the problem comes from.
I also have this query
-- The most active seller(the one who has offered the most number of items)
SELECT
a.ownerID, b.sellerName
FROM
item AS a
INNER JOIN
seller AS b ON a.ownerID = b.sellerID
GROUP BY
a.ownerID, b.sellerName
ORDER BY
COUNT(a.itemID) DESC;
I want to add itemQty along with the ownerID and sellerName from item table stated above, what would be the best way to achieve that?
Just write distinct instead of Group By as Group By will not work with out an aggregated function like sum,max etc. in select statement which is missing in your query.An example of this is second query which I have written
SELECT distinct c.itemID, c.itemName
FROM item AS c
JOIN (
SELECT b.itemID as itemid, MAX(b.item_initialPrice) AS MaxPrice FROM buyeritem AS a
INNER JOIN item AS b ON a.item_ID = b.itemID
GROUP BY b.itemID) as d
ON c.itemID = d.itemid ;
For second query
Select a.* from
(
SELECT a.ownerID, b.sellerName, count(distinct a.ITEM_ID) as item_qty
FROM item AS a
INNER JOIN seller AS b ON a.ownerID = b.sellerID
GROUP BY a.ownerID,b.sellerName
) a
order by item_qty DESC

Getting extra data from the join table

Ok so i have this structure
PRODUCTS
* PRODUCT_ID (primary key)
* PRODUCT_NAME
CATEGORIES
* CATEGORY_ID (primary key)
* CATEGORY_NAME
* CODE
PRODUCT_CATEGORIES_MAP
* PRODUCT_ID (primary key, foreign key to PRODUCTS)
* CATEGORY_ID (primary key, foreign key to CATEGORIES)
* QUANTITY
I am using this query
SELECT * FROM `products`
where id in (
SELECT `product_id`
FROM `categorizations`
WHERE category_id = (
SELECT id
FROM `categories`
where code = 'something'))
this works great but i am just getting a list of products. I need the quantity of each one one in the join table
The table names in your explanation are different from your sample query. I used the ones from your query.
SELECT p.product_id ,
p.product_name,
pc.quantity
FROM `products` p
JOIN `categorizations` pc
ON p.id = pc.`product_id`
JOIN `categories` c
ON c.id = pc.category_id
WHERE c.code = 'something'
sql's a bit rusty but here goes
select PRODUCT_ID, PRODUCT_NAME, QUANTITY
from PRODUCTS as A, PRODUCTS_CATEGORIES_MAP as B, CATEGORIES as C
where A.PRODUCT_ID = B.PRODUCT_ID
and C.CATEGORY_ID = B.CATEGORY_ID
and C.CODE = 'something'
You may want to sum over quantity in case product is in more categories.
select p.*,
sum(m.quantity) quantity
from products p
join categorizations m on m.product_id = p.product_id
join categories on c.category_id = m.category_id
and c.code = 'something'
group by p.product_id
Otherwise look at #Martin code
The detail that you want is the quantity of the product and so, why not start by them....you can always retrieve the products because you have the product_id.
SELECT * FROM (SELECT `product_id`, `quantity`
FROM `categorizations`
WHERE category_id = (
SELECT id
FROM `categories`
where code = 'something'))) AS `filtered_categorization`
LEFT JOIN `products`
ON (`products`.`id` == `filtered_categorization`.`product`);
try this...
select
p.*,
c.category_id,
c.category_name,
pcm.quantity
from
products p
inner join product_categories_map pcm on p.product_id = pcm.product_id
inner join categories c on pcm.category_id = c.category_id
where
c.code = 'something'
order by
p.product_id, c.category_id;

SQL query multi table selection

I have 3 tables,
- Section table that defines some general item sections.
- Category table -> has a "section" column (foreign key).
- Product table -> has a "category" column (foreign key).
I want to get all products that belong to X section.
How can I do it?
select from select?
Select
prod.*
FROM
Product prod
INNER JOIN Category cat ON prod.category = cat.id
INNER JOIN Section sec ON cat.section = sec.id
WHERE
sec.id = X
Lots of same answers here. For some reason, though, all of them are joining the Section table which is (likely) not necessary.
select
p.*
from
Product p,
Category c
where
p.category_id = c.id and
c.section_id = 123
;
Explicit ANSI JOIN syntax per #nemiss's request:
select
p.*
from Product p
join Category c
on c.id = p.category_id
and c.section_id = 123
;
Possible reason to include Section table: Selecting products based on Section name (instead of ID).
select
p.*
from Product p
join Category c
on c.id = p.category_id
join Section s
on s.id = c.section_id
and s.name = 'Books'
;
If doing this, you'll want to make sure Section.name is indexed
alter table Product add index name;
select s.section, p.*
from section s
inner join category c on c.section = s.section
inner join product p on p.category = c.category
where s.section = 'section1'
select p.*
from Product p
join Category c on p.CategoryId = c.Id
join Section s on c.SectionId = s.Id
where s.Id = #val