概述
子查詢的概念:
當一個查詢是另一個查詢的條件時,稱之為子查詢。子查詢可以嵌套在主查詢中所有位置,包括SELECT、FROM、WHERE、GROUP BY、HAVING、ORDER BY。
外面的查詢成為父查詢,圓括號嵌入的查詢成為稱為子查詢。SQL Server執行時,先執行子查詢部分,求出子查詢部分的值,再執行整個父查詢,返回最后的結果。
查看多表的數據也可使用表連接,表連接(join on...),表連接都可用子查詢替換,但有的子查詢不能用表連接替換,子查詢比較靈活,方便,形式多樣,適合於作為查詢的篩選條件。
子查詢按照相關性分類
1.相關子查詢
必須依賴於它所屬的外部查詢,不能獨立地調用它。
外部查詢返回一行,子查詢就執行一次。
2.非相關子查詢
獨立於外部查詢的子查詢。
子查詢總共執行一次,執行完畢后后將值傳遞給外部查詢。
需要注意的是相關子查詢主查詢執行一回,子查詢就執行一回,十分耗費時間,尤其是當數據多的時候。
子查詢按照返回的結果集分類
1.單值子查詢
只有返回且僅返回一行、一列數據的子查詢才能當成單值子查詢。當子查詢跟隨在=、!=、<、<=、>、>=,<> 之后,或子查詢用作表達式,只能使用單值子查詢。
2.多值子查詢
如果子查詢是多行單列的子查詢,這樣的子查詢的結果集其實是一個集合,那么可以使用in關鍵字代替=號。
Exists原理
exists做為where 條件時,是先對where 前的主查詢詢進行查詢,然后用主查詢的結果一個一個的代入exists的查詢進行判斷,如果為真則輸出當前這一條主查詢的結果,否則不輸出。
查詢時,一般情況下,子查詢會分成兩種情況:
1.子查詢與外表的字段有關系時
select 字段1 , 字段2 from 表1 where exists (select 字段1 , 字段2 from 表2 where 表2.字段2 = 表1.字段2)
這時候,此SQL語句相當於一個關聯查詢。
它先執行表1的查詢,然后把表1中的每一條記錄放到表2的條件中去查詢,如果存在,則顯示此條記錄。
2.子查詢與外表的字段沒有任何關聯
Select 字段1 , 字段2 from 表1 where exists ( select * from 表2 where 表2.字段 = ‘ 條件‘)
在這種情況下,只要子查詢的條件成立,就會查詢出表1中的所有記錄,反之,如果子查詢中沒有查詢到記錄,則表1不會查詢出任何的記錄。
當子查詢與主表不存在關聯關系時,簡單認為只要exists為一個條件判斷,如果為true,就輸出所有記錄。如果為false則不輸出任何的記錄。
自包含子查詢
自包含標量子查詢
DECLARE @maxid AS INT = (SELECT MAX(orderid) FROM Sales.Orders); SELECT orderid, orderdate, empid, custid FROM Sales.Orders WHERE orderid = @maxid;
SELECT orderid, orderdate, empid, custid FROM Sales.Orders WHERE orderid = (SELECT MAX(O.orderid) FROM Sales.Orders AS O);
自包含多值子查詢
SELECT orderid FROM Sales.Orders WHERE empid = (SELECT E.empid FROM HR.Employees AS E WHERE E.lastname LIKE N'B%');
SELECT n FROM dbo.Nums WHERE n BETWEEN (SELECT MIN(O.orderid) FROM dbo.Orders AS O) AND (SELECT MAX(O.orderid) FROM dbo.Orders AS O) AND n NOT IN (SELECT O.orderid FROM dbo.Orders AS O);
相關子查詢
什么是相關子查詢:引用了外部查詢中出現的表的列,依賴於外部查詢,不能獨立地運行子查詢。在邏輯上,子查詢會為每個外部行單獨計算一次。
例子1:查詢每個客戶返回在他參與活動的最后一天下過的所有訂單。
期望結果:
影響行數:90
1.首先用獨立標量子查詢查詢出最大的訂單日期,返回給外部查詢
SELECT MAX(orderdate) FROM sales.Orders AS O2
2.外部查詢用O1.orderdate進行過濾,過濾出等於最大訂單日期的訂單
3.因為要查詢出每個客戶參與的訂單,所以將獨立標量子查詢改成相關子查詢,用子查詢O2.custid與外查詢O1.custid關聯。
對於O1中每一行,子查詢負責返回當前客戶的最大訂單日期。如果O1中某行的訂單日期和子查詢返回的訂單日期匹配,那么O1中的這個訂單日期就是當前客戶的最大的訂單日期,在這種情況下,查詢便會返回O1表中的這個行。
SELECT MAX(orderdate) FROM sales.Orders AS O2 WHERE O2.custid = O1.custid
綜合上面的步驟,得到下面的查詢語句:
SELECT orderid,orderdate,custid FROM sales.Orders AS O1 WHERE O1.orderdate = ( SELECT MAX(orderdate) FROM sales.Orders AS O2 WHERE O2.custid = O1.custid
例子2:為每個客戶返回最大訂單ID的訂單。
第一步:
SELECT MAX(O2.orderid) FROM Sales.Orders AS O2
第二步:
SELECT MAX(O2.orderid) FROM Sales.Orders AS O2 WHERE O2.custid = O1.custid
第三步:
SELECT custid, orderid, orderdate, empid FROM Sales.Orders AS O1 WHERE orderid = (SELECT MAX(O2.orderid) FROM Sales.Orders AS O2 WHERE O2.custid = O1.custid);
Exists謂詞
帶有EXISTS的子查詢不返回任何記錄的數據,只返回邏輯值“True”或“False”
返回下訂單的西班牙客戶
SELECT custid, companyname FROM Sales.Customers AS C WHERE country = N'Spain' AND EXISTS (SELECT * FROM Sales.Orders AS O WHERE O.custid = C.custid);
返回沒有下訂單的西班牙客戶
SELECT custid, companyname FROM Sales.Customers AS C WHERE country = N'Spain' AND NOT EXISTS (SELECT * FROM Sales.Orders AS O WHERE O.custid = C.custid);
對於EXISTS,它采用的是二值邏輯(TRUE和FALSE),它只關心是否存在匹配行,而不考慮SELECT列表中指定的列,並且無須處理所有滿足條件的行。可以將這種處理方式看做是一種“短路”,它能夠提高處理效率。
另外,由於EXISTS采用的是二值邏輯,因此相較於IN要更加安全,可以避免對NULL值得處理。
高級子查詢
如何表示前一個或后一個記錄?邏輯等式:上一個->小於當前值的最大值;下一個->大於當前值的最小值;
-- 上一個訂單ID select orderid, orderdate, empid, custid, ( select MAX(o2.orderid) from sales.Orders as o2 where o2.orderid<o1.orderid ) as prevorderid from sales.Orders as o1;
如何實現連續聚合函數?在子查詢中連續計算
-- 連續聚合 select orderyear, qty, (select SUM(o2.qty) from sales.OrderTotalsByYear as o2 where o2.orderyear<=o1.orderyear) as runqty from sales.OrderTotalsByYear as o1 order by orderyear;
使用NOT EXISTS謂詞取代NOT IN隱式排除NULL值:當對至少返回一個NULL值的子查詢使用NOT IN謂詞時,外部查詢總會返回一個空集。(前面提到,EXISTS謂詞采用的是二詞邏輯而不是三詞邏輯)
-- 隱式排除NULL值 select custid,companyname from sales.Customers as c where not exists (select * from sales.Orders as o where o.custid=c.custid);
又如以下查詢請求返回每個客戶在2007年下過訂單而在2008年沒有下過訂單的客戶:
select custid, companyname from sales.Customers as c where exists (select * from sales.Orders as o1 where c.custid=o1.custid and o1.orderdate>='20070101' and o1.orderdate<'20080101') and not exists (select * from sales.Orders as o2 where c.custid=o2.custid and o2.orderdate>='20080101' and o2.orderdate<'20090101');
Exists
exists是用來判斷是否存在的,當exists查詢中的查詢存在結果時則返回真,否則返回假。not exists則相反。
exists做為where 條件時,是先對where 前的主查詢詢進行查詢,然后用主查詢的結果一個一個的代入exists的查詢進行判斷,如果為真則輸出當前這一條主查詢的結果,否則不輸出。
exists后面的查詢稱為相關子查詢,即子查詢的查詢條件依賴於外層父查詢中的某個屬性值,其處理過程一般為:先取外層查詢中的第一個元組,根據它與內層查詢中的相關屬性值處理內層查詢,若where子句返回true,則將此元組放入結果表中,然后取外層查詢中的下一個元組,重復這個過程直到全部檢查完畢為止。
例如:我們有一張人員信息表,里邊有一個人員類型Id字段(pTypeId),它是一個外鍵,對應着人員類型表的主鍵ptId。如果我們有以下的SQL語句,使用Exists關鍵字則可以有如下的理解:
select * from Employee e where exists (select * from EmployeeType et where e.pTypeId=et.ptId)
那么,在這句SQL的執行過程中,我們可以將其理解為一個雙重的for循環,外邊是主表的循環遍歷,然后將其放到一個temp變量中,再進入從表的for循環,並與從表的項進行一個一個的按照匹配規則(這里是e.pTypeId=et.ptId)進行匹配,如果有匹配成功則返回true,並且將這一行記錄放到要返回的結果集中,否則返回false。
SQL中EXISTS的使用
查詢所有選修了“語文”課程的學生名
普通SQL查詢:
SELECT s.Sname FROM dbo.Student s WHERE s.S# IN (SELECT sc.S# FROM dbo.Sc sc INNER JOIN dbo.Course c ON c.C# =sc.C# WHERE c.Cname ='語文')
帶EXISTS的SQL查詢:
SELECT s.Sname FROM dbo.Student s WHERE EXISTS (SELECT sc.S# FROM dbo.Sc sc INNER JOIN dbo.Course c ON c.C# =sc.C# WHERE c.Cname ='語文' AND s.S# =sc.S#) 帶EXISTS的SQL查詢:
收集資料
https://blog.csdn.net/qq_26937525/article/details/53930498
http://www.cnblogs.com/jackson0714/p/TSQLFundamentals_03.html
https://blog.csdn.net/mascf/article/details/50288199