我有用户,有很多帖子。我想构建一个SQL查询,它将在1个查询(没有子查询)中执行以下操作,并且如果可能的话,希望没有联合。我知道我可以用联盟做到这一点,但我想知道是否可以仅使用连接来完成。
我想获得一个不同的活跃用户列表:
这是我到目前为止所拥有的:
SELECT DISTINCT u.*
FROM users u
LEFT JOIN posts p
ON p.user_id = u.id
LEFT JOIN posts p2
ON p2.user_id = u.id
WHERE u.status = 'active'
AND (p.status IS NULL
OR p2.status != 'approved');
问题是用户有多个帖子且一个是活动的。这仍然会返回我不想要的用户。如果用户有活动帖子,则应将其从结果集中删除。有什么想法吗?
这是数据的样子:
mysql> select * from users;
+----+---------+
| id | status |
+----+---------+
| 1 | active |
| 2 | pending |
| 3 | pending |
| 4 | active |
| 5 | active |
+----+---------+
5 rows in set (0.00 sec)
mysql> select * from posts;
+----+---------+----------+
| id | user_id | status |
+----+---------+----------+
| 1 | 1 | approved |
| 2 | 1 | pending |
| 3 | 4 | pending |
+----+---------+----------+
3 rows in set (0.00 sec)
这里的答案应该只是用户4和5. 4没有批准的帖子,5没有帖子。它不应该包括1,它有一个批准的帖子。
答案 0 :(得分:2)
不存在:
SELECT u.*
FROM users u
WHERE NOT EXISTS (
SELECT 1
FROM posts p
WHERE p.user_id = u.id AND p.status = 'approved');
或等效LEFT JOIN
SELECT u.*
FROM users u
LEFT JOIN posts p
ON p.user_id = u.id AND p.status = 'approved'
WHERE p.user_id IS NULL;
答案 1 :(得分:1)
请解释为什么你不想要JOIN或UNION。如果是因为性能,请考虑以下因素:
CREATE TABLE t ( PRIMARY KEY(user_id) )
SELECT user_id, MIN(status) AS z
FROM Posts
GROUP BY user_id;
SELECT u.id AS user,
IFNULL(z, 'no_posts') AS status
FROM users u
WHERE u.status = 'active'
LEFT JOIN t ON t.user_id = u.id
HAVING status != 'approved';
每个表只会传递一次,因此效率相当高(考虑到查询的复杂性)。
答案 2 :(得分:1)
这个可能会有所帮助:
SELECT DISTINCT u.*
FROM users u
LEFT JOIN posts p ON 1=1
-- matches only if user has any post
AND p.user_id = u.id
-- matches only if user has any active post
AND p.status = 'approved'
WHERE 1=1
-- matches only active users
AND u.status = 'active'
-- matches only users with no matches on the LEFT JOIN
AND p.status IS NULL
;
答案 3 :(得分:1)
根据您的要求并将它们逐字地翻译成SQL,我明白了:
SELECT users.id,
COUNT(posts.id) as posts_count,
COUNT(approved_posts.id) as approved_posts_count
FROM users
LEFT JOIN posts ON posts.user_id = users.id
LEFT JOIN posts approved_posts
ON approved_posts.status = 'approved'
AND approved_posts.user_id = users.id
WHERE users.status = "active"
GROUP BY users.id
HAVING (posts_count = 0 OR approved_posts_count = 0);
对于上面的测试数据,返回:
4|1|0
5|0|0
即。使用ids 4
和5
的用户,其中第一个帖子有1个帖子但没有已批准的帖子,第二个帖子没有帖子。
但是,在我看来,这可以简化,因为任何没有批准帖子的用户也没有帖子,因此条件的结合是不必要的。
在这种情况下,SQL只是:
SELECT users.id,
COUNT(approved_posts.id) as approved_posts_count
FROM users
LEFT JOIN posts approved_posts
ON approved_posts.status = 'approved'
AND approved_posts.user_id = users.id
WHERE users.status = "active"
GROUP BY users.id
HAVING approved_posts_count = 0;
这也会返回相同的两个用户。我错过了什么吗?
答案 4 :(得分:1)
我认为这应该很容易。
SELECT u.`id`, u.`status` FROM `users` u
LEFT OUTER JOIN `post` p ON p.`user_id` = u.`id` AND p.`status` = 'approved'
WHERE u.`status` = 'active' AND p.`id` IS NULL
给出结果为4& 5。
[编辑]只是想补充其中的原因:
你。
status
='活跃'
这导致排除所有不活跃的用户。
p。
status
='已批准'
这排除了所有已批准的帖子。
因此,通过使用这两行,我们已经排除了符合条件的所有用户。
[编辑2]
如果您还需要知道有多少待处理和已批准的数量,这里有更新版本:
SELECT u.`id`, u.`status`, SUM(IF(p.`status` = 'approved', 1, 0)) AS `Approved_Posts`, SUM(IF(p.`status` = 'pending', 1, 0)) AS `Pending_Posts`
FROM `test_users` u
LEFT OUTER JOIN `test_post` p ON p.`user_id` = u.`id`
WHERE u.`status` = 'active'
GROUP BY u.`id`
HAVING SUM(IF(p.`id` IS NOT NULL, 1, 0))
答案 5 :(得分:0)
您可以尝试使用以下查询:
SELECT DISTINCT u.*
FROM users u
LEFT JOIN posts p
ON p.user_id = u.id
WHERE
u.status = 'active' AND (
p.user_id IS NULL
OR p.status != 'approved');
修改强>
根据更新的问题,上面的查询将包括用户1.如果我们想要阻止它,并且不想使用内部查询,我们可以使用MySQL的group_concat
函数来获取所有(不同的状态并查看它是否包含“活动”状态,下面的查询应该给出所需的输出:
SELECT u.id, group_concat(distinct p.status) as statuses
FROM users u
LEFT JOIN posts p
ON u.id = p.user_id
WHERE
u.status = 'active'
group by u.id
having (statuses is null or statuses not like '%approved%');
答案 6 :(得分:0)
试试这个
SELECT DISTINCT u.*
FROM users u LEFT JOIN posts p
ON p.user_id = u.id
WHERE p.status IS NULL
OR p.status != 'approved';