通过关联从 has many 中查找匹配和不匹配记录的单次出现

Find single occurrence of matched and non-matched records from has many through association

我有 3 个 Postgres table

  1. groups_favourites
  2. 最爱

这 3 个 table 的结构是:

groups
--------------------------
| id |  name   | user_id |
--------------------------
| 1  | Group A |   100   |
| 2  | Group B |   100   |
| 3  | Group C |   100   |
| 4  | Group D |   100   |
--------------------------

favourites_groups
-------------------------------
| id | group_id | favorite_id |
-------------------------------
| 1  |  2       | 10          |
| 2  |  2       | 12          |
| 3  |  2       | 14          |
| 4  |  2       | 15          |
| 5  |  2       | 16          |
| 6  |  3       | 12          |
| 7  |  4       | 10          |
| 8  |  4       | 11          |
| 9  |  4       | 12          |
| 10 |  4       | 13          |
-------------------------------
UNIQUE INDEX ON [group_id, favorite_id]

favourites
-------------------------------
| id | product_id  | user_id  |
-------------------------------
| 10 |    1000     |   100    |
| 11 |    1001     |   100    |
| 12 |    1002     |   100    |
| 13 |    1003     |   100    |
| 14 |    1004     |   100    |
| 15 |    1005     |   100    |
| 16 |    1006     |   100    |
-------------------------------

我在后端的 Rails 上使用 Ruby,这 3 个 table 之间的关系是通过 [=14= 设置的]协会

我想要完成的结果是:对于给定的产品和用户,我想找到所有在 favorite_groups table 中有条目的组 + 所有没有条目的组在 favorites_groups table.

假设 user_id = 100 AND product_id = 1000,结果将是:

| group_id | group_name | favorite_id | product_id |
----------------------------------------------------
|    1     |  Group A   |    NULL     |    NULL    |
|    2     |  Group B   |     10      |    1000    |
|    3     |  Group C   |    NULL     |    NULL    |
|    4     |  Group D   |     10      |    1000    |
----------------------------------------------------

另一个用例是 user_id = 100 AND product_id = 1002,结果是:

| group_id | group_name | favorite_id | product_id |
----------------------------------------------------
|    1     |  Group A   |    NULL     |    NULL    |
|    2     |  Group B   |     12      |    1002    |
|    3     |  Group C   |     12      |    1002    |
|    4     |  Group D   |     12      |    1002    |
----------------------------------------------------

我正在使用的当前 SQL 给我 1 行 NULL favorite_idproduct_id 以及 1 行匹配的 favorite_idproduct_id

这是我的 SQL:

SELECT DISTINCT ON (group_id, favorite_id, product_id) groups.id AS group_id, groups.name AS group_name, favorites.id AS favorite_id, favorites.product_id AS product_id
FROM groups
LEFT JOIN favorite_groups ON favorite_groups.group_id = groups.id
LEFT JOIN favorites ON favorites.id = favorite_groups.favorite_id AND favorites.product_id = 1002
WHERE groups.user_id = 100

由于此查询返回的结果,我必须额外工作以遍历每一行并准备我想要的结果集。

有更好的方法吗?

在此先感谢您的帮助!

您的模型有些奇怪。 groups.namegroupsfavourites 中出现的 user_id 之间的关系尚不清楚。 favourites_groups 上的 unique 约束应该使 favourites 中的 user_id 变得不必要,所以我添加了一个注释掉的 join 条件。

请试试这个查询,看看它是否return是您需要的:

select g.id as group_id, g.name as group_name
  from groups g
  left join favourites_groups fg
    on fg.group_id = g.id
  left join favourites f
    on f.id = fg.favorite_id
 --  and f.user_id = g.user_id 
 where g.user_id = 100
   and f.product_id = 1002
;

更新 对于那个很抱歉。这应该 return 你想要什么:

select g.id as group_id, g.name as group_name,
       max(f.id) as favorite_id, 
       max(f.product_id) as product_id
  from groups g
  left join favourites_groups fg 
    on fg.group_id = g.id
  left join favourites f 
    on f.id = fg.favorite_id
   and f.product_id = 1000
 where g.user_id = 100
 group by g.id, g.name
 order by g.id;