如何实现两表关联查询:兼容任意表无对应结果的场景?
现有两张表:
表A
| Person ID | BloodPressure | HeartRate |
|---|---|---|
| 1 | 140/77 | 88 |
| 2 | 138/75 | 76 |
表B
| Person ID | HeightInches | WeightLbs |
|---|---|---|
| 1 | 68 | 170 |
| 3 | 71 | 165 |
我想用单条查询语句从两张表中获取数据,但找不到合适的JOIN方式,甚至怀疑JOIN是不是正确的方案。
执行以下查询:
SELECT A.BloodPressure, A.HeartRate, B.HeightInches, B.WeightLbs FROM A LEFT JOIN B ON A.PersonId = B.PersonId WHERE A.PersonId = 1
能得到预期结果:
| Person ID | BloodPressure | HeartRate | HeightInches | WeightLbs |
|---|---|---|---|---|
| 1 | 140/77 | 88 | 68 | 170 |
但用同一查询查找PersonId=3的数据时,没有任何结果返回;查找PersonId=2时,返回结果符合预期(表B对应字段为NULL):
| Person ID | BloodPressure | HeartRate | HeightInches | WeightLbs |
|---|---|---|---|---|
| 2 | 138/75 | 76 | NULL | NULL |
我知道PersonId=3无结果的原因:表A里没有这个ID的数据,导致JOIN时无法匹配表B的对应行。
我需要获取包含表A和表B所有字段的结果集,允许任意表的字段值为NULL,但要做到无需提前知道哪个表存在对应PersonId数据时,仍能返回PersonId=3的结果。请问这个需求能实现吗?如果可以,查询语句需要怎么修改?
这个需求完全可以实现,核心是要覆盖两张表中所有的Person ID——不管它只存在于A、只存在于B,还是同时存在于两张表。以下是几种可行方案:
方案1:使用FULL OUTER JOIN(主流数据库通用)
PostgreSQL、SQL Server等主流数据库支持FULL OUTER JOIN,它会返回两张表的所有行:ID同时存在时合并字段,ID仅存在于某一张表时,另一张表的字段自动补NULL。
修改后的查询语句:
SELECT COALESCE(A.PersonId, B.PersonId) AS "Person ID", A.BloodPressure, A.HeartRate, B.HeightInches, B.WeightLbs FROM A FULL OUTER JOIN B ON A.PersonId = B.PersonId WHERE COALESCE(A.PersonId, B.PersonId) = 3;
COALESCE(A.PersonId, B.PersonId)用于提取非NULL的Person ID(因为ID只在A中时B.PersonId为NULL,反之亦然)。- 查询PersonId=3时,返回结果:
| Person ID | BloodPressure | HeartRate | HeightInches | WeightLbs |
|---|---|---|---|---|
| 3 | NULL | NULL | 71 | 165 |
方案2:兼容不支持FULL OUTER JOIN的数据库(如MySQL)
如果你的数据库不支持FULL OUTER JOIN,可以用UNION ALL合并LEFT JOIN和RIGHT JOIN的结果,确保覆盖所有ID:
SELECT COALESCE(A.PersonId, B.PersonId) AS "Person ID", A.BloodPressure, A.HeartRate, B.HeightInches, B.WeightLbs FROM A LEFT JOIN B ON A.PersonId = B.PersonId UNION ALL SELECT B.PersonId AS "Person ID", A.BloodPressure, A.HeartRate, B.HeightInches, B.WeightLbs FROM B LEFT JOIN A ON A.PersonId = B.PersonId WHERE A.PersonId IS NULL;
这条语句先获取所有A中存在的ID(含匹配B的),再补充只存在于B中的ID,合并后就是完整的结果集。若需查询特定ID,可在两个SELECT分支中添加WHERE条件,或在外层套一层查询过滤。
方案3:先获取所有Person ID集合再关联
若需要更灵活的控制,可先通过子查询提取所有不重复的Person ID,再分别与A、B做LEFT JOIN:
SELECT ids.PersonId AS "Person ID", A.BloodPressure, A.HeartRate, B.HeightInches, B.WeightLbs FROM ( SELECT PersonId FROM A UNION SELECT PersonId FROM B ) AS ids LEFT JOIN A ON ids.PersonId = A.PersonId LEFT JOIN B ON ids.PersonId = B.PersonId WHERE ids.PersonId = 3;
子查询ids会返回两张表中所有唯一的Person ID,之后通过LEFT JOIN关联A和B,保证每个ID都能被查询到,不存在的表字段自动补NULL。
内容的提问来源于stack exchange,提问作者Nasmarr

