SQL Server進階(六)表表達式--派生表、公用表表達式(CTE)、視圖和內聯表值函數

概述

  表表達式是一種命名的查詢表達式,表明一個有效地關係表。能夠像其餘表同樣,在數據處理中使用表表達式。html

  SQL Server支持四種類型的表表達式:派生表公用表表達式視圖內聯表值函數sql

爲何使用表表達式:數據庫

  1.使用表表達式的好處是邏輯方面,在性能上沒有提高。模塊化

  2.經過模塊化的方法簡化問題的解決方案,規避語言上的某些限制。在外部查詢的任何字句中均可以引用在內部查詢的SELECT字句中分配的列別名。好比在SELECT字句中起的別名,不能在WHERE,group by等字句(邏輯順序位於SELECT字句以前的字句)中使用,經過表表達式能夠解決這類問題函數

派生表

派生表(也稱爲表子查詢)是在外部查詢的FROM子句中定義的,只要外部查詢一結束,派生表也就不存在了。post

派生表能夠簡化查詢,避免使用臨時表。相比手動生成臨時表性能更優越。派生表與其餘表同樣出如今查詢的FROM子句中性能

select * from (select * from athors)  temp

temp 就是派生表大數據

派生出來的表必需要是一個有效的表.所以,它必須遵照如下幾條規則:優化

  1. 全部列必需要有名稱ui

  2. 列名稱必須是要惟一

  3. 不容許使用ORDER BY(除非指定了TOP)

派生表:好比要查找一個叫張鐵牛的人的信息,咱們知道他是男性,爲了縮小查找範圍我把全部的男性都找出來,而後從這些男性中裏面再去找張鐵牛.這裏男性的集合就至關於派生表,轉成sql語句:

select 姓名,住址,身份證 from (select * from 表名 where 性別='男性') temp 

where 姓名='張鐵牛'

(這裏只是爲了舉例子),這裏的 temp這個數據集就是派生表,它是虛表,在數據庫中不存在的,是咱們構建的,在這裏的目的是爲了縮小數據的查找範圍.

分配列別名(建議使用內嵌別名形式)

SELECT orderyear, COUNT(DISTINCT custid) AS numcusts FROM (SELECT YEAR(orderdate), custid FROM Sales.Orders) AS D(orderyear, custid) GROUP BY orderyear;

使用參數

DECLARE @empid AS INT = 3; SELECT orderyear, COUNT(DISTINCT custid) AS numcusts FROM (SELECT YEAR(orderdate) AS orderyear, custid FROM Sales.Orders WHERE empid = @empid) AS D GROUP BY orderyear;

 嵌套

若是需要用一個自己就引用了某個派生表的查詢去定義另外一個派生表,最終獲得的就是嵌套派生表。

例子:查詢每一年處理客戶數超過70的訂單年度和每一年所處理的客戶數量。

方案一:咱們用第一節中單表查詢查詢出結果

SELECT YEAR(orderdate) AS orderyear, COUNT(DISTINCT custid) AS numcusts FROM Sales.Orders GROUP BY YEAR(orderdate) HAVING COUNT(DISTINCT custid) > 70;

方案二:嵌套派生表

SELECT orderyear, numcusts FROM (SELECT orderyear, COUNT(DISTINCT custid) AS numcusts FROM (SELECT YEAR(orderdate) AS orderyear, custid FROM Sales.Orders) AS D1 GROUP BY orderyear) AS D2 WHERE numcusts > 70;

嵌套查詢看起來很是複雜,嵌套查詢也是很容易產生問題的一個方面。在這個例子中,使用嵌套派生表的目的是爲了重用列別名。可是,因爲嵌套增長了代碼的複雜性,因此對於本例考慮使用方案一。

多個引用

SELECT Cur.orderyear, Cur.numcusts AS curnumcusts, Prv.numcusts AS prvnumcusts, Cur.numcusts - Prv.numcusts AS growth FROM (SELECT YEAR(orderdate) AS orderyear, COUNT(DISTINCT custid) AS numcusts FROM Sales.Orders GROUP BY YEAR(orderdate)) AS Cur LEFT OUTER JOIN (SELECT YEAR(orderdate) AS orderyear, COUNT(DISTINCT custid) AS numcusts FROM Sales.Orders GROUP BY YEAR(orderdate)) AS Prv ON Cur.orderyear = Prv.orderyear + 1;

缺點:咱們看到其實咱們是對同一張表進行聯接,可是須要將代碼定義多遍,這無疑形成代碼的混亂和冗餘。

公用表表達式(CTE)——推薦

公用表表達式是和派生表類似的另外一種形式的表表達式,可是公用表表達式具備一些優點。

公用表表達式和派生表同樣,前面須要遵照的規則對公用表表達式一樣適用。當外部查詢結束,公用表表達式的生命週期就結束了。

其實CTE的做用就至關於子查詢。

內聯格式:別名寫在內部查詢中

WITH C AS ( SELECT YEAR(orderdate) AS orderyear, custid FROM Sales.Orders ) SELECT orderyear, COUNT(DISTINCT custid) AS numcusts FROM C GROUP BY orderyear;

外聯格式:列的別名寫在外部查詢中

WITH C(orderyear, custid) AS ( SELECT YEAR(orderdate), custid FROM Sales.Orders ) SELECT orderyear, COUNT(DISTINCT custid) AS numcusts FROM C GROUP BY orderyear;

使用參數

DECLARE @empid AS INT = 3; WITH C AS ( SELECT YEAR(orderdate) AS orderyear, custid FROM Sales.Orders WHERE empid = @empid ) SELECT orderyear, COUNT(DISTINCT custid) AS numcusts FROM C GROUP BY orderyear;

定義多個CTE

CTE和派生表相關具備如下優點:

若是要在一個CTE中引用另外一個CTE,不需要像派生表那樣進行嵌套,只須要在同一個WITH字句中定義多個CTE,並用逗號把它們分隔開。每一個CTE能夠引用在它前面定義的全部CTE,而外部查詢則能夠引用全部CTE

因爲CTE只能在接下來一條語句中使用,所以,當須要接下來的一條語句中引用多個CTE時,能夠定義多個,中間用逗號分隔,下面是一次定義多個CTE的例子:

WITH C1 AS ( SELECT YEAR(orderdate) AS orderyear, custid FROM Sales.Orders ), C2 AS ( SELECT orderyear, COUNT(DISTINCT custid) AS numcusts FROM C1 GROUP BY orderyear ) SELECT orderyear, numcusts FROM C2 WHERE numcusts > 70;

CET中的多個引用

公用表表達式的好處之一是能夠在接下來一條語句中屢次引用:

WITH CTE_Test   AS   (   SELECT * FROM Person_1   )   SELECT * FROM CTE_Test AS a  --第一次引用
  INNER JOIN  CTE_Test AS b    --第二次引用
  ON a.Id = b.Id   ORDER BY a.Id DESC

 

WITH YearlyCount AS ( SELECT YEAR(orderdate) AS orderyear, COUNT(DISTINCT custid) AS numcusts FROM Sales.Orders GROUP BY YEAR(orderdate) ) SELECT Cur.orderyear, Cur.numcusts AS curnumcusts, Prv.numcusts AS prvnumcusts, Cur.numcusts - Prv.numcusts AS growth FROM YearlyCount AS Cur LEFT OUTER JOIN YearlyCount AS Prv ON Cur.orderyear = Prv.orderyear + 1;

遞歸CET

  CTE 能夠包含對自身的引用,這種表達式被稱爲遞歸公用表表達式。

;with district as ( select * from ta where [name]=N'河北省' 
union all 
select a.* from ta a inner join district b on  a.parentid=b.id ) select * from district

  CTE是一種十分優雅的存在。CTE所帶來最大的好處是代碼可讀性的提高,這是良好代碼的必須品質之一。使用遞歸CTE能夠更加輕鬆愉快的用優雅簡潔的方式實現複雜的查詢。

CET、臨時表、表變量

CET優勢:

  相對於派生表最主要的優點在於能夠一次定義,屢次使用。

  CET大部分地方能夠代替臨時表。CTE最優秀的地方是在實現遞歸操做,和替代絕大部分遊標的功能。

  CET後面必須直接跟使用CTE的SQL語句(如select、insert、update等),不然,CET將失效。可是臨時表一直存在,除非drop掉。

CET缺點:

  對於大數據量,因爲CET不能建索引,因此明顯比臨時表差。我給開發的建議是少於1萬數據的話,CET和表變量就不要用於作暫存數據的功能。而改用臨時表。

  數據量大時,CET的性能要比臨時表差不少(即便臨時表不建索引)

  CET要比表變量效率高得多!

臨時表和表變量的選擇

  咱們對於較小的數據或者是經過計算出來的推薦使用表變量。若是數據的結果比較大,在代碼中用於臨時計算,在選取的時候沒有什麼分組的聚合,就能夠考慮使用表變量

  通常對於大的數據結果,或者由於統計出來的數據爲了便於更好的優化,咱們就推薦使用臨時表,同時還能夠建立索引,因爲臨時表是存放在Tempdb中,通常默認分配的空間不多,須要對tempdb進行調優,增大其存儲的空間。

  表變量實際上使用了臨時表,從而增長了額外的I/O開銷,所以,表變量的方式並不太適合數據量大且頻繁查詢的狀況

 視圖

1.視圖和派生表和CTE的區別和共同點

區別:

  派生表和CTE不可重用:只限於在單個語句的範圍內使用,只要包含這些表表達式的外部查詢完成操做,它們就消失了。

  視圖和內聯表值函數是可重用的:它們的定義存儲在一個數據對象中,一旦建立,這些對象就是數據庫的永久部分;只有用刪除語句顯示刪除或用右鍵刪除,它們纔會從數據庫中移除。

共同點:

  在不少方面,視圖和內聯表值函數的處理方式都相似於派生表和CTE。當查詢視圖和內聯表值函數時,SQL Server會先擴展表表達式的定義,再直接查詢底層對象。

建立一個視圖:

IF OBJECT_ID('Sales.USACusts') IS NOT NULL
   DROP VIEW Sales.USACusts; GO
CREATE VIEW Sales.USACusts AS
SELECT custid, companyname, contactname, contacttitle, address, city, region, postalcode, country, phone, fax FROM Sales.Customers WHERE country=N'USA'; GO

使用該視圖:

SELECT * FROM Sales.USACusts;

內聯表值函數(內嵌TVF)

什麼是內聯表值函數

一種可重用的表表達式,可以支持輸入參數。除了支持輸入參數之外,內聯表值函數在其餘方面都與視圖類似。

內嵌表值函數是支持輸入參數的可重複使用的表表達式。除了支持輸入參數以外的其餘全部方面都和視圖相似。咱們來看下怎麼建立內嵌表值函數。

下面演示如何建立函數:

IF OBJECT_ID('dbo.fn_GetCustOrders') IS NOT NULL
   DROP FUNCTION dbo.fn_GetCustOrders; GO
CREATE FUNCTION dbo.fn_GetCustOrders (@cid AS INT) RETURNS TABLE
AS
RETURN 
    SELECT orderid, custid, empid, orderdate, requireddate, shippeddate, shipperid, freight, shipname, shipaddress, shipcity, shipregion, shippostalcode, shipcountry FROM Sales.Orders WHERE custid=@cid; GO

如何使用函數:

SELECT orderid, custid FROM dbo.fn_GetCustOrders(1) AS CO;

 小結

  藉助表表達式能夠簡化代碼,提升代碼的可維護性,還能夠封裝查詢邏輯。

  當須要使用表表達式,並且不計劃重用它們的定義時,可使用派生表或CTE。與派生表相比,CTE具備兩個優勢:CTE不用像派生表那樣嵌套使用,此外,還能夠引用同一CTE的多個實例,也派生表不能這麼用。

  當須要定義可重用的表表達式時,可使用視圖和內聯表值函數。若是不需要支持輸入參數,則使用視圖,相反則使用內聯表值函數。

  (1)表表達式能夠簡化代碼,提升代碼的可維護性和封裝查詢邏輯。

  (2)當須要使用表表達式而且不打算重複使用其定義時,可使用派生表或CTE,而CTE對派生表具備更多優點不須要像派生表那樣嵌套CTE,使用CTE使代碼更加模塊化和便於維護,此外,還能夠引用同一個CTE的多個實例,這一點是派生表沒法實現的。

  (3)當須要使用表表達式而且須要定義可重複使用的表表達式時,可使用視圖或內嵌表值函數,當不須要支持輸入參數時,可使用視圖,不然,應當使用內嵌表值函數(TVF)。

 APPLY運算符

APPLY運算符是一個非標準標準運算符。APPLY運算符對兩個輸入進行操做,其中右邊的表能夠是一個表表達式。

CROSS APPLY把右邊表達式應用到左表中的每一行,再把結果集組合起來,生成一個統一的結果表。和交叉鏈接類似

OUTER APPLY把右邊表達式應用到左表中的每一行,再把結果集組合起來,而後添加外部行。和左外聯接中增長外部行的那一步類似

 

 資料

https://www.cnblogs.com/kissdodog/archive/2013/06/24/3153012.html

https://blog.csdn.net/miqi770/article/details/51505720

https://www.cnblogs.com/janneystory/p/5623019.html

http://www.cnblogs.com/jackson0714/p/TSQLFundamentals_04_part1.html

相關文章
相關標籤/搜索