都知道这样的语句:
GROP TABLE user IF EXISTS
如果这个表存在,则删除一个表。但是在实际查询中,exists函数就用的少了。
今天做一个关联多表的查询,需要过滤掉满足某一条件的这些数据,大概的需求:
1、查询用户角色是学生(涉及到三个表:user、role、rel_user_role)的列表;
2、一个学生只能加入某老师创建的一个分组,不能同时加入一个老师创建的多个分组,但是可以加入不同老师创建的不同分组,即:这些学生加入的学习组中,不能有某一个老师创建的任何一个组;(设计到的表:group、rel_user_group、user)
3、或者学生没有加入任何组;
4、学生和老师都在同一个user表,用角色区别开。
尝试了各种方法,顺利得到学生创建的学习组的创建者id,但是苦死很久,也没想到如果判断这些创建者ID只要有一个值就不查出这些学生。
SELECT teacher.* FROM `user` student INNER JOIN rel_user_role rur ON student.ID = rur.USER_ID inner JOIN role r ON ROLE_ID = r.ID AND r.ROLE_CODE = 'ROLE_STUDENT'
INNER JOIN rel_user_group rug ON student.ID = rug.USER_ID INNER JOIN `group` g ON rug.GROUP_ID = g.ID
INNER JOIN `user` teacher ON teacher.ID = g.CREATE_USER_ID
WHERE g.CREATE_USER_ID = 1
最后google了下,看到被人说可以用exists,尝试了下,果然好用:
只需要在查询学生的时候,让他所在组的创建者中,有一个ID等于特定值(如1)的这种条件不存在,就过滤了创建者ID为1的这些学生,剩下的学生就是我需要的数据,还有更方便的是,可以不管学生有没有学习组了,没有组,当然上面的这种条件也不成立。
最终SQL:
-- 查询是学生且他所在的学习组中没有创建者id为1的组的学生
SELECT student.* FROM `user` student,rel_user_role rur,role r
WHERE not EXISTS
(
-- 所在的学习组中没有创建者id为1的
SELECT 0 FROM rel_user_role rur,role r,rel_user_group rug,`group` g
WHERE
student.ID = rur.USER_ID
AND ROLE_ID = r.ID
AND r.ROLE_CODE = 'ROLE_STUDENT'
AND student.ID = rug.USER_ID
AND rug.GROUP_ID = g.ID
AND g.CREATE_USER_ID = 1
)
-- 身份是学生
AND r.ID = rur.ROLE_ID
AND student.ID = rur.USER_ID
AND r.ROLE_CODE = 'ROLE_STUDENT'
转为Hibernate的QBC查询方式:
DetachedCriteria stuDc = DetachedCriteria.forClass(User.class, "u");
stuDc.createAlias("roles", "role");
stuDc.add(Restrictions.eq("role.roleCode", SystemConst.ROLE_STUDENT));
// 不能在关联USER表查询group的创建老师user.
DetachedCriteria rugDc = DetachedCriteria.forClass(RelUserGroup.class, "rug").createAlias("group", "group");
rugDc.add(Property.forName("group.user.id").eq(UserUtil.getUser().getId())); // 组ID不能为当前登录老师的ID
rugDc.add(Property.forName("rug.user.id").eqProperty(Property.forName("u.id")));// 关联User的id和RelUserGroup的ID
// 这里的投影为子查询中SELECT出来的结果数据
stuDc.add(Subqueries.notExists(rugDc.setProjection(Projections.property("rug.user.id"))));
最终将stuDc用于查询数据即可.
以下摘自网友博客,备查:
有一个查询如下:
1 | SELECT c.CustomerId, CompanyName |
4 |
SELECT OrderID FROM Orders o |
5 |
WHERE o.CustomerID = cu.CustomerID) |
这里面的EXISTS是如何运作呢?子查询返回的是OrderId字段,可是外面的查询要找的是CustomerID和CompanyName字段,这两个字段肯定不在OrderID里面啊,这是如何匹配的呢?
EXISTS用于检查子查询是否至少会返回一行数据,该子查询实际上并不返回任何数据,而是返回值True或False。
EXISTS 指定一个子查询,检测行的存在。语法:EXISTS subquery。参数 subquery 是一个受限的 SELECT 语句 (不允许有 COMPUTE 子句和 INTO 关键字)。结果类型为 Boolean,如果子查询包含行,则返回 TRUE。
这个例子在子查询中指定 NULL,并返回结果集,通过使用 EXISTS 仍取值为 TRUE。
3 | WHERE EXISTS ( SELECT NULL ) |
这个例子比较了两个语义类似的查询。第一个查询使用 EXISTS 而第二个查询使用 IN。注意两个查询返回相同的信息。
6 |
WHERE pub_id = publishers.pub_id |
本示例显示查找与出版商住在同一城市中的作者的两种查询方法:第一种方法使用 = ANY,第二种方法使用 EXISTS。注意这两种方法返回相同的信息。
1 | SELECT au_lname, au_fname |
6 |
WHERE authors.city = publishers.city) |
1 | SELECT au_lname, au_fname |
本示例所示查询查找由位于以字母 B 开头的城市中的任一出版商出版的书名:
6 |
WHERE pub_id = titles.pub_id |
NOT EXISTS 的作用与 EXISTS 正相反。如果子查询没有返回行,则满足 NOT EXISTS 中的 WHERE 子句。本示例查找不出版商业书籍的出版商的名称:
6 |
WHERE pub_id = publishers.pub_id |
又比如以下 SQL 语句:
6 | where 学号=xs.学号 and 课程号=kc.课程号 |
把最外层的查询xs里的数据一行一行的做里层的子查询。
中间的 exists 语句只做出对上一层的返回 true 或 false,因为查询的条件都在 where 学号=xs.学号 and 课程号=kc.课程号这句话里。每一个 exists 都会有一行值。它只是告诉一层,最外层的查询条件在这里成立或都不成立,返回的时候值也一样回返回上去。直到最高层的时候如果是 true(真)就返回到结果集。为 false(假)丢弃。
3 | where 学号=xs.学号 and 课程号=kc.课程号 |
这个 exists 就是告诉上一层,这一行语句在我这里不成立。因为他不是最高层,所以还要继续向上返回。
select distinct 姓名 from xs where not exists (这里的 exists 语句收到上一个为 false 的值。他在判断一下,结果就是为 true(成立),由于是最高层所以就会把这行的结果(这里指的是查询条件)返回到结果集。
几个重要的点:
- 最里层要用到的醒询条件的表比如:xs.学号、kc.课程号等都要在前面的时候说明一下select * from kc,select distinct 姓名 from xs
- 不要在太注意中间的exists语句.
- 把exists和not exists嵌套时的返回值弄明白