Counting children in another table for each row - sql

I have two tables: CATEGORY and SUBCATEGORY
Table Structure for CATEGORY
category_id int(11)
category_name varchar(250)
category_status enum('0', '1')
Table Structure for SUBCATEGORY
subcategory_id int(10)
subcategory_name varchar(255)
status enum('0', '1')
For example there is a single CATEGORY named .NET and it has entries in SUBCATEGORY like ASP.NET, VB.NET, C#.NET . In this case I need to get the count of CATEGORY as 1 and the count of SUBCATEGORY as 3 using MySQL.
How can I accomplish this?

Well, you can do it with a subquery. However, you'll need to add a category_id column to the subcategory table, so that we know what subcategories go with which categories. Then, you can get what you want with the following query:
select
category_name,
1 as CategoryCount,
(select count(*) from subcategory where category_id = c.category_id) as SubCategoryCount
from
category c

Since we can assume category count is one and there's more than likely a key constraint on category_id between the two tables, this will work as well:
select c.category_id, count(c.category_id)
from category c
inner join subcategory s on (c.category_id = s.category_id)
group by c.category_id

SELECT COUNT(*) FROM CATEGORY;
SELECT COUNT(*) FROM SUB_CATEGORY;
I don't believe that's exactly what you're going for, but that's all you're really gonna get without a foreign key.

Related

Finding max count of product

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.

Select MAX price of a book with JOIN (2 tables) in SQL Server?

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.

Listing columns from multiple tables in sql

I've got a database which is formed like this:
BOOKTYPES ( book_type_id (PK), book_type )
CATEGORIES ( category_id (PK), category )
PUBLISHERS ( publisher_id (PK), publisher, speciality, country )
BOOKS ( book_id (PK), title, publisher_id (FK), published_year,
purchase_price, category_id (FK),purchase_date, pages,
book_type_id(FK) )
AUTHORS ( author_id (PK), first_name, last_name, pseudonym )
AUTHORSHIP ( author_id (PK), book_id (PK) )
Now, what I need help with is listing category and the number of books that contain that category. This means that I need to retrieve the category from CATEGORIES, and category_id from books. The problem I'm facing with this is that category_id already exists inside categories, and that isn't the one that I want to retrieve.
I'd really appreciate some help with this since it's been picking my brain for quite the while now.
listing category and the number of books that contain that category
That looks like a simple aggregate query on books:
SELECT category_id, COUNT(*) count_of_books FROM books GROUP BY category_id
If you want the category name as well, you can JOIN on categories. You do disambiguate the column names by prefixing them with the table they belong to (or the table alias):
SELECT c.category, COUNT(*)
FROM books b
INNER JOIN categories c ON c.category_id = b.category_id
GROUP BY c.category_id, c.category
To filter on a given count of categories, you can use a HAVING clause:
SELECT c.category, COUNT(*)
FROM books b
INNER JOIN categories c ON c.category_id = b.category_id
GROUP BY c.category_id, c.category
HAVING COUNT(*) > 5

What is the most efficient way of joining tables of different dimensions?

I have the following schema:
CREATE TABLE products (
id BIGSERIAL NOT NULL,
created_at_timestamp TIMESTAMP NOT NULL DEFAULT NOW(),
last_update_timestamp TIMESTAMP NOT NULL DEFAULT NOW(),
PRIMARY KEY (id)
);
CREATE TABLE product_names (
product_id BIGINT NOT NULL,
language TEXT NOT NULL,
name TEXT NOT NULL,
PRIMARY KEY (product_id, language),
FOREIGN KEY (product_id) REFERENCES products (id)
);
CREATE TABLE product_summaries (
product_id BIGINT NOT NULL,
language TEXT NOT NULL,
summary TEXT NOT NULL,
PRIMARY KEY (product_id, language),
FOREIGN KEY (product_id) REFERENCES products (id)
);
And I want to select all Products.
However as you can see a Product contains a list of names and summaries (per language).
I can retrieve all Products
SELECT * FROM products
And then iterate all the rows (in this case in Kotlin), and then request the names and summaries:
SELECT * FROM product_names WHERE product_id = $id
And
SELECT * FROM product_summaries WHERE product_id = $id
However, this seems inefficient, since I am making 3 separate queries to the database.
I though of using JOINs to get all of this with one query, but then I get multiple repeated rows for each product_names and product_summaries entry.
So in the end, is there a better way of requesting all this data in one query?
You definitely don't want to do multiple queries and then iterate over them in the code. That's horribly inefficient. When you do the second JOIN, you need to include language in the JOIN. That should keep you from getting duplicate rows. This should give you one row for each unique combination of [products.id, product_names.language]
SELECT
products.id
,products.created_at_timestamp
,products.last_update_timestamp
,product_names.name
,product_summaries.summary
,product_names.language
FROM
products
INNER JOIN
product_names ON product_names.product_id = products.id
INNER JOIN
product_summaries ON product_summaries.product_id = products.id
AND product_summaries.language = product_names.language
I've found a way of doing it:
SELECT * FROM products as p INNER JOIN
(SELECT json_agg(product_names) as names, product_id FROM product_names GROUP BY product_id) as tb_names ON tb_names.product_id = p.id
INNER JOIN
(SELECT json_agg(product_summaries) as summaries, product_id FROM product_summaries GROUP BY product_id) as tb_summaries ON tb_summaries.product_id = p.id
returns:
1 | 2018-07-20 09:36:21.56904 | 2018-07-20 09:36:21.56904 | [{"product_id":1,"language":"EN","name":"lol"},
{"product_id":1,"language":"DE","name":"lel"}] | 1 [{"product_id":1,"language":"EN","summary":"deded"},
{"product_id":1,"language":"DE","summary":"rererere"},
{"product_id":1,"language":"FR","summary":"jejejeje"}] | 1
Basically I'm converting the multi-dimensional tables to JSON :)
Postgres is amazing!

Many-to-many relation filter

I need to filter my query with categories table which has many2many relation with another table. Is it possible to filter query with many2many relation?
Table res_partner has many2many field category_id relating to table res_partner_category.res_partner, or let's just say partners can have many categories. What I need is to filter res_partners table where it has category named 'business' or 'retail'. If it doesn't have any of these categories, it should not be shown.
Also there is another field in res_partner which is category_value_ids and has one2many relation with res_partners_category_value:
res_partner has following fields with relations:
category_id to res_partner_category (many2many)
category_value_ids to res_partner_category_value (one2many)
name (char)
res_partner_category has following fields with relations:
partner_ids to res_partner (many2many)
name (char)
res_partner_category_value has following fields with relations:
category_group_id to res_partner_category (many2one)
category_id to res_partner_category (many2one)
object_id tores_partner (many2one)
But if I try to use res_partner_category_value table in SQL query I get error that I can't use it in query.
So for example, if there are 4 partners with these categories:
first: categ1, categ2, business
second: retail
third: retail, business
fourth: categ1, categ2
The query should return first, second and third partners.
One person told me it's not possible to filter like this with many2many relation. So I wonder is it really not possible or just complicated?
EDIT:
I found one more table called res_partner_category_rel. I didn't see it, because in Openerp administration interface, where you can see all objects of database, that table is not shown. You can only see it directly through database.
So I was confused by this "missing" table:
res_partner_category_rel:
partner_id (many2one)
category_id (many2one)
Setup
This is the test case you should have provided:
CREATE TABLE partner (
partner_id serial PRIMARY KEY
, partner text
);
INSERT INTO partner (partner) VALUES
('partner1')
, ('partner2')
, ('partner3')
, ('partner4')
;
CREATE TABLE category (
category_id serial PRIMARY KEY
, category text
);
INSERT INTO category (category) VALUES
('categ1')
, ('categ2')
, ('business')
, ('retail')
;
CREATE TABLE partner_category (
partner_id int REFERENCES partner(partner_id)
, category_id int REFERENCES category(category_id)
, CONSTRAINT cat_pk PRIMARY KEY (partner_id, category_id)
);
INSERT INTO partner_category (partner_id, category_id) VALUES
(1,1), (1,2), (1,3)
, (2,4)
, (3,3), (3,4)
, (4,1), (4,2);
Solution
One way:
SELECT p.*
FROM partner p
WHERE EXISTS (SELECT FROM partner_category pc WHERE pc.partner_id = p.partner_id AND pc.category_id = 3)
OR EXISTS (SELECT FROM partner_category pc WHERE pc.partner_id = p.partner_id AND pc.category_id = 4)
ORDER BY p.partner_id;
Another:
SELECT p.*
FROM (SELECT partner_id FROM partner_category WHERE category_id = 3) pc1
FULL JOIN (SELECT partner_id FROM partner_category WHERE category_id = 4) pc2 USING (partner_id)
JOIN partner p USING (partner_id)
ORDER BY p.partner_id;
fiddle
Old sqlfiddle
The second one assumes unique (partner_id, category_id) in partner_category.
As you already noticed, the many2one category_id is not represented in the database as a table field, but as a table relating Partners and Categories.
The SQL you need could look like this:
SELECT p.*
FROM res_partner p
INNER JOIN res_partner_category_rel rel ON p.id = rel.partner_id
INNER JOIN res_partner_category c ON rel.category_id = c.id
WHERE c.id in (3,4)
If you want to do the filter in the python object, the usual searchcall should work:
list_ids = partner_model.search(cr, uid, [('category_id', 'in', [3,4])])
As a bonus, since Categories are organized in a tree, you could get those categories and all their children using:
list_ids = partner_model.search(cr, uid, [('category_id', 'child of', [3,4])])