T-SQL查詢——嵌套子查詢


   SQL有着非常強大且靈活的查詢方式,而多表連接操作往往也可以用子查詢進行替代,子查詢本質上是嵌套進其他select,update,insert,delete語句的一個被限制的select語句,在子查詢中,只有下面幾個子句可以使用

1、select子句(必須)

2、from子句(必須)

3、where子句(可選)

4、group by(可選)

5、having(可選)

6、order by(只有在top關鍵字被使用時才可用)

子查詢可以嵌套在其他子查詢中,這個嵌套最多可以達到32層。子查詢也叫內部查詢(inner query)或者內部選擇(innder  select),而包含子查詢的查詢語句也叫外部查詢(outter)或者外部選擇(Outer select),子查詢的概念可以用簡單下圖闡述:

上圖是作為數據源使用的一個子查詢

通常來講,子查詢按照子查詢所返回的數據類型,可以分分為三種,分別為:

1、返回一張數據表(table)

2、返回一列值(column)

3、返回單個值(Scalar)

子查詢作為數據源使用

    當子查詢在外部查詢的form子句之后使用,子查詢被當做一個數據源使用,即使這時子查詢只返回一個單一值(scalar)或是一列值(column),在這里依然可以看做一個特殊的數據源,即一個二維數據表(table),作為數據源使用的子查詢很像一個view(視圖),只是這個子查詢只是臨時存在,並不含在數據庫中。

比如這個語句  

SELECT     P.ProductID, P.Name, P.ProductNumber, M.Name AS ProductModelName
FROM Production.Product AS P
INNER JOIN
(SELECT Name, ProductModelID
FROM Production.ProductModel) AS M
ON P.ProductModelID = M.ProductModelID

上述子查詢語句將ProductModel表中的子集M,作為數據源(表)和product表進行內連接。結果如下:

作為數據源使用也是子查詢最簡單的應用。當然,當子查詢作為數據源使用時,也分為相關子查詢和無關子查詢。

子查詢作為選擇條件使用
  作為選擇條件的子查詢也是子查詢相對復雜的應用

  作為選擇條件的子查詢是那些只返回一列(column)的子查詢,如果作為選擇條件使用,即使只返回單個值,也可以看做是只有一行的一列,比如:

  在AdvertureWorks中

  我想取的總共請假天數大於68小時的員工:

SELECT [FirstName]
,[MiddleName]
,[LastName]
FROM [AdventureWorks].[Person].[Contact]
WHERE ContactID IN

(SELECT EmployeeID
FROM [AdventureWorks].[HumanResources].[Employee]
WHERE SickLeaveHours>68)

上面的查詢中,在In關鍵字后面的子查詢返回一列值作為外部查詢的選擇條件使用。

同樣的,於in關鍵字的邏輯取反的Not in關鍵字,這里不再贅述

但是要強調的是,不要用in和not in關鍵字,這回引起很多潛在的問題,這篇文章對這個問題有很好的闡述http://wiki.lessthandot.com/index.php/Subquery_typo_with_using_in,總之一句話:用exist代替in,當然在有固定值的時候,可以用in或not in...存在就是合理嘛。

SELECT [FirstName]
,[MiddleName]
,[LastName]
FROM [AdventureWorks].[Person].[Contact]
WHERE ContactID IN (25,33)

只有在上面的這種情況下,使用in和not in關鍵字才是安全的,其他情況下,最好使用exists,not exists,join關鍵字來進行替代,除了in之外,用於選擇條件的關鍵字還有any和all,這兩個關鍵字和其他字面意思一樣,和“<”,“>”,“=”連接使用,比如上面的in的那個子查詢

我想取得總共請病假天數大於58小時的員工

用any關鍵字進行等效的查詢為

SELECT [FirstName]
,[MiddleName]
,[LastName]
FROM [AdventureWorks].[Person].[Contact]
WHERE ContactID =ANY

(SELECT EmployeeID
FROM [AdventureWorks].[HumanResources].[Employee]
WHERE SickLeaveHours>68)

作為any和all關鍵字在子查詢中使用時,所實現的效果如下:

=ANY 和IN等價
<>ANY 和NOT IN等價
>ANY 大於最小的(>MIN)
<ANY 小於最大的(<MAX)
>ALL 大於最大的(>MAX)
<ALL 小於最小的(<MIN)
=ALL 下面說

   =ALL關鍵字很少使用,這個的效果在子查詢中為如果只有一個返回值,則和“=”相等,而如果有多個返回值,結果為空

   這里需要注意,SQL是一種很靈活的語言,就像子查詢所實現的效果可以使用join來實現一樣(效果一樣,實現思路不一樣),any和all所實現的效果也完全可以使用其他方式來替代,按照上面的表格所示,>any和>min完全等價,比如下面兩個查詢語句完全等價:

SELECT *
FROM AdventureWorks.HumanResources.Employee
WHERE SickLeaveHours>ANY

(SELECT SickLeaveHours FROM AdventureWorks.HumanResources.Employee WHERE SickLeaveHours>68)


SELECT *
FROM AdventureWorks.HumanResources.Employee
WHERE SickLeaveHours>

(SELECT MIN(SickLeaveHours) FROM AdventureWorks.HumanResources.Employee WHERE SickLeaveHours>68)

相關子查詢和exists關鍵字

  前面所說的查詢都是無關子查詢,子查詢中還有一類很重要的查詢是相關子查詢,也叫重復子查詢,比如,還是上面那個子查詢,用相關子查詢寫:

我想取得總共請病假天數大於68天的員工:

SELECT [FirstName]
,[MiddleName]
,[LastName]
FROM [AdventureWorks].[Person].[Contact] c
WHERE EXISTS

(SELECT *
FROM [AdventureWorks].[HumanResources].[Employee] e
WHERE c.ContactID=e.ContactID AND e.SickLeaveHours>68)

   結果和使用IN關鍵字的查詢結果相同:

如何區別相關子查詢和無關子查詢呢?最簡單的辦法就是直接看子查詢本身是否執行,比如上面的例子中的子查詢

(SELECT *
FROM [AdventureWorks].[HumanResources].[Employee] e
WHERE c.ContactID=e.ContactID AND e.SickLeaveHours>68)

這一句本身執行本省會報錯,因為這句引用到了外部查詢的表

對於無關子查詢來說,整個查詢的過程為子查詢只執行一次,然后交給外部查詢。比如:

SELECT *
FROM AdventureWorks.HumanResources.Employee
WHERE SickLeaveHours>ANY

SQLRESULT

上面的無關子查詢,整個查詢過程可以看做是子查詢首相返回SQL Result(SQL結果集),然后交給外部查詢使用,整個過程子查詢只執行一次

而相反,作為相關子查詢,子查詢的執行次數依賴於外部查詢,外部查詢每執行一次,比如,還是上面的例子:我想取得總共請病假天數大於68天的員工

SELECT [FirstName]
,[MiddleName]
,[LastName]
FROM [AdventureWorks].[Person].[Contact] c
WHERE EXISTS

(SELECT *
FROM [AdventureWorks].[HumanResources].[Employee] e
WHERE c.ContactID=e.ContactID AND e.SickLeaveHours>68)

----
step 1:
SELECT [FirstName]
,[MiddleName]
,[LastName]
FROM [AdventureWorks].[Person].[Contact] c
WHERE EXISTS

(SELECT *
FROM [AdventureWorks].[HumanResources].[Employee] e
WHERE 1=e.ContactID AND e.SickLeaveHours>68)
----
step 2:
SELECT [FirstName]
,[MiddleName]
,[LastName]
FROM [AdventureWorks].[Person].[Contact] c
WHERE EXISTS

(SELECT *
FROM [AdventureWorks].[HumanResources].[Employee] e
WHERE 2=e.ContactID AND e.SickLeaveHours>68)
----
step n:
SELECT [FirstName]
,[MiddleName]
,[LastName]
FROM [AdventureWorks].[Person].[Contact] c
WHERE EXISTS

(SELECT *
FROM [AdventureWorks].[HumanResources].[Employee] e
WHERE n=e.ContactID AND e.SickLeaveHours>68)

如上面的代碼所示。上面的相關子查詢實際上會執行N次(N取決與外部查詢的行數),外部查詢沒執行一行,都會對應行所用的參數到子查詢中,如果子查詢有對應的的值,則返回true(即當前行被選中並在結果中顯示),如果沒有,則返回false.然后重復執行下一行。


子查詢作為計算列使用
  當子查詢作為計算列使用時,只返回單個值(scalar)。用在select語句之后,作為計算列使用。同樣分為相關子查詢和無相關子查詢

相關子查詢的例子比如:我想取得每件產品的名稱和總共的銷量

SELECT [Name],
(SELECT COUNT(*) FROM AdventureWorks.Sales.SalesOrderDetail S
WHERE S.ProductID=P.ProductID) AS SalesAmount
FROM [AdventureWorks].[Production].[Product] P

當子查詢作為計算列使用時,會針對外部查詢的每一行,返回唯一的值。

同樣的,SQL子查詢都可以使用其他語句達到同樣的效果,上面的語句和如下語句達到同樣的效果

SELECT P.Name,COUNT(S.ProductID)
FROM [AdventureWorks].[Production].[Product] P
LEFT JOIN AdventureWorks.Sales.SalesOrderDetail S
ON S.ProductID=P.ProductID
GROUP BY P.Name

子查詢作為計算列且作為無關子查詢的使用,只會一次性返回但一值,這里就不再闡述了。

小結
   本篇文章通過分子查詢的三種不同用來闡述子查詢。同時,所有的子查詢還可以分為相關子查詢和無關子查詢。而子查詢所實現的功能都可以使用連接或者其他方式實現。但一個好的作家應該是掌握豐富的詞匯,而不是僅僅能表達出自己的意思。學會多種SQL查詢方式是學習SQL查詢必經之路。

轉自http://www.cnblogs.com/CareySon/archive/2011/07/18/2109406.html


免責聲明!

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



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