Postgres复杂的完整外连接保持“on”列

时间:2017-11-20 19:54:18

标签: sql postgresql join

我编写了一个相对高性能的PostgresSQL查询,并为我提供了我想要的数据集,但我想知道它是否是编写查询的最简单/最好的方法。似乎应该有一个更简单的连接操作来满足我需要的条件。

编辑:我确实需要这个才能在大型表格上表现出色。在下面给出的例子中,宠物是1.5亿行,食物大约是100k行。我的解决方案在底部时钟约为0.6毫秒。两个表都有id和user_id的索引。食物表还包括pet_id的索引。

我的系统中有两个与一个保证共享属性相关的表 - user_id。这是一个实质上显示我的问题的例子:

宠物

+------+-------+---------+
|  id  | type  | user_id |
+------+-------+---------+
| 1234 | dog   |       1 |
| 1235 | cat   |       1 |
| 1236 | gecko |       1 |
+------+-------+---------+

食品

+------+-----------+---------+--------+
|  id  |   name    | user_id | pet_id |
+------+-----------+---------+--------+
| 4321 | hamburger |       1 | NULL   |
| 4322 | dog food  |       1 | 1234   |
| 4323 | cat food  |       1 | 1235   |
+------+-----------+---------+--------+

期望的结果

+------+------+
| p.id | f.id |
+------+------+
| NULL | 4321 |  --no pet, hamburger
| 1234 | 4322 |  --dog, dog food
| 1235 | 4323 |  --cat, cat food
| 1236 | NULL |  --gecko, no food
+------+------+

现在有一个例子可以参考,我会确定结果是什么。结果包含属于我的user_id的两侧的所有行(假设该表可能包含数千个不属于user_id 1的其他行)。我希望这些结果行包含与另一个表匹配的每一行的一个副本。

我尝试使其完成外部联接的示例:

SELECT p.id, f.id
FROM pets p FULL OUTER JOIN food f ON p.user_id = f.user_id
WHERE p.user_id = 1;

此查询中存在一些问题,因为

  1. 它会从查询左侧排除NULL个。我需要那些。
  2. 因为user_id在这里基本上是常量,所以我最终得到了大量的重复项,因为它与user_id匹配。左边的每一行都与右边的每一行匹配。不是我需要的。我需要一对一的比赛。
  3. 我可以通过在OR过滤器中添加WHERE来修复#1:

    SELECT p.id, f.id
    FROM pets p FULL OUTER JOIN food f ON p.user_id = f.user_id
    WHERE p.user_id = 1 OR f.user_id = 1;
    

    由于我不完全确定的原因,它会使查询花费很长时间。在我们的系统中,两个表都有一个user_id索引,因此不缺少索引。

    为了解决我的问题,我登陆了以下查询(实际上是两个组合):

    SELECT p.id, f.id
        FROM pets p LEFT JOIN food f
            ON p.id = f.pet_id AND f.user_id = 1
        WHERE p.user_id = 1
    UNION
    SELECT p.id, f.id FROM pets p RIGHT JOIN food f
            ON p.id = f.pet_id
        WHERE f.user_id = 1 AND p.id IS NULL;
    

    所以我的问题是:有一种更简单的方法可以将其作为单个查询执行吗?

2 个答案:

答案 0 :(得分:3)

<强> SQL DEMO

SELECT p.id, f.id
FROM pets p 
FULL OUTER JOIN food f 
  ON p.user_id = f.user_id
 AND p.id = f.pet_id
 AND p.user_id = 1;

输出

|     id |     id |
|--------|--------|
|   1234 |   4322 |
|   1235 |   4323 |
|   1236 | (null) |
| (null) |   4321 |

注意:

您应该在(user_id, pet_id)上为两个表添加复合索引。

答案 1 :(得分:2)

你只是过度思考这一点。您想加入P.ID = F.PET_ID

SELECT P.ID, F.ID
FROM PETS P
FULL OUTER JOIN FOOD F ON P.ID = F.PET_ID
                      AND P.USER_ID = F.USER_ID
                      AND P.USER_ID = 1 --optional                          
ORDER BY P.ID