How to use join with aggregate function in postgresql? - postgresql

I have 4 tables
Table1
id | name
1 | A
2 | B
Table2
id | name1
1 | C
2 | D
Table3
id | name2
1 | E
2 | F
Table4
id | name1_id | name2_id | name3_id
1 | 1 | 2 | 1
2 | 2 | 2 | 2
3 | 1 | 2 | 1
4 | 2 | 1 | 1
5 | 1 | 1 | 2
6 | 2 | 2 | 1
7 | 1 | 1 | 2
8 | 2 | 1 | 1
9 | 1 | 2 | 1
10 | 2 | 2 | 1
Now I want to join all tables with 4 and get this type of output
name | count
{A,B} | {5, 5}
{C,D} | {5, 6}
{E,F} | {7, 3}
I tried this
select array_agg(distinct(t1.name)), array_agg(distinct(temp.test))
from
(select t4.name1_id, (count(t4.name1_id)) "test"
from table4 t4 group by t4.name1_id
) temp
join table1 t1
on temp.name1_id = t1.id
I am trying to achieve this. Anybody can help me.

Calculate the counts for every table separately and union the results:
select
array_agg(name order by name) as name,
array_agg(count order by name) as count
from (
select 1 as t, name, count(*)
from table4
join table1 t1 on t1.id = name1_id
group by name
union all
select 2 as t, name, count(*)
from table4
join table2 t2 on t2.id = name2_id
group by name
union all
select 3 as t, name, count(*)
from table4
join table3 t3 on t3.id = name3_id
group by name
) s
group by t;
name | count
-------+-------
{A,B} | {5,5}
{C,D} | {4,6}
{E,F} | {7,3}
(3 rows)

Related

Get dummy columns from different tables

I have three different tables that look like that:
Table 1
| id | city|
|----|-----|
| 1 | A |
| 1 | B |
| 2 | C |
Table 2
| id | city|
|----|-----|
| 2 | B |
| 1 | B |
| 3 | C |
Table 3
| id | city|
|----|-----|
| 1 | A |
| 1 | B |
| 2 | A |
I need to create one column for each table, and the dummies values if it's present.
| id | city| is_tbl_1 | is_tbl_2 | is_tbl_3 |
|----|-----|-----------|-------------|------------|
| 1 | A | 1 | 0 | 1 |
| 1 | B | 1 | 1 | 1 |
| 2 | A | 0 | 0 | 1 |
| 2 | C | 1 | 0 | 0 |
| 2 | B | 0 | 1 | 0 |
| 3 | C | 0 | 1 | 0 |
I have tried to add the columns is_tbl# myself on three different selects, UNION all the three tables and group, but it looks ugly, is there a better way to do it?
You can outer-join the 3 tables on id and city, then group by the id and city, and finally count the number of non-null values of the city columns :
SELECT
COALESCE (t1.id, t2.id, t3.id) AS id
, COALESCE (t1.city, t2.city, t3.city) AS city
, count(*) FILTER (WHERE t1.city IS NOT NULL) AS is_tbl_1
, count(*) FILTER (WHERE t2.city IS NOT NULL) AS is_tbl_2
, count(*) FILTER (WHERE t3.city IS NOT NULL) AS is_tbl_3
FROM
t1 AS t1
FULL OUTER JOIN
t2 AS t2 ON t1.id = t2.id AND t1.city = t2.city
FULL OUTER JOIN
t3 AS t3 ON t1.id = t3.id AND t1.city = t3.city
GROUP BY
1,2
ORDER BY
1,2

Reverse column values after grouping

I have a table as follows
id | group | value
------+-------------+----------
1 | 1 | 2
2 | 1 | 4
3 | 1 | 3
4 | 2 | 2
5 | 2 | 9
6 | 2 | 5
I want to group the rows by 'group' with the order of 'id' and create a new column that reverses the 'value' column as follows
id | group | value | reversedvalue
------+-------------+---------+---------
1 | 1 | 2 | 3
2 | 1 | 4 | 4
3 | 1 | 3 | 2
4 | 2 | 2 | 5
5 | 2 | 9 | 9
6 | 2 | 5 | 2
Try the following:
SELECT q1.id,q1.group_id,q1.value,q2.value
FROM
(
SELECT *,ROW_NUMBER()OVER(PARTITION BY group_id ORDER BY id) n
FROM your_table
) q1
JOIN
(
SELECT *,ROW_NUMBER()OVER(PARTITION BY group_id ORDER BY id DESC) n
FROM your_table
) q2
ON q1.group_id=q2.group_id AND q1.n=q2.n
You also can use CTE:
WITH cte AS(
SELECT *,
ROW_NUMBER()OVER(PARTITION BY group_id ORDER BY id) n1,
ROW_NUMBER()OVER(PARTITION BY group_id ORDER BY id DESC) n2
FROM your_table
)
SELECT q1.id,q1.group_id,q1.value,q2.value
FROM
cte q1
JOIN
cte q2
ON q1.group_id=q2.group_id AND q1.n1=q2.n2;

How to find the number of matches a player has played?

I have two tables namely match and player. I am trying to find the total number of matches played by each player by adding no_of_wins and no_of_loses columns.
player:
id | name
----|----
1 | Suhas
2 | Srivats
3 | James
4 | Watson
match:
id | winner | loser
----|--------|-------
1 | 1 | 2
2 | 1 | 3
3 | 1 | 4
4 | 2 | 4
5 | 4 | 3
6 | 3 | 2
I tried the following SQL command:
select p.id, p.name, count(m.winner) as no_of_wins,count(m.loser) as no_of_loses from player as p left join match as m on p.id=m.winner group by p.id order by p.id;
This command shows the wrong output for the number of loses.
id | name | no_of_wins | no_of_loses
----|---------|------------|-------------
1 | Suhas | 3 | 3
2 | Srivats | 1 | 1
3 | James | 1 | 1
4 | Watson | 1 | 1
Kindly help.
Calculate aggregated numbers of wins and loses for a player in two queries and (full) join them by a player id:
select
name,
coalesce(wins, 0) as no_of_wins,
coalesce(loses, 0) as no_of_loses,
coalesce(wins, 0) + coalesce(loses, 0) as total
from (
select winner as id, count(*) as wins
from match
group by 1
) w
full join (
select loser as id, count(*) as loses
from match
group by 1
) l using (id)
full join player using(id)
order by id;
name | no_of_wins | no_of_loses | total
---------+------------+-------------+-------
Suhas | 3 | 0 | 3
Srivats | 1 | 2 | 3
James | 1 | 2 | 3
Watson | 1 | 2 | 3
(4 rows)
Your query will cause an error because you didn't add p.name to the GROUP BY clause.
You'll have to join match twice, because these are two independent joins:
SELECT p.id,
p.name,
COALESCE(w.wins, 0) no_of_wins,
COALESCE(l.losses, 0) no_of_losses
FROM player p
LEFT JOIN
(SELECT winner id,
count(*) wins
FROM match
GROUP BY winner
) w
USING (id)
LEFT JOIN
(SELECT loser id,
count(*) losses
FROM match
GROUP BY loser
) l
USING (id);

Incrementally count row numbers for distrinct rows in a join select

I have a select that joins two tables, a and b, via a join table, ab.
select a.*, b.*
from a
left join ab on a.id = ab.aid
left join b on b.id = ab.bid;
And this produces
id | athing | id | bthing
----+----------+----+-----------
7 | athing x | 1 | bthing a
7 | athing x | 2 | bthing b
7 | athing x | 3 | bthing c
3 | athing y | 1 | bthing a
(4 rows)
I want a column that incrementally counts the number of rows in a. That is:
count | id | athing | id | bthing
-------+----+----------+----+-----------
1 | 7 | athing x | 1 | bthing a
1 | 7 | athing x | 2 | bthing b
1 | 7 | athing x | 3 | bthing c
2 | 3 | athing y | 1 | bthing a
(4 rows)
I have looked at using the window function row_number(), but that seems to count all the rows.
I want to incrementally count the distinct a rows, regardless of how many rows the joined table creates.
Is this possible in Postgresql? Thank you.
Use row_number() when selecting from the table a (note, the order of the rows in a is defined in over clause):
select a.*, b.*
from (
select row_number() over (order by id desc) as count, *
from a
) a
left join ab on a.id = ab.aid
left join b on b.id = ab.bid;
count | id | athing | id | bthing
-------+----+----------+----+----------
1 | 7 | athing x | 1 | bthing a
1 | 7 | athing x | 2 | bthing b
1 | 7 | athing x | 3 | bthing c
2 | 3 | athing y | 1 | bthing a
(4 rows)
or dense_rank() on the result dataset.
select
dense_rank() over (order by a.id desc) as count,
a.*, b.*
from a
left join ab on a.id = ab.aid
left join b on b.id = ab.bid;
Read about window functions.

Postgresql joining one column with 2 not null columns

I have 2 tables that I want match by ID.
EDIT: Table1:
| id | other columns A |
| 23 | ... |
| 27 | ... |
| 9 | ... |
| 50 | ... |
Table2:
| id_new | id_old | other columns B
| 23 | 7 | ...
| 27 | 8 | ...
| 33 | 9 | ...
Problem is that the second table contains 2 ID columns: first with new ID second with the old one - both can match the ID from first table.
EDIT: there are some rows from table A which ID not match neither id_new nor id_old. But I want them to retain in the new table.
This is my desired result:
| id | id_new | id_old | other columns A + B
| 23 | 23 | 7 | A + B
| 27 | 27 | 8 | A + B
| 9 | 33 | 9 | A + B
| 50 | -- | -- | A
I tried this one but it's a huge dataset and my query takes a long time to execute.
create table spoj2
as
select *
from table1
left join table2 on table1.id = table2.id_new
or table1.id = table2.id_old
Is this what you need?
select table1.id, IFNULL(T2A.id_new, T2B.id_new) as id_new
, IFNULL(T2A.id_old, T2B.id_old) as id_old
FROM table1
LEFT JOIN table2 as T2A ON table1.id = T2A.id_new
LEFT JOIN table2 as T2B ON table1.id = T2B.id_old
WITH t1(id,o_t1) AS ( VALUES
(23,'...'),
(27,'...'),
(9,'...')
), t2(id_new,id_old,o_t2) AS ( VALUES
(23,7,'...'),
(27,8,'...'),
(33,9,'...')
)
SELECT t1.id,t2.id_new,t2.id_old,t1.o_t1,t2.o_t2 FROM t1
INNER JOIN t2 ON t2.id_new = t1.id
UNION ALL
SELECT t1.id,t2.id_new,t2.id_old,t1.o_t1,t2.o_t2 FROM t1
INNER JOIN t2 ON t2.id_old = t1.id;
Result:
id | id_new | id_old | o_t1 | o_t2
----+--------+--------+------+------
23 | 23 | 7 | ... | ...
27 | 27 | 8 | ... | ...
9 | 33 | 9 | ... | ...
(3 rows)