Postgres: Joining twice on a table - sql

I (admitted SQL noob) have three tables in Postgresql that look like this:
groups
id | name | cat_id
----+--------+--------
1 | group1 | 1
3 | group3 | 1
2 | group2 | 2
4 | group4 | 2
category
id | name
----+------
1 | cat1
2 | cat2
translation
id | source | value | type | res_id
----+--------+---------+----------+--------
1 | group1 | Gruppe1 | groups | 1
2 | group2 | Gruppe2 | groups | 2
3 | group3 | Gruppe3 | groups | 3
4 | group4 | Gruppe4 | groups | 4
5 | cat1 | Kat1 | category | 1
6 | cat2 | Kat2 | category | 2
The translation table is global to the application and references other tables using the "res_id" and "type" fields. So to get the translation for "group1" I need to use "where res_id = 1 and type = 'groups'.
I need to list the groups in the following format:
category | group | translated category | translated group
This query gets me almost there:
select category.name, groups.name, translation.value from groups
join category on groups.cat_id = category.id
join translation on groups.id = translation.res_id
where type = 'groups';
But of course I'm missing the translated category, and have no clue how to get it.

I think you want something like:
select
category.name,
groups.name,
tg.value AS translated_group,
tc.value AS translated_category
from groups
inner join translation tg on (groups.id = tg.res_id AND tg.type = "groups")
inner join category on groups.cat_id = category.id
inner join translation tc on (category.id = tc.res_id AND tc.type = "category");
i.e. join translation twice, alias each copy, and use a join condition that also filters for the type field.
Untested as there's no CREATE TABLE and INSERT-form sample data in the question.
None of this is PostgreSQL specific, it's all just standard SQL.
BTW, it's nicer if you don't mix plural and singular forms for table names.

Related

How do I receive a pair of ids of entities in many-to-many relation with null value for the second id if condition is not met

I've got the following tables: person (id), person_agency (person_id, agency_id) and agency(id, type)
this is my query:
select p.id, a.id from person p
left join person_agency pa on p.id = pa.person_id
left join agency a on pa.agency_id = a.id
where a.type = 'agency_type1'
However, with the query I get only the persons who have a relation with an agency of "agency_type1". Instead, I would like to get a list of ids of ALL persons with ids of agencies, where the relation exists and null where it doesn't. I tried naive outer joins but it did not work.
For this content of the tables:
Person:
+-------+
| id |
+-------+
| 1 |
| 2 |
| 3 |
| 4 |
+-------+
Person_agency:
+-----------+-----------+
| person_id | agency_id |
+-----------+-----------+
| 1 | 1 |
| 1 | 2 |
| 2 | 4 |
| 4 | 5 |
+-----------+-----------+
Agency:
+--------+------------------+
| id | type |
+--------+------------------+
| 1 | agency_type1 |
| 2 | some_other_type |
| 3 | agency_type1 |
| 4 | agency_type1 |
| 5 | some_other_type |
+--------+------------------+
I receive the folloing output of my query:
+----------+------+
| p.id | a.id |
+----------+------+
| 1 | 1 |
| 2 | 4 |
+----------+------+
The desired output would be:
+----------+------+
| p.id | a.id |
+----------+------+
| 1 | 1 |
| 2 | 4 |
| 3 | null |
| 4 | null |
+----------+------+
It looks like you don't want to distinguish between an agency which is missing and an agency which is present but the wrong type. So you would want a regular JOIN not a LEFT JOIN for the pa/a pair, and also want to filter out the unwanted type directly on that join. Then you want to do a LEFT JOIN from person to the results of that just-described join.
select p.id p_id, a.id a_id from person p
left join (person_agency pa join agency a on pa.agency_id = a.id and a.type='agency_type1')
on p.id = pa.person_id;
p_id | a_id
------+--------
1 | 1
2 | 4
3 | (null)
4 | (null)
The parenthesis around the join pair are not necessary but I find they make it clearer.
If one person is associated to multiple agencies of the correct type, all of them will be shown. I assume this is what you want, although it was not a scenario covered in your example data.
Try to change left join
to
join (inner join).

SQL generate Data based of the ids of three tables

I have three tables store, gender, age_group each of these tables have ids. I need to generate table data for each one all possible combinations of the three.
ex. store_id = (1,2,3) gender_id = (1,2,3) age_group_id = (1,2,3)
so that i have a table that looks like this:
|store_id|gender_id|age_group_id|
|:------:|:-------:|:----------:|
| 1 | 1 | 1 |
| 1 | 2 | 1 |
| 1 | 3 | 1 |
| 2 | 1 | 3 |
| 2 | 2 | 3 |
| 3 | 1 | 3 |
| 3 | 2 | 3 |
etc. continuing on until each combination is populated, any suggestions on best approach to do this in SQL
Cross join the three tables:
select
s.Id as store_id,
g.Id as gender_id,
a.Id as age_group_id
from store s
cross join gender g
cross join age_group a

SQL join multiple columns into different rows

i have two tables name Combine and Product , combine has multiple products inside and combine has strictly 3 products so that i have two tables strutred as below:
Combine | id | name | image | item1 | item2 | item3
1 | exmpl | www.exmpl.com/exmp.jpg | 3 | 2 | 16
Product | id | name | image | stock
2 | productexmpl | www.product.com/product2.jpg | 3
3 | productexmpl2 | www.product.com/product3.jpg | 7
16 | productexmpl3 | www.product.com/product16.jpg | 3
What i want to get is search by combine id like SELECT * FROM Combine Where id = '' and get combine products in different rows , what i've tried is join tables with SELECT * FROM Combine as c JOIN Product as p on c.item1 = p.id AND c.item2 = p.id AND c.item3 = p.id but it joins information horizontally what i want is to get information vertically which means in different rows as below
id | name | image | stock
2 | productexmpl | www.product.com/product2.jpg | 3
3 | productexmpl2 | www.product.com/product3.jpg | 7
16 | productexmpl3 | www.product.com/product16.jpg | 3
i dont know if the structre is wrong by design but any help is appreciated
Thank you
you can try this query:
SELECT p.id, p.name, p.image, p.stock
FROM Combine as c
inner join
Product as p
on (c.item1=p.id or c.item2=p.id or c.item3=p.id)
Where id = ###

SQL: Get count of rows w.r.t rows of another table

I want to get the count of rows from a table belonging to a category (which are defined in another table). Kind of like the following.
-----------------------------------------
id | name | category
| |
1 | Name 1 | toddler
2 | Name 2 | toddler
3 | Name 3 | newborn
4 | Name 4 | toddler
5 | Name 5 | adult
And I have another table where all the categories are defined
-----------------------------------------
id | category
|
1 | toddler
2 | newborn
3 | adult
4 | elderly
Now I need an SQL Query on the first table which can give me a return result something like this
-----------------------------------------
category | count
|
toddler | 3
newborn | 1
adult | 1
elderly | 0
I need to count each name from Table 1 with a particular category from Table 2 and return the result.
This seems to have a fairly simple solution but I can't get my mind to work on it. Please help!
This is a simple query with LEFT JOIN and COUNT.
select c.category, COUNT(n.category) as count
from Table2 c
left join Table1 n on c.category = n.category
group by c.category
SQL Fiddle demo
Simple. Use a left join on your Table2 with Table1, then use the count function on category and do group by on category.
select b.category, count(a.category)
from Table1 as a
left join Table2 as b
on a.id = b.id
group by b.category

Query for joining all info from three tables

I need to Join 3 tables in SQL Server. Those 3 tables have basically this schema:
Users Items UsersItems
+--------+ +--------+-------------+ +--------+--------+-------+
| UserID | | ItemID | Description | | UserID | ItemId | Value |
+--------+ +--------+-------------+ +--------+--------+-------+
| 1 | | 1 | desc1 | | 1 | 1 | 1 |
| 2 | | 2 | desc2 | | 1 | 2 | 2 |
| ... | | ... | desc3 | | 2 | 2 | 1 |
| n | | n | desc4 | | n | 1 | 1 |
+--------+ +--------+-------------+ +--------+--------+-------+
As you can see both Users and Items can grow indefinitely and UsersItems is used to express the relation between those two, also including a Value column.
I need a query to retrieve all users, and for each user I need all the items with it's corresponding Value.
If the relation doesn't exist in UsersItems then Null (or a default value) should be returned for that row's Value column.
The expected query result should be:
ResultSet
+--------+--------+-------+
| UserID | ItemID | Value |
+--------+--------+-------+
| 1 | 1 | 1 |
| 1 | 2 | 2 |
| 1 | n | NULL |
| 2 | 1 | NULL |
| 2 | 2 | 1 |
| 2 | n | NULL |
| n | 1 | 1 |
| n | n | NULL |
+--------+--------+-------+
Okay, since there are several answers that I think aren't correct, I'll post what I think the answer is:
SELECT Users.UserID,
Items.Description,
UsersItems.Value
FROM
Users
CROSS JOIN
Items
LEFT JOIN
UsersItems
ON
Users.UserID = UsersItems.UserID
AND
Items.ItemID = UsersItems.ItemID
I'm inferring from your comment about nulls that you want to see all Items againsts all Users, with the Value from the UsersItems table where it exists.
SELECT
Users.UserID,
Items.Description,
Items.Value
FROM Users LEFT OUTER JOIN UsersItems
ON Users.UserID = UsersItems.UserID
LEFT OUTER JOIN Items
ON UserItems.ItemID = Items.ItemID
You do it like this:
SELECT Users.UserID, Items.Description, Items.Value
FROM Users
LEFT OUTER JOIN UsersItems ON Users.UserID = UsersItems.UserID
LEFT OUTER JOIN Items ON UserItems.ItemID = Items.ItemID
For more info on left outer join read the following:
The result of a left outer join (or simply left join) for table A and
B always contains all records of the "left" table (A), even if the
join-condition does not find any matching record in the "right" table
(B). This means that if the ON clause matches 0 (zero) records in B,
the join will still return a row in the result—but with NULL in each
column from B. This means that a left outer join returns all the
values from the left table, plus matched values from the right table
(or NULL in case of no matching join predicate). If the right table
returns one row and the left table returns more than one matching row
for it, the values in the right table will be repeated for each
distinct row on the left table. From Oracle 9i onwards the LEFT OUTER
JOIN statement can be used as well as (+).