Many-to-many relation filter - sql

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])])

Related

Return everything from many-to-many relationship with only one query

I'll give an example to better clarify what I want:
Suppose I have the following classes in my programming language:
Class Person(
int id,
string name,
List<Car> cars
);
Class Car(
int id,
string name,
string brand
)
I want to save that in a PostgreSQL database, so I'll have the following tables:
CREATE TABLE person(
id SERIAL,
name TEXT
);
CREATE TABLE car(
id SERIAL,
name TEXT,
brand TEXT
)
CREATE TABLE person_car(
person_id int,
car_id int,
CONSTRAINT fk_person
FOREIGN KEY (person_id)
REFERENCES person(id),
CONSTRAINT fk_car
FOREIGN KEY (car_id)
REFERENCES car(id)
)
Then, I want to select all people with their cars from DB. I can select all people, then for each person, select their cars. But supposing I have 1000 people, I will have to query the DB 1001 times (one to select all people, and one for each person, to get their cars).
Is there an efficient way to bring all people, each with all their cars in a single query, so that I can fill my classes with the correct data without querying the DB a lot of times?
If you want to return a hierarchical dataset, you can use subqueries with COALESCE, for example :
SELECT
p.id
p.name,
COALESCE((SELECT
json_agg(json_build_object(
'id', c.id,
'name', c.name,
'brand', c.brand
))
FROM car AS c
JOIN person_car pc ON c.id = pc.car_id
WHERE pc.person_id = p.id), '[]'::json) AS cars
FROM person AS p;
You are joining person and car to person_car based on their respective ID’s.
SELECT
person.name,
person.id as person_id,
car.name,
car.brand,
car.id as car_id
FROM
person
JOIN
person_car
ON
person.id = person_car.person_id
JOIN
car
ON
car.id = person_car.car_id

Posgresql join two tables where the foriegn key is an array of ids

I am new to SQL and I have three table
Templates Table
CREATE TABLE templates (
template_id serial PRIMARY KEY,
template_name VARCHAR ( 15 ) UNIQUE NOT NULL,
FOREIGN KEY (developer_id) REFERENCES users(user_id),
FOREIGN KEY (category_id) REFERENCES categories(category_id),
tag_ids int[],
FOREIGN KEY (EACH ELEMENT OF tag_ids) REFERENCES tags(tag_id)
);
Categories Table
CREATE TABLE categories (
category_id serial PRIMARY KEY,
category_name VARCHAR ( 15 ) UNIQUE NOT NULL
);
Tags Table
CREATE TABLE tags (
tag_id serial PRIMARY KEY,
tag_name VARCHAR ( 100 ) NOT NULL,
);
I want to Select all templates where each template has a category object and a tags object.
Each template has one category but may have multiple tags.
I want to have the tags as an array attribute in the template object
I have tried this query, it does what i want but it creates multiple objects for the same template. So it simply creates n objects where n is the number of tags.
let query = `SELECT t.*, to_json(c) "category", ${developerJson} "developer", json_agg(tgs) "tags" FROM templates t INNER JOIN categories c ON t.category_id = c.category_id INNER JOIN users d ON t.developer_id = d.user_id JOIN tags tgs ON tgs.tag_id = ANY(t.tags_id) ${condition} ${groupBy}`;
Can anyone help me?
I have found the solution. I was passing the tag_id in the group elements.
Once I removed it, I got what I was expecting
const developerJson = `json_build_object( 'first_name',first_name, 'last_name', last_name, 'avatar_link', avatar_link, 'slug', d.slug ,'date_joined',date_joined)`;
const groupBy = `GROUP BY t.template_id, c.*, d.first_name, d.last_name, d.avatar_link, d.slug, d.date_joined`;
let query = `SELECT t.*, to_json(c) "category", ${developerJson} "developer", json_agg(tgs) "tags" FROM templates t INNER JOIN categories c ON t.category_id = c.category_id JOIN users d ON t.developer_id = d.user_id JOIN tags tgs ON tgs.tag_id = ANY(t.tags_id) ${groupBy}`;

Create tables with Many-to-One relation in Postgresql

I am a newbie with Postgresql and database . I have the following table Shop
Create table Shop(
id integer ,
Name varchar(50),
Adress varchar(50) )
And I have a second table Product
Create table Product(
id integer ,
Name varchar(50),
Price float)
How to create a relation One-To-One between Product and Shop ?
Use a separate table, called a junction table or join table (and unfortunately has many other names)
CREATE TABLE Products_in_shop (
shop_id INTEGER,
product_id INTEGER
)
You can then JOIN them for specific queries, e.g. the products a specific shop carries:
SELECT p.Name
FROM Shop AS s
JOIN Products_in_shop AS pis ON pis.shop_id = s.id
JOIN Product AS p ON pis.product_id = p.id
WHERE s.Name = 'MyShop'

Table with catogories and multiple sub catogories for each catogory

I am trying to write a new application but I'm stuck. I want to create a database with a table for storing catogories and another one for storing sub categories. I have some ideas about how to create the database/tables but no idea about how I can select categories and all sub categories belonging to that category at once. Can someone help me?
Scenario:
Table with Users identified by ID's.
Table with Categories identified by ID and with a foreign key to the User.ID (one category can belong to only one user)
Table with SubCategories with foreign keys to the Category table it belongs to and the User.ID that the Category belongs to.
How could I select and display all usernames with belonging categories and sub categories?
If I understand your question correctly, you want a query that will give you results like the following:
User Category SubCategories
Bob | Builder | BuilderSub1
Bob | Builder | BuilderSub2
Jerry | Supervisor | SupervisorSub1
Tim | Builder | BuilderSub3
SELECT u.UserName as 'User', c.CategoryName as 'Category', sc.SubcategoryName as 'SubCategories'
FROM Users u
INNER JOIN Category c ON c.UserId = u.Id
INNER JOIN Subcategory sc ON
sc.CategoryId = c.Id
GROUP BY sc.SubcategoryName, c.CategoryName, u.UserName
The GROUP BY is what you need.
You'll need a foreign key relationship and a JOIN:
create table category (
id int not null auto_increment,
primary key(id)
);
create table subcategory (
id int not null auto_increment,
category_id int,
primary key(id),
foreign key(category_id) references category(id)
);
Here's a sample JOIN:
select *
from user
join category
on user.category_id = category.id
join subcategory
on category.id = subcategory.category_id
where user.id = ?

Counting children in another table for each row

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.