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


概述

  表表達式是一種命名的查詢表達式,代表一個有效地關系表。可以像其他表一樣,在數據處理中使用表表達式。

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

為什么使用表表達式:

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

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

派生表

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

派生表可以簡化查詢,避免使用臨時表。相比手動生成臨時表性能更優越。派生表與其他表一樣出現在查詢的FROM子句中

select * from (select * from athors)  temp

temp 就是派生表

派生出來的表必須要是一個有效的表.因此,它必須遵守以下幾條規則:

  1. 所有列必須要有名稱

  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


免責聲明!

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



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