PostgreSQL / Hive join multiple tables - postgresql

Table a:
id value0
101 a1
102 a2
103 a3
Table b:
id value1
101 b1
101 b2
101 b3
Table c:
id value2
101 c1
103 c3
103 c4
Rezult table:
id value0 value1 value2
101 a1 b1 0
101 a1 b2 0
101 a1 b3 0
101 a1 0 c1
102 a2 0 0
103 a3 0 c3
103 a3 0 c4
Is it possible to produce rezult table from tables a, b, c with one query (without creating two tables and join them)? Maybe there is a possibility to do it by using only left joins?

This may help you-
select t1.id, t2.id, t3.id
from tablea t1 inner join tableb t2 on t1.id = t2.id
inner join tablec t3 on t2.id=t3.id
group by id

If you have a base table, select that and do a left join to the others. If none of your tables can act as a base table, you can use full joins (both works as outer joins):
select *
from table_a
full join table_b using (id)
full join table_c using (id)
This will select sql NULLs, where there is no data, but you can use COLAESCE(value0, 'N/A'), etc. to select some default data.

Related

Unable to filter rows of one table based on data in another table in PostgreSQL

I have two tables tbl1 (552 rows) and tbl2 (257 rows) with unequal rows in my PostgreSQL database (PG 9.5, x64 Windows). The tables tbl1 and tbl2 contain the following data (sample shown here):
Grp id val1 val2 sum
1 1 112 142 5.2
1 2 124 137 6.7
1 3 136 189 6.8
1 4 112 114 9.8
1 5 130 145 6.1
1 6 142 130 7.7
Grp id sum
1 1 5.2
1 3 6.8
1 6 7.7
For each group in table 1, I am trying to select rows where "id" in the first table is not equal to "id" of second table. For example, my expected output would be:
Grp id val1 val2 sum
1 2 124 137 6.7
1 4 112 114 9.8
1 5 130 145 6.1
I have tried this query so far but it doesn't work, and return empty output.
SELECT
a.grp, a.id,
a.val1, a.val2, a.sum
FROM tbl1 a
WHERE NOT EXISTS (
SELECT *
FROM tbl2 b WHERE b.grp = a.grp AND b.id != a.id
)
Can someone help on this and explain what am I doing wrong?
Since you are using NOT EXISTS the condition you need is b.id = a.id and not b.id != a.id:
...................
WHERE b.grp = a.grp AND b.id = a.id
...................
The problem with your query is that the NOT EXISTS clause will always return false with the data you have shown in the sample (for each row in a, there is a row in b where grp is the same but id is different).
A LEFT JOIN with a IS NULL filter will do the trick:
SELECT a.grp, a.id,
a.val1, a.val2, a.sum
FROM tbl1 a
LEFT JOIN tbl2 b ON a.grp = b.grp AND a.id = b.id
WHERE b.id IS NULL --b.id is null if there is no row in b which matches both a.grp and a.id

Group retrieved records from a RECURSIVE query, that have the same parents, grandparents

I have a table with Item(s) than are in a ManyToMany Relationship with Category.
Item
id | name
1 name1
2 name2
3 name3
ItemCategory
id | category_id | item_id
1 4 1
2 5 1
3 7 1
4 8 1
Category (parent_id foreign key to itself)
id | parent_id | name
1 Null A1
2 1 B1
3 1 B2
4 2 C1
5 3 C2
6 1 D1
7 6 DE
8 1 DT
I'm trying to get the Item Categories, from child to parent, for an Item so I use:
WITH RECURSIVE descendants(name, id, slug, parent_id, bread_order) AS (
SELECT name, id, slug, parent_id, 0
FROM categories
where id in (
SELECT c.id FROM items AS p
INNER JOIN items_categories AS pc ON p.id=pc.item_id
INNER JOIN categories AS c ON pc.category_id = c.id
WHERE p.id = 10
)
UNION ALL
SELECT c2.name, c2.id, c2.slug, c2.parent_id, bread_order+1
FROM categories AS c2
INNER JOIN descendants AS d ON c2.id=d.parent_id
) SELECT id, name, parent_id, bread_order FROM descendants
ORDER by bread_order DESC
The problem is that two child Categories can have the same parent, or a parent for one is grand parent for the other.
C1 -> B1 -> A1
C2 ->B1 -> A1
C3 ->B2  -> A1
DE -> D1 -> A1
DT -> A1
I tried to group them but, because have the same parents, grand_parents is not what I need, I need when I retrieve the information from database to know how to make the path(See above).
Is there any way, like using CASE with the IDs that returns from the subquery ?
You can aggregate the path during the query:
WITH RECURSIVE descendants(name, id, parent_id, bread_order, path) AS (
SELECT name, id, parent_id, 0, array[id] as path
FROM categories
where id in (SELECT c.id
FROM items AS p
JOIN items_categories AS pc ON p.id=pc.item_id
JOIN categories AS c ON pc.category_id = c.id
WHERE p.id = 1)
UNION ALL
SELECT c2.name, c2.id, c2.parent_id, bread_order+1, d.path||c2.id
FROM categories AS c2
JOIN descendants AS d ON c2.id=d.parent_id
)
SELECT id, name, parent_id, path, bread_order
FROM descendants
ORDER by path

sql query to count number of users based on event sequence

I have a table called test which is sorted by time.
user_id event time
1 e1 t1
1 e3 t2
1 e2 t3
2 e2 t4
2 e1 t5
2 e5 t6
3 e2 t7
3 e4 t8
I have to find out how many unique user_id is there in which event e1 happens before e2. here the answer is one with user_id 1.
I am using postgresql.
Any help would be much appreciated.
This is probably your solution, with a sub-select of events where ev2:
WITH event(user_id,event,time) AS (
VALUES (1,'e1','t1'),
(1,'e3','t2'),
(1,'e2','t3'),
(2,'e2','t4'),
(2,'e1','t5'),
(2,'e5','t6'),
(3,'e2','t7'),
(3,'e4','t8'))
SELECT count(event.event) FROM event
JOIN (SELECT user_id, time
FROM event WHERE event = 'e2') AS ev2 ON event.user_id = ev2.user_id
WHERE event.time < ev2.time AND event.event = 'e1'
Filter all rows before ev2 takes place and the value should be equal to ev1.
SELECT e.user_id,
Count(e.event)
FROM event e
join(SELECT user_id,
TIME
FROM event
WHERE event = 'e2') AS ee
ON e.user_id = ee.user_id
WHERE e.TIME < ee.TIME
AND e.event = 'e1'
GROUP BY e.user_id

How can i query the last data from 3 tables

now i have 3 tables, for example A,B,C
the relation between them is A onetomany B, B onetomany C.
C is a table saved photos
now i want get data from A, but only the last photo each A.
the colomns maybe like this:
table a:
id a_msg
a1 msg in a
a2 msg in a
a3 msg in a
table b:
id b_msg a_id
b1 some data in b a1
b2 some data in b a1
b3 some data in b a2
b4 some data in b a3
table c:
id url createdate c_msg b_id
c1 /file/1.jpg 2014-12-01 06:55:54.600 some data in c b1
c2 /file/2.jpg 2014-12-01 06:55:54.601 some data in c b1
c3 /file/3.jpg 2014-12-01 06:55:54.602 some data in c b1
c4 /file/4.jpg 2014-12-01 06:55:54.603 some data in c b2
c5 /file/5.jpg 2014-12-01 06:55:54.604 some data in c b2
c6 /file/6.jpg 2014-12-01 06:55:54.605 some data in c b3
the result i want get
c_id url createdate c_msg b_msg b_id a_msg a_id
c6 /file/6.jpg 2014-12-01 06:55:54.605 some data in c some data in b b3 msg in a a1
c5 /file/5.jpg 2014-12-01 06:55:54.604 some data in c some data in b b2 msg in a a1
Sorry ,i don't know how to use tool to describle the table,hope you can easily understand what i mean.
if my description is not clear enough,i will edit the question,thank you if anyone can help me
Consider the following as an example :
create table table_a (id int,a_msg text);
create table table_b (id int,b_msg text,a_id int);
create table table_c (id int,url text,createdate timestamp with time zone,c_msg text ,b_id int);
and the data
insert into table_a values (1,'msg in table_a')
,(2,'2nd msg in table_a')
,(3,'3rd msg in table_a');
insert into table_b values (20,'msg in table_b',1)
,(21,'2nd msg in table_b',2)
,(22,'3rd msg in table_b',3);
insert into table_c values (30,'url','2014-12-01 06:55:54.600','msg in table_c',20)
,(31,'url 1','2014-12-01 06:55:54.604','2nd msg in table_c',21)
,(32,'url 2','2014-12-01 06:55:54.605','3rd msg in table_c',22);
to get the result you need to use INNER JOIN and to get the last two data use order by createdate desc limit 2
select c.id,c.url
,c.createdate
,c.c_msg,b.b_msg
,b.id bi_id,a.a_msg
,a.id a_id
from
table_c c inner join table_b b on c.b_id=b.id /* to get data from table_b */
inner join table_a a on b.a_id=a.id /* to get data from table_a */
order by createdate desc limit 2 /* DESC will sort from the highest date time values and LIMIT 2 will return two rows */
>SQLFIDDLE DEMO WITH OP'S DATA

How to get GroupName and GroupMembers in a Row

I have a table with GroupMembers and GorupName in 2 Columns as
Col 1 GroupMember Col2 GroupName
A1 A
A2 A
B1 B
B2 B
C1 C
C2 C
How to get output result as
A - GroupName
A1 - GroupMember
A2 - GroupMember
B
B1
B2
C
C1
C2
Here I am trying to get the GroupName and its GroupMembers in a single Column
;with Groups AS
(
select distinct GroupName from YourTableName
)
,OrderedGroups AS
(
select GroupName, ROW_NUMBER() Over(order by GroupName) R from Groups
)
,RankedData As
(
select T.GroupMember, T.GroupName, OG.R from YourTableName T
inner join OrderedGroups OG on T.GroupName = OG.GroupName
)
select GroupMember, R from RankedData
union
select GroupName, R from RankedData
order by R