在联接的 table 中为 SQL 中的每个 ID 返回一个结果

Returning one result per id in SQL in a joined table

我有一个 table 用户。

users

| user_id | name   |
| ------- | ------ |
| 1       | Jerry  |
| 2       | George |
| 3       | Elaine |
| 4       | Kramer |

我有一个 table 将角色链接到用户,并且角色是在树上分配的。

user_roles

| user_id | role_id | tree_id |
| ------- | ------- | ------- |
| 1       | 5       | 1       |
| 1       | 5       | 2       |
| 2       | 6       | 1       |
| 3       | 7       | 1       |
| 4       | 8       | 1       |

我只需要 return 在某个 tree_id 分配用户角色的结果,所以我正在检查所有角色和树。最后,我希望每个用户 return 一行。

我正在使用 Knex 并执行如下查询:

knex('users')
  .leftJoin('user_roles', {'user.user_id': 'user_roles.user_id'})
  .whereIn('user_roles.tree_id', arrayOfTreeIds)
  .andWhere(moreFilters)
SELECT *
FROM users
LEFT JOIN user_roles on users.user_id = user_roles.user_id
WHERE user_roles.tree_id in (1, 2, 3)

不过,我得到了五个结果,而不是四个。如果我尝试 SELECT DISTINCT,它告诉我我需要 GROUP BY,但我无法让它工作。我需要做什么才能让每个用户 ID 只获得一个结果?

您有一个用户在两个不同的 tree_id 上匹配,所以这会增加行数。

在纯 SQL 中,您可以使用 exists 而不是 join:

SELECT *
FROM users u
WHERE EXISTS (
    SELECT 1
    FROM user_roles ur
    WHERE ur.user_id = u.user_id AND ur.tree_id in (1, 2, 3)
)

另一种选择是聚合:

SELECT u.*
FROM users u
INNER JOIN user_roles ur on u.user_id = ur.user_id
WHERE ur.tree_id in (1, 2, 3)
GROUP BY u.user_id

我将 LEFT JOIN 更改为 INNER JOIN,因为从本质上讲,这就是您想要的(以及您的原始查询所做的)。

您甚至可以使用字符串聚合列出匹配的角色:

SELECT u.*, STRING_AGG(ur.tree_id::text, ',' ORDER BY ur.tree_id) tree_ids
FROM users u
INNER JOIN user_roles ur on u.user_id = ur.user_id
WHERE ur.tree_id in (1, 2, 3)
GROUP BY u.user_id

免责声明:我不知道如何在 knex 中写这个!

Demo on DB Fiddle