我有三个与另一个表关联的Postgresql表:
USER(id_user,first_name,last_name)
ADDRESS(id_address,id_user,address)
BANK_CARD(id_bank_card,id_user,number)
NETWORK(id_network,id_user,status)
用户可以拥有多个地址,银行卡和网络。
例如对于USER表:
id_user first_name last_name
-------------------------------------
1 John Doe
2 David Smith
对于ADDRESS表:
id_address id_user address
----------------------------------
10 1 address1
对于BANK_CARD:
id_bank_card id_user number
-----------------------------------
20 1 1234
21 1 5678
对于NETWORK:
id_network id_user status
--------------------------------
30 1 status1
31 1 status2
32 1 status3
我想检索用户和其他表之间的现有关联,如下所示:
id_user id_address address id_bank_card number id_network status
-----------------------------------------------------------------------
1 10 address1 20 1234 30 status1
1 NULL NULL 21 5678 31 status2
1 NULL NULL NULL NULL 32 status3
我尝试使用JOIN或UNION进行不同的查询,但没有任何效果。谢谢你的帮助。
答案 0 :(得分:1)
其中一种方法是使用union all
和聚合:
select id_user, max(id_address) as id_address,
max(id_bank_card) as id_bank_card,
max(number) as number,
max(id_network) as id_bank_card,
max(status) as status)
from ((select id_user, id_address, NULL as id_bank_card, NULL as number,
NULL as id_network, NULL as status
row_number() over (partition by id_user order by id_address) as seqnum
from address
) union all
(select id_user, NULL as id_address, id_bank_card, number,
NULL as id_network, NULL as status
row_number() over (partition by id_user order by id_bank_card) as seqnum
from bank_card
) union all
(select id_user, NULL as id_address, NULL as id_bank_card, NULL as number,
id_network, status
row_number() over (partition by id_user order by id_network) as seqnum
from network
)
) abcn
group by id_user, seqnum;
您可能会发现将结果放在数组中更实用:
select *
from (select id_user, array_agg(id_address) as id_addresses
from address
group by id_user
) a full outer join
(select id_user, array_agg(id_bank_card) as id_bank_cards,
array_agg(number) as numbers
from bank_card
group by id_user
) bc
using (id_user) full outer join
(select id_user, array_agg(id_network) as id_networds,
array_agg(status) as statuses
from network
group by id_user
) n
using (id_user);
答案 1 :(得分:0)
LEFT JOIN
可以帮助你完成大部分工作。
最混乱的是你实际上想要没有明确关系但没有重复的连接。
下面的答案介绍了一个额外的计算字段,以便:
- 第一张银行卡总是被分配到第一个地址
- 第一个网络总是被分配到第一个银行卡
- 第二张银行卡总是被分配到第二个地址
- 第二个网络总是被分配到第二个银行卡
- 等等
WITH
sorted_address AS
(
SELECT
ROW_NUMBER() OVER (PARTITION BY id_user ORDER BY id_address) AS ordinal,
*
FROM
address
),
sorted_bank_card AS
(
SELECT
ROW_NUMBER() OVER (PARTITION BY id_user ORDER BY id_bank_card) AS ordinal,
*
FROM
bank_card
),
sorted_network AS
(
SELECT
ROW_NUMBER() OVER (PARTITION BY id_user ORDER BY id_network) AS ordinal,
*
FROM
network
)
SELECT
*
FROM
user
LEFT JOIN
sorted_address
ON sorted_address.id_user = user.id_user
LEFT JOIN
sorted_bank_card
ON sorted_bank_card.id_user = user.id_user
AND sorted_bank_card.ordinal = sorted_address.ordinal
LEFT JOIN
sorted_network
ON sorted_network.id_user = user.id_user
AND sorted_network.ordinal = COALESCE(sorted_bank_card.ordinal, sorted_address.ordinal)
作为旁注:我建议不要将user
等保留关键字用于表名。