sql server 子查詢 和exists使用


概述

子查詢的概念:

  當一個查詢是另一個查詢的條件時,稱之為子查詢。子查詢可以嵌套在主查詢中所有位置,包括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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM