Linkage Table: Technical name given to table storing associations? - sql

Title pretty much sums it up.
Is there a technical name given to a table that stores primary key from two separate tables to create a linkage.
i.e.
car ( id, manufacturer, model, year, vin),
passenger ( id, name ),
linkage_table ( car, passenger )
Where car stores value of the id column from the car table and passenger stores the value of id column from the passenger table.
SELECT c.*, p.*
FROM car c, passenger p, linkage_table l
WHERE c.id = 15
AND c.id = l.car
AND p.id = l.passenger

It's called a junction table, and is used in a Many-to-Many relationship.

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

How to stop a database result from re-appearing once value is taken

I have a car rental project, when renting an available car from database table CAR with car ID as primary key, I insert data into table RESERVATION with reservation ID as primary key and car ID as foreign key from car table.
However, I don't want the car I just rented out to appear as an "available car" to rent. What query should I write in order to hide it?
Select cars.*
from cars, reservation
where ?
You should not need to update any tables to hide the car.
When selecting the list of available cars for display, use this SQL:
SELECT * FROM Car WHERE CarID NOT IN (SELECT CarID FROM Reservation)
This will exclude any cars that have already been reserved. That's the basic idea, anyway.
It's likely that reservations are only good for a certain period, and the user may be attempting to reserve a different period, so you might need something more complicated, like:
SELECT * FROM Car WHERE CarID NOT IN
(
SELECT CarID
FROM Reservation
WHERE StartDate < #DesiredEndDate
AND EndDate > #DesiredStartDate
)
This will provide a list of cars that do not appear in a reservation that overlaps the user's desired reservation time.
You can write a join query as:
Select C.carID
from #Car C
Left join #Reservation R on C.carID = R.carID
where R.reservationID is null

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'

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

SQL one-to-many relationship, how to select parent containing unique set of children

I have a routine one-to-many schema, similar to this simple example:
PERSON
person_id (PK)
PERSON_TRAIT
person_id (FK)
trait_id (FK)
quantity
TRAIT
trait_id (PK)
name
//other attributes
Given a set of traits ("friendly, funny"), how would I return the associated person_id and quantity recordset.
At first glance I was tempted to use this but it's not this simple:
select person_id, quantity
from trait t
inner join person_trait pt on t.trait_id = pt.trait_id
where name in ('friendly', 'funny')
This isn't correct because I could have a person that contains those traits plus more ("friendly, funny, skinny") and it would be returned.
To take it a step further, if there isn't a person that contains all of the traits exactly, how would I aggregate the traits from multiple different persons and return a recordset of those person_id and quantity values?
Using SQL Server 2005.
This will give you a list of people who have only the two traits indicated.
SELECT person_id, quantity
FROM PERSON_TRAIT
WHERE person_id IN
(
SELECT person_id
FROM PERSON_TRAIT pt
LEFT OUTER JOIN TRAIT t ON pt.trait_id = t.trait_id
AND t.name IN ('friendly','funny')
GROUP BY person_id
HAVING COUNT(*) = COUNT(t.trait_id)
)
Try:
select person_id, sum(quantity)
from trait t
inner join person_trait pt on t.trait_id = pt.trait_id
where name in ('friendly', 'funny')
having count(distinct name) = 2
Change the having number to be the number of distinct names to be selected - so if 'friendly', 'funny' and 'clever' are required, change the having clause to be having count(distinct name) = 3.