(SELECT user_id FROM t_user_group WHERE group_name ='g2') -- 第 1 步 EXCEPT-- 第 3 步 (SELECT user_id FROM t_user_group WHERE group_name ='g5') -- 第 2 步
然后再用得到 user_id 去 t_user 表里把具体的用户信息查询出来
1 2 3 4 5 6 7
SELECT* FROM t_user WHERE user_id IN ( (SELECT user_id FROM t_user_group WHERE group_name ='g2') EXCEPT (SELECT user_id FROM t_user_group WHERE group_name ='g5') )
SELECT* FROM t_user WHERE user_id IN ( SELECT user_id FROM t_user_group WHERE group_name ='g2'-- #1 ) AND user_id NOTIN ( SELECT user_id FROM t_user_group WHERE group_name ='g5'-- #2 )
这是比较直观的一种写法,#1 这个子查询筛选出参加了 g2 小组的用户,#2 这个子查询筛选出参加了 g5 小组的用户。... IN (...) AND ... NOT IN (...) 表示在第一个集合但是不在第二个集合的用户。
写法二:EXISTS 和 NOT EXISTS
1 2 3 4 5 6 7 8
SELECT* FROM t_user u WHEREEXISTS ( SELECT1FROM t_user_group ug WHERE u.user_id = ug.user_id AND ug.group_name ='g2' ) ANDNOTEXISTS ( SELECT1FROM t_user_group ug WHERE u.user_id = ug.user_id AND ug.group_name ='g5' )
SELECT u.* FROM t_user u INNERJOIN t_user_group ug1 ON u.user_id = ug1.user_id AND ug1.group_name ='g2' LEFTJOIN t_user_group ug2 ON u.user_id = ug2.user_id AND ug2.group_name ='g5' WHERE ug2.user_id ISNULL
这里得到了 t_user u 表和 t_user_group ug1 表满足条件 u.user_id = ug1.user_id AND ug1.group_name = 'g2' 的交集,即参加了 g2 小组的用户。为了方便后续表述,把这一结果临时表,记为 temp1。
1 2 3 4
SELECT u.*, ug1.*, ug2.* FROM t_user u INNERJOIN t_user_group ug1 ON u.user_id = ug1.user_id AND ug1.group_name ='g2' LEFTJOIN t_user_group ug2 ON u.user_id = ug2.user_id AND ug2.group_name ='g5'
SELECT u.*, ug1.*, ug2.* FROM t_user u INNERJOIN t_user_group ug1 ON u.user_id = ug1.user_id AND ug1.group_name ='g2' LEFTJOIN t_user_group ug2 ON u.user_id = ug2.user_id AND ug2.group_name ='g5' WHERE ug2.user_id ISNULL
SELECT u.* FROM t_user u INNERJOIN t_user_group ug1 ON u.user_id = ug1.user_id AND ug1.group_name ='g2' LEFTJOIN t_user_group ug2 ON u.user_id = ug2.user_id AND ug2.group_name ='g5' WHERE ug2.user_id ISNULL
SELECT temp2.u_user_id, temp2.u_user_name FROM ( SELECT temp1.*, ug2.user_id ug2_user_id, ug2.group_name ug2_group_name FROM ( SELECT u.user_id u_user_id, u.user_name u_user_name, ug1.user_id ug1_user_id, ug1.group_name ug1_group_name FROM t_user u INNERJOIN t_user_group ug1 ON u.user_id = ug1.user_id AND ug1.group_name ='g2' ) temp1 LEFTJOIN t_user_group ug2 ON temp1.u_user_id = ug2.user_id AND ug2.group_name ='g5' ) temp2 WHERE temp2.ug2_user_id ISNULL
用 WITH ... AS ... 公共表表达式可能表达得更清楚一些
1 2 3 4 5 6 7 8 9 10 11 12 13
WITH temp2 AS ( WITH temp1 AS ( SELECT u.user_id, u.user_name FROM t_user u INNERJOIN t_user_group ug1 ON u.user_id = ug1.user_id AND ug1.group_name ='g2' ) SELECT temp1.user_id, temp1.user_name, ug2.user_id ug2_user_id FROM temp1 LEFTJOIN t_user_group ug2 ON temp1.user_id = ug2.user_id AND ug2.group_name ='g5' ) SELECT temp2.user_id, temp2.user_name FROM temp2 WHERE temp2.ug2_user_id ISNULL
写法四:LEFT JOIN 和 LEFT JOIN
1 2 3 4 5
SELECT u.* FROM t_user u LEFTJOIN t_user_group ug1 ON u.user_id = ug1.user_id AND ug1.group_name ='g2' LEFTJOIN t_user_group ug2 ON u.user_id = ug2.user_id AND ug2.group_name ='g5' WHERE ug1.user_id ISNOTNULLAND ug2.user_id ISNULL
这种写法和写法三是类似的,只是用 INNER JOIN 替换为了 LEFT JOIN。第一个 LEFT JOIN 配合 ug1.user_id IS NOT NULL 就是参加 g2 的用户,第二个 LEFT JOIN 配合 ug2.user_id IS NULL 就是排除参加了 g5 的用户。
写法五:JOIN、GROUP BY、HAVING 和 CASE WHEN
1 2 3 4 5 6
SELECT u.* FROM t_user u JOIN t_user_group ug ON u.user_id = ug.user_id GROUPBY u.user_id, u.user_name HAVINGSUM(CASEWHEN ug.group_name ='g2'THEN1ELSE0END) >0 ANDSUM(CASEWHEN ug.group_name ='g5'THEN1ELSE0END) =0
这种写法还是有点意思的,值得好好看一看。首先来看 JOIN 部分
1 2 3
SELECT u.*, ug.* FROM t_user u JOIN t_user_group ug ON u.user_id = ug.user_id
SELECT user_id, user_name FROM t_user WHERE user_id IN ( SELECT user_id FROM t_user_group WHERE group_name ='g2' ) EXCEPT SELECT user_id, user_name FROM t_user WHERE user_id IN ( SELECT user_id FROM t_user_group WHERE group_name ='g5' )
MySQL 不支持 EXCEPT 语法,PostgreSQL 支持该语法。是模型分析中写法的一种变形。
写法七:INNER JOIN 和 NOT EXISTS
1 2 3 4 5 6 7 8
SELECT u.* FROM t_user u INNERJOIN t_user_group ug1 ON u.user_id = ug1.user_id AND ug1.group_name ='g2' WHERENOTEXISTS ( SELECT1 FROM t_user_group ug2 WHERE ug2.user_id = u.user_id AND ug2.group_name ='g5' )
SELECT u.* FROM t_user u LEFTJOIN t_user_group ug1 ON u.user_id = ug1.user_id AND ug1.group_name ='g2' LEFTJOIN t_user_group ug2 ON u.user_id = ug2.user_id AND ug2.group_name ='g5' GROUPBY u.user_id, u.user_name HAVINGCOUNT(ug1.user_id) >0ANDCOUNT(ug2.user_id) =0
可以参考写法五进行类事分析,只需注意 COUNT(...) 和 SUM(CASE WHEN ... THEN ... ELSE ... END) 的等价性。
写法十:LEFT JOIN 和 NOT EXISTS
1 2 3 4 5 6 7 8
SELECT u.* FROM t_user u LEFTJOIN t_user_group ug1 ON u.user_id = ug1.user_id AND ug1.group_name ='g2' WHERENOTEXISTS ( SELECT1 FROM t_user_group ug2 WHERE u.user_id = ug2.user_id AND ug2.group_name ='g5' ) AND ug1.user_id ISNOTNULL
和写法七类似,INNER JOIN 换成了 LEFT JOIN,只是需要 ug1.user_id IS NOT NULL 来找出参加了 g2 的用户。
写法十一:IN、NOT IN 和 INTERSECT
1 2 3 4 5 6 7 8 9 10 11
SELECT user_id, user_name FROM t_user WHERE user_id IN ( SELECT user_id FROM t_user_group WHERE group_name ='g2' ) INTERSECT SELECT user_id, user_name FROM t_user WHERE user_id NOTIN ( SELECT user_id FROM t_user_group WHERE group_name ='g5' )