1. 程式人生 > >Sql server 表表達式

Sql server 表表達式

sqlserve 即使 nta 優化 方式 通過 外部 使用 不可

1、表表達式概述

(1)表表達式(table expression) 是一個命名的查詢表達式、代表一個有效的關系表

(2)在DML 中,使用表表達式和使用其他表非常類似

(3)sqlserver 支持4種表表達式: 派生表、公用表達式(CTE)、視圖 、內嵌值函數(內嵌TVF)

(4)表表達式沒有任何的物理實例化、是虛擬的、內部查詢是嵌套 的

(5)使用表表達式的好處是代碼邏輯方便有關、與性能無關、表表達式本身不會對性能帶來正負影響

2、有效定義表表達式查詢需要滿足的3個要求

2.1 無法保證順序

(1)表表達式用於代表一個關系表、關系表的行是無序的,有序那是遊標。

(2)如果在表表達式,如派生表中使用order by ,必須使用top 或者 OFFSET 否則報錯

(3)即使在 派生表中使用order by ,在外部查詢沒有使用order by的情況下,查詢結果仍然是無序的

2.2 所有列都必須具有名稱

(1)在定義表表達式的查詢中,必須有名稱、如果出現表達式 或者 函數 導致沒有名稱的列出現,直接報錯

2.3所有列名必須唯一

(1)當連表查詢、兩種表具有相同名稱的列,就會到時該表表達式invalid ,此時通過別名解決

3、派生表

(1)派生表 也稱子查詢表、臨時表,是在from 子句中定義的,他們存在的範圍是外部查詢,一旦外部查詢接收,派生表就消失了

(2)派生表的查詢需要定義在括號內

(3)使用表表達式的一個好處就是,外部查詢可以引用內部查詢select 子句中分配的列別名、這就可以繞開where 和 group by 無法使用select子句分配別名的問題

(4)在定義派生表的查詢中,可以引用參數,參數可以是例行的能夠用於存儲過程或函數的變量

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

(5)如果定義的派生表查詢需要引用林外一個派生表,這將是一個嵌套派生表。嵌套是常見的編程問題方面、會導致代碼復制、可讀性差

(6)派生表是定義在外部查詢的from 子句中,由於不是外部查詢之前就已存在的物理事實,所以無法在同一個from中引用同一個派生表的多個實例

-- 解決方案: 基於同一個子查詢的多個派生表
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;

不能引用同一派生表的多個實例,導致維護多份相同代碼,這也是派生表的一個問題

4、公用表表達式(CTE)

(1)公用表表達式(CTE)是表表達式的另一種標準形式,與派生表類似,但具有幾個優勢

(2)CTE 通過with 語句定義

with USACusts as
(
  select custid, companyname
  from Sales.Customers
  where country = NUSA
)
select * from USACusts

(3) 表表達式的三個要求,在CTE 中也是都要滿足

(4)T-SQL 中with 子句可用於不同的目的,為避免歧義,在with子句用於定義CTE時 ,with前面的分句必須打分號隔開

(5)CTE 和 派生表一樣也支持量好難過命名方式、內嵌式 和 外部式

(6)CTE 中也可以使用參數

(7)表面上看CTE 和 派生表只有語義上的區別。但是先定義後使用的特點,解決了嵌套問題

可定義多個CTE ,用分號隔離;

相互引用,不再嵌套;

(8)不能在派生表中的括號內去定義CTE

(9)就外部查詢的from子句而言,CTE 在其之前就已經存在了,可以已用同一個CTE 的多個實例,避免了維護多份一樣的代碼

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;

(10)CTE 支持遞歸操作,但是遞歸操作很危險,有可能吧數據庫搞崩的,特別是取消了遞歸次數限制的情況

5、視圖(VIEW)

(1)派生表和CTE範圍有限、外部查詢完成就消失 、無法重復使用

(2)視圖和內嵌值函數(TVF)是兩種可以重復使用的表達式類型、其定義被存儲為數據庫對象、永久保存、直到顯式刪除

(3)其他方便,視圖和TVF 和CTE 一樣,比如查詢時,sql server 都會拓展表表達式的定義、直接查底層的對象

(4)創建視圖

create view  Sales.USACusts
as 
select 
  custid, companyname, contactname, contacttitle, address,
  city, region, postalcode, country, phone, fax
FROM Sales.Customers
WHERE country = NUSA;

(5)和CTE 一樣,視圖的別名也有內嵌式 和 外部式兩種

(6) 由於視圖是數據庫的對象,可以控制視圖的訪問權限、包括select 、insert 、update 、delete 權限

(7) 定義視圖是盡量不要使用select * ,因為在創建視圖之後,底層對象表發生改變、視圖中的列是不會同步的、顯式指定列,然後通過alter view 修改視圖定義

(8)視圖的select 也必須滿足表表達式的所有要求 、視圖是無法保證行的順序的、列名稱必須唯一且存在

(9)要想查詢視圖返回有序的結果集,需要在外部查詢中進行order by

(10) sql server 2012 嘗試通過 orderby xxx offset 0 rows 獲取 有序視圖,但這是當前的一種優化手段,不能絕對保證有序

(11)獲取視圖定義(創建視圖的sql 就是視圖定義)

select OBJECT_DEFINITION(OBJECT_ID(Sales.USACusts))

(11)視圖選項

在創建和更改視圖時,可以指定作為視圖定義一部分的視圖屬性 和 視圖選項。

①encryption 選項

encryption選項指示sql server 在內部以代碼混淆方式存儲對象定義文本,代碼混淆文本對通過任何目錄對象的用戶都是不可直接見的,僅對通過特定方法的特權用戶可見

-- 修改視圖、指定encryption選項 ,修改之後查詢視圖定義返回NULL
ALTER VIEW Sales.USACusts 
WITH ENCRYPTION
AS
SELECT
  custid, companyname, contactname, contacttitle, address,
  city, region, postalcode, country, phone, fax
FROM Sales.Customers
WHERE country = NUSA;

②schemabinding 選型

schemabinding 選項對視圖和UDF可用 ,將“被引用對象的架構和列(就是視圖的元數據表和列)” 綁定到 引用對象(視圖)的架構中 ,此時不能刪除和修改 被引用的列 和對象(不能刪除修改元數據表和列)

ALTER VIEW Sales.USACusts 
WITH schemabinding,encryption
AS
SELECT
  custid, companyname, contactname, contacttitle, address,
  city, region, postalcode, country, phone, fax
FROM Sales.Customers
WHERE country = NUSA;

③check option 選項

check option的目的是防止出現視圖修改與視圖視圖塞選的沖突,下面演示該沖突

-- 定義視圖,值查來自美國(USA)客戶

CREATE VIEW Sales.USACusts
AS
SELECT
  custid, companyname, contactname, contacttitle, address,
  city, region, postalcode, country, phone, fax
FROM Sales.Customers
WHERE country = NUSA;

-- 通過視圖往源數據表Customers表插入一條英國的用戶數據
INSERT INTO Sales.USACusts(
  companyname, contactname, contacttitle, address,
  city, region, postalcode, country, phone, fax)
 VALUES(
  NCustomer ABCDE, NContact ABCDE, NTitle ABCDE, NAddress ABCDE,
  NLondon, NULL, N12345, NUK, N012-3456789, N012-3456789);

-- 沖突出現,此時從視圖查詢不到剛剛往視圖插入的英國用戶數據,但是直接去Customers 表中查是有這條數據的
SELECT custid, companyname, country
FROM Sales.USACusts
WHERE companyname = NCustomer ABCDE;

check option 就是解決上述沖突、此時當通過視圖插入的數據不滿足視圖塞選條件,會直接報錯

-- 修改視圖定義,添加check point 屬性
ALTER VIEW Sales.USACusts 
WITH SCHEMABINDING
AS
SELECT
  custid, companyname, contactname, contacttitle, address,
  city, region, postalcode, country, phone, fax
FROM Sales.Customers
WHERE country = NUSA
WITH CHECK OPTION;

-- 測試插入與視圖塞選條件不符 的數據,報錯
INSERT INTO Sales.USACusts(
  companyname, contactname, contacttitle, address,
  city, region, postalcode, country, phone, fax)
 VALUES(
  NCustomer FGHIJ, NContact FGHIJ, NTitle FGHIJ, NAddress FGHIJ,
  NLondon, NULL, N12345, NUK, N012-3456789, N012-3456789);

6、內嵌表值函數(TVF)

(1)內嵌TVF 是支持輸入參數可重復使用的表表達式 ,可以理解成參數化視圖

(2)定義

-- 創建TVF ,傳入客戶cid, 查詢該客戶的訂單
create function 
dbo.GetCustOrders
(@cid as int) returns TABLE
return 
  select 
    orderid, custid, empid, orderdate, requireddate,
    shippeddate, shipperid, freight, shipname, shipaddress, shipcity,
    shipregion, shippostalcode, shipcountry
  from Sales.Orders
  where custid = @cid

(3)使用

-- 強烈建議給TVF 取別名,參數在括號內傳入
SELECT orderid, custid
FROM dbo.GetCustOrders(1) AS O;

Sql server 表表達式