都知道這樣的語句:
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 ) |
4 |
ORDER BY CategoryName ASC |
這個例子比較了兩個語義類似的查詢。第一個查詢使用 EXISTS 而第二個查詢使用 IN。注意兩個查詢返回相同的信息。
1 |
SELECT DISTINCT pub_name |
6 |
WHERE pub_id = publishers.pub_id |
1 |
SELECT distinct pub_name |
6 |
WHERE type = 'business' ) |
本示例顯示查找與出版商住在同一城市中的作者的兩種查詢方法:第一種方法使用 = 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 語句:
1 |
select distinct 姓名 from xs |
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嵌套時的返回值弄明白