1. 程式人生 > >基礎很重要~~04.表表達式-下篇

基礎很重要~~04.表表達式-下篇

 以前總是追求新東西,發現基礎才是最重要的,今年主要的目標是精通SQL查詢和SQL效能優化。

本系列【T-SQL基礎】主要是針對T-SQL基礎的總結。

概述:

本篇主要是對錶表示式中檢視和內聯表值函式基礎的總結。

表表達式包含四種:

  1.派生表

  2.公用表表達式

  3.檢視

  4.內聯表值函式

本篇是表表達式的下篇,只會講到檢視和內聯表值函式。

下面是表表達式的思維導圖:

 

表表達式:

1.一種命名的查詢表示式,代表一個有效的關係表。

2.可以像其他表一樣,在資料處理語句中使用表表達式。

3.在物理上不是真實存在的什麼物件,它們是虛擬的。對於表示式的查詢在資料庫引擎內部都將轉化為對底層物件的查詢。

為什麼使用表表達式:

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

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

在閱讀下面的章節時,我們可以先把環境準備好,以下的SQL指令碼可以幫助大家建立資料庫,建立表,插入資料。

一、檢視

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

區別:

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

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

共同點:

在很多方面,檢視和內聯表值函式的處理方式都類似於派生表和CTE。當查詢檢視和內聯表值函式時,SQL Server會先擴充套件表表達式的定義,再直接查詢底層物件。

2.語法

下面的例子定義了一個檢視,檢視名稱為Sales.USACusts,查詢所有來自美國的客戶。

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

定義好了檢視之後,在資料庫中重新整理檢視列表之後就會出現剛剛建立的檢視Sales.USACusts

  

然後,就可以像查詢資料庫中其他表一樣對檢視進行查詢:

SELECT  custid ,
        companyname
FROM    sales.usacusts

3.檢視的許可權

可以像其他資料庫物件一樣,對檢視的許可權進行控制:如SELECTINSERTUPDATEDELETE許可權

4.避免使用SELECT * 語句

列是在編譯檢視時進行列舉的,新加的列不會自動加到檢視中。以後對檢視中用到的表增加了幾列,這些列不會自動新增到檢視中。可以用sp_refreshview的儲存過程重新整理檢視的元資料,但是為了以後的維護,還是在檢視中顯示地需要的列名。如果在底層表中添加了列,而在檢視中需要這些新加的列,可以使用ALTER VIEW語句對檢視定義進行相應的修改。

5.建立檢視的要求:

必須要滿足之前介紹派生表時對錶表示式提到的所有要求:

  a.列必須有名稱

  b.列必須唯一

  c.不保證有一定的順序。在定義表表達式的查詢語句中不允許出現ORDER BY字句。因為關係表的行之間沒有順序。

6.加密選項ENCRYPTION

在建立檢視、儲存過程、觸發器及使用者定義函式時,都可以使用ENCRYPTION加密選項。如果指定ENCRYPTION選項,SQL Server在內部會對定義物件的文字資訊進行混淆(obfuscated)處理。普通使用者看不到該檢視的文字,只有特權使用者通過特殊手段才能訪問建立物件的文字。

在檢視定義的頭部,用WITH字句來指定ENCRYPTION選項,如下所示:

CREATE VIEW Sales.USACusts WITH ENCRYPTION

可以用下面的語句檢視檢視的文字:

SELECT OBJECT_DEFINITION(OBJECT_ID('Sales.USACusts'))

結果如下:

  

也可以用sp_helptext檢視檢視的文字:

sp_helptext  'Sales.USACusts'

結果如下:

 只有在對安全要求較高的情況下才需要對檢視進行加密,一般情況不需要加密。

7.架構繫結選項SCHEMABINDING

檢視和使用者自定義函式支援SCHEMABINDING選項。一旦指定了這個選項,檢視引用的物件不能刪除,被引用的列不能刪除或修改。

在檢視定義的頭部,用WITH字句來指定SCHEMABINDING選項,如下所示:

CREATE VIEW Sales.USACusts WITH SCHEMABINDING

可以用下面的語句,更新Sales.USACusts檢視所引用的Sales.Customers物件address

ALTER TABLE Sales.Customers DROP COLUMN address

結果如下:

建議在建立檢視時,使用SCHEMABINDING選項。

如果使用SCHEMABINDING選項,必須滿足兩個技術要求:

  a.必須在SELECT字句中顯示地列出列名

  b.在引用物件時,必須使用帶有架構名稱修飾的完整物件名稱。

8.CHECK OPTION選項

CHECK OPTION選項的目的是為了防止通過檢視執行的資料修改與檢視中設定的過濾條件(假設在定義檢視的查詢中存在過濾條件)發生衝突。

假設想通過Sales.USACusts檢視往Sales.Customers表中插入資料,可以使用下面的語句:

INSERT  INTO Sales.USACusts
        ( companyname ,
          contactname ,
          contacttitle ,
          address ,
          city ,
          region ,
          postalcode ,
          country ,
          phone ,
          fax
        )
VALUES  ( 'A' ,
          'A' ,
          'A' ,
          'A' ,
          'London' ,
          'A' ,
          'A' ,
          'UK' ,
          '123' ,
          '123'
        )

然後查詢Sales.Customers表,如下所示:

SELECT  custid,companyname,country
FROM    Sales.Customers
WHERE   companyname = 'A'  

結果:

 

 如果用檢視進行查詢,如下所示:

SELECT  custid ,
        companyname ,
        country
FROM    Sales.USACusts
WHERE   companyname = 'A'

則得到的是一個空的結果集,因為檢視中的WHERE條件WHERE country = N'USA'只篩選來自美國的客戶。

如果想防止這種與檢視的查詢過濾條件相沖突的修改,只須在定義檢視的查詢語句末尾加上WITH CHECK OPTION即可:

ALTER VIEW [Sales].[USACusts]
AS
    SELECT  custid ,
            companyname ,
            contactname ,
            contacttitle ,
            address ,
            city ,
            region ,
            postalcode ,
            country ,
            phone ,
            fax
    FROM    Sales.Customers
    WHERE   country = N'USA'
WITH CHECK OPTION;
GO

再試下插入與檢視的過濾條件相沖突的記錄:

INSERT  INTO Sales.USACusts
        ( companyname ,
          contactname ,
          contacttitle ,
          address ,
          city ,
          region ,
          postalcode ,
          country ,
          phone ,
          fax
        )
VALUES  ( 'A' ,
          'A' ,
          'A' ,
          'A' ,
          'London' ,
          'A' ,
          'A' ,
          'UK' ,
          '123' ,
          '123'
        )

結果如下:

9.練習題:

1)建立一個檢視,返回每個僱員每年處理的總訂貨量:

期望結果:

本題考察檢視的建立

IF OBJECT_ID('Sales.VEmpOrders') IS NOT NULL 
    DROP VIEW Sales.VEmpOrders;
GO
CREATE VIEW Sales.VEmpOrders
AS
    SELECT  empid ,
            YEAR(orderdate) AS orderyear ,
            SUM(qty) AS qty
    FROM    Sales.Orders AS O
            INNER JOIN Sales.OrderDetails AS D ON O.orderid = D.orderid
    GROUP BY empid ,
            YEAR(orderdate);

GO

2)寫一個對Sales.VEmpOrders表的查詢,返回每個僱員每年處理過的連續總訂貨量

期望的輸出:

此題需要用到相關子查詢:

SELECT  empid ,
        orderyear ,
        qty ,
        ( SELECT    SUM(qty) AS runqty
          FROM      Sales.VEmpOrders AS EO2
          WHERE     EO1.empid = EO2.empid
                    AND EO1.orderyear <= EO2.orderyear
        )
FROM    Sales.VEmpOrders AS EO1
ORDER BY EO1.empid ,

子查詢返回訂單年份小於或等於外查詢當前行的訂單年份的所有行,並計算這些行的訂貨量之和。

二、內聯表值函式

1.什麼是內聯表值函式

一種可重用的表表達式,能夠支援輸入引數。除了支援輸入引數以外,內聯表值函式在其他方面都與檢視相似。

2.如何定義內聯表值函式

下面的例子建立了一個函式fn_GetCustOrders。這個內聯表值接收一個輸入客戶ID引數@cid,另外一個輸入引數訂單年份引數@orderdateyear,返回客戶ID等於@cid的客戶下的所有訂單,且訂單的訂單年份等於@orderdateyear

IF OBJECT_ID('dbo.fn_GetCustOrders') IS NOT NULL 
    DROP FUNCTION dbo.fn_GetCustOrders
GO
CREATE FUNCTION dbo.fn_GetCustOrders ( @cid AS INT ,@orderdateyear AS DATETIME)
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 AND YEAR(orderdate) = YEAR(@orderdateyear)
Go

定義好了內聯表值函式之後,在資料庫中重新整理可程式設計性-函式-表值函式列表之後就會出現剛剛建立的函式fn_GetCustOrders

3.如何使用內聯表值函式

可以用內聯表值函式查詢出客戶id=1,訂單日期年份=2008的所有訂單:

SELECT orderid,custid,orderdate FROM fn_GetCustOrders(1,'2008')

內聯表值函式也可以用在聯接查詢中:

下面的例子是用內聯表值函式與HR.Employees表進行關聯,查詢出客戶id=1,訂單日期年份=2008的所有訂單,以及處理對應訂單的員工詳情:

SELECT  orderid ,
        custid ,
        orderdate ,
        empid ,
        lastname ,
        firstname ,
        title
FROM    fn_GetCustOrders(1, '2008')
        INNER JOIN HR.Employees AS E ON dbo.fn_GetCustOrders.empid = E.empid 

 結果如下:

 

4.練習題

1)建立一個內聯表值函式,其輸入引數為供應商ID(@supid AS INT)和要求的產品數量(@n AS INT)。該函式返回給定供應商@supid提供的產品中,單價最高的@n個產品。

當執行以下查詢時:

SELECT * FROM fn_TopProducts(5,2)

期望結果:

本題可以分三個步驟:

1.寫一個查詢語句

SELECT TOP ( 1 )
        productid ,
        productname ,
        unitprice
FROM    Production.Products
WHERE   supplierid = 1
ORDER BY unitprice DESC;

2.將引數替換進去:

SELECT TOP ( @n )
        productid ,
        productname ,
        unitprice
FROM    Production.Products
WHERE   supplierid = @supid
ORDER BY unitprice DESC;

3.將這個查詢放到內聯表值函式中

IF OBJECT_ID('dbo.fn_TopProducts') IS NOT NULL 
    DROP FUNCTION dbo.fn_TopProducts
GO
CREATE FUNCTION dbo.fn_TopProducts ( @supid AS INT, @n AS INT )
RETURNS TABLE
AS RETURN
    SELECT TOP ( @n )
            productid ,
            productname ,
            unitprice
    FROM    Production.Products
    WHERE   supplierid = @supid
    ORDER BY unitprice DESC;
Go 

三、APPLY運算子

1.APPLY運算子

APPLY運算子是一個非標準標準運算子。APPLY運算子對兩個輸入進行操作,其中右邊的表可以是一個表表達式。

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

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

2.練習題

1)使用CROSS APPLY運算子和fn_TopProducts函式,為每個供應商返回兩個價格最貴的產品。

涉及到的表:Production.Suppliers

期望結果:

 

使用CROSS APPLY運算子為每個供應商應用前一個fn_TopProducts函式。

SELECT  supplierid ,
        companyname ,
        productid ,
        productname ,
        unitprice
FROM    Production.Suppliers AS S
        CROSS APPLY fn_TopProducts(S.supplierid, 2) AS P

參考資料:

《SQL2008技術內幕:T-SQL語言基礎》


作  者:
出  處:http://www.cnblogs.com/jackson0714/
關於作者:專注於微軟平臺的專案開發。如有問題或建議,請多多賜教!
版權宣告:本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連結。
特此宣告:所有評論和私信都會在第一時間回覆。也歡迎園子的大大們指正錯誤,共同進步。或者直接私信
聲援博主:如果您覺得文章對您有幫助,可以點選文章右下角推薦一下。您的鼓勵是作者堅持原創和持續寫作的最大動力!

相關推薦

基礎重要~~04.表表-下篇

 以前總是追求新東西,發現基礎才是最重要的,今年主要的目標是精通SQL查詢和SQL效能優化。 本系列【T-SQL基礎】主要是針對T-SQL基礎的總結。 概述: 本篇主要是對錶表示式中檢視和內聯表值函式基礎的總結。 表表達式包含四種:   1.派生表   2.公用表表達式   

基礎重要~~04.表表-上篇

 以前總是追求新東西,發現基礎才是最重要的,今年主要的目標是精通SQL查詢和SQL效能優化。 本系列【T-SQL基礎】主要是針對T-SQL基礎的總結。 概述: 本篇主要是對錶表示式中派生表和公用表表達式基礎的總結。 表表達式包含四種:   1.派生表   2.公用表表達式  

基礎重要~~04.表表達

cit sca 頭部 生成 res alter 加密 貴的 return 閱讀目錄 概述: 一、視圖 二、內聯表值函數 三、APPLY運算符 以前總是追求新東西,發現基礎才是最重要的,今年主要的目標是精通SQL查詢和SQL性能優化。 本系列【T-SQL基礎】主

SQL Server-表表基礎

sql server表表達式表表達式沒有任何的物理實例化,在查詢表表達式時它們是虛擬的,內部查詢是非嵌套的,換句話說,外部查詢和內部查詢直接合並到一個底層對象的查詢中,使用表表達式的好處通常與代碼的邏輯方面有關,而與代碼的性能無關-摘抄自SQL Server 2012基礎教程。在使用表表達式時我們必須滿足以下

JSTL表表基礎

ring 作用 狀態 value current style 結合 nco The <%@page language="java" pageEncoding="utf-8" import="java.util.*" %> <%@ taglib prefi

公用表表(CTE)

運行 語句 提升 ... 特性 自身 技術分享 欄目 結構   在編寫T-SQL代碼時,往往需要臨時存儲某些結果集。前面我們已經廣泛使用和介紹了兩種臨時存儲結果集的方法:臨時表和表變量。除此之外,還可以使用公用表表達式的方法。公用表表達式(Common Table Expr

sql 異常<除非另外還指定了 TOP、OFFSET 或 FOR XML,否則,ORDER BY 子句在視圖、內聯函數、派生表、子查詢和公用表表中無效。>

select sel top percent 異常 子句 cor 查詢 表達 問題:當子查詢內存在ORDER BY 字句時查詢會報錯 SQL: SELECT * FROM (   SELECT * FROM USER ORDER BY USER_CORD ) S. 解

python 鏈表表 map、filter易讀版

ali required rake 創建鏈表 保留 guid lambda dbo hand 鏈表推導式 [x for x in x] 鏈表推導式提供了一個創建鏈表的簡單途徑,無需使用 map(), filter() 以及 lambda。返回鏈表的定義通常要比

SQL中使用WITH AS提高性能-使用公用表表(CTE)簡化嵌套SQL

column 簡化 expr tor .com 獲取 另一個 rom 指定 一.WITH AS的含義 WITH AS短語,也叫做子查詢部分(subquery factoring),可以讓你做很多事情,定義一個SQL片斷,該SQL片斷會被整個SQL語句所用到。有的時候

HGDB中CTE(公共表表)的用法

目錄 文件用途 詳細資訊 文件用途 本文介紹了HGDB中CTE(公共表表達式)普通用法及遞迴用法。 詳細資訊     Common table expression(公共表表達式)簡稱CTE,由SQL:1999標準引入,CTE可以看成是一個臨時建立的檢視,

SQL Server 公用表表(CTE)實現遞迴

公用表表達式簡介: 公用表表達式 (CTE) 可以認為是在單個 SELECT、INSERT、UPDATE、DELETE 或 CREATE VIEW 語句的執行範圍內定義的臨時結果集。CTE 與派生表類似,具體表現在不儲存為物件,並且只在查詢期間有效。與派生表的不同之處在於,公用表表達式 (CTE) 具有一個

針對sql錯誤:“除非另外還指定了 TOP 或 FOR XML,否則,ORDER BY 子句在檢視、行內函數、派生表、子查詢和公用表表中無效”的原理與解決

執行sql語句: select * from ( select * from tab where ID>20 order by userID desc ) as a order by date desc 邏輯上看著挺對 但是報錯: 除非另

使用公用表表(CTE)簡化巢狀SQL 和進行遞迴呼叫

    上圖顯示了一個表中的資料,這個表有三個欄位:id、node_name、parent_id。實際上,這個表中儲存了一個樹型結構,分三層:省、市、區。其中id表示當前省、市或區的id號、node_name表示名稱、parent_id表示節點的父節點的id。    現在有一個需求,要查詢出某個省下面的所有

SQL中使用WITH AS提高效能-使用公用表表(CTE)簡化巢狀SQL

原文:http://wudataoge.blog.163.com/blog/static/80073886200961652022389/一.WITH AS的含義     WITH AS短語,也叫做子查詢部分(subquery factoring),可以讓你做很多事情,定義一

SQL Server2005雜談(3):公用表表(CTE)的遞迴呼叫

 本文為原創,如需轉載,請註明作者和出處,謝謝! 先看如下一個資料表(t_tree):     上圖顯示了一個表中的資料,這個表有三個欄位:id、node_name、parent_id。實際上,這個表

Sql — CTE公用表表和With用法總結

CTE(Common Table Expression) 公用表表達式,它是在單個語句的執行範圍內定義的臨時結果集,只在查詢期間有效。它可以自引用,也可在同一查詢中多次引用,實現了程式碼段的重複利用。 CTE最大的好處是提升T-Sql程式碼的可讀性,可以更加優雅簡潔的方

SQL Server2005雜談(2):公用表表(CTE)的遞迴呼叫

本文為原創,如需轉載,請註明作者和出處,謝謝! 上一篇:SQL Server2005雜談(1):使用公用表表達式(CTE)簡化巢狀SQL 先看如下一個資料表(t_tree):     上圖顯示了一

T-SQL查詢進階--詳解公用表表(CTE)

簡介      對於SELECT查詢語句來說,通常情況下,為了使T-SQL程式碼更加簡潔和可讀,在一個查詢中引用另外的結果集都是通過檢視而不是子查詢來進行分解的.但是,檢視是作為系統物件存在資料庫中,那對於結果集僅僅需要在儲存過程或是使用者自定義函式中使用一次的時候,

SQL Server2005中的公用表表(CTE)的遞迴呼叫

public resultset getResultSet(resultset) { if(resultset is null) { current_resultset =第一個結果集(包含省的記錄集) 將結果集的id儲存在集合中 getResu

除非另外還指定了 TOP 或 FOR XML,否則,ORDER BY 子句在視圖、內聯函數、派生表、子查詢和公用表表中無效

condition 分組 gif 篩選器 使用 外部 如果 代碼 con   SQL 不同於與其他編程語言的最明顯特征是處理代碼的順序。在大數編程語言中,代碼按編碼順序被處理,但是在SQL語言中,第一個被處理的子句是FROM子句,盡管SELECT語句第一個出現,但是幾乎總是