SQL Server-聚焦在視圖和UDF中使用SCHEMABINDING(二十六)


前言

上一節我們討論了視圖中的一些限制以及建議等,這節我們講講關於在UDF和視圖中使用SCHEMABINDING的問題,簡短的內容,深入的理解,Always to review the basics。

SCHEMABINDING

在上節中我們講到在視圖創建索引時必須指定SCHEMABINDING,所以我們有必要先去了解下這個知識點再繼續往下講解。SCHEMABINDING到底是什么呢?在視圖和UDF中有這個選項,如果在視圖和UDF函數中指定了這個選項,那么說明會將視圖和UDF嚴格綁定到數據庫對象中去,一來指定此選項可以將其嚴格綁定到數據庫對象中去,二來可以提高查詢計划執行的性能。下面我們來看看關於SCHEMABINDING在UDF和視圖中的使用。

在UDF中的使用

創建UDF函數有三種方式,我們一一來過一遍。

(1)創建TVF內嵌表值函數

USE TSQL2012
GO

IF OBJECT_ID('dbo.GetOrderId') IS NOT NULL
    DROP FUNCTION dbo.GetOrderId;
GO

CREATE FUNCTION dbo.GetOrderId 
    (@custid INT) RETURNS TABLE WITH SCHEMABINDING
AS   
RETURN
 SELECT orderid FROM Sales.Orders WHERE custid = @custid
GO
  

上述UDF是通過TVF的方式來創建,當需要在里面聲明一個臨時變量並返回時我們需要像如下操作。

(2)創建標量值函數

USE TSQL2012
GO

IF OBJECT_ID('dbo.GetOrderId') IS NOT NULL
    DROP FUNCTION dbo.GetOrderId;
GO

CREATE FUNCTION dbo.GetOrderId 
(@custid INT)  RETURNS INT WITH SCHEMABINDING
AS 
BEGIN  
  DECLARE @tempID INT  
  SELECT @tempID = orderid 
  FROM Sales.Orders  
  WHERE custid = @custid;      
  RETURN @tempID;  
END;  

當利用UDF來對查詢出來的數據進行插入到臨時表中時,我們可以像如下操作

(3)創建多語句TVF內嵌表值函數

USE TSQL2012
GO

CREATE FUNCTION [UDF]
(@PageNum int, @PageSize int)
RETURNS @TestTable TABLE (RowNumber INT, ID INT, Name VARCHAR(20))
AS
BEGIN
    declare @RowNumber int

    ;WITH C
    As (
        SELECT 'RowNumber' = ROW_NUMBER() OVER(ORDER BY id DESC), 
            orderid, shipname
        FROM Sales.Orders
        )
    INSERT    @TestTable
    SELECT rownumber, orderid, shipname
    from C

    RETURN
END

好了我們過了一遍關於UDF創建的幾種方式,我們回到主題,我們創建一個如下UDF

USE TSQL2012
GO

IF OBJECT_ID('dbo.GetId') IS NOT NULL
    DROP FUNCTION dbo.GetId;
GO

CREATE FUNCTION dbo.GetId 
    (@id INT) RETURNS TABLE WITH SCHEMABINDING
AS   
RETURN
 SELECT val1 FROM compare.t_inner WHERE id = @id
GO

此時我們在對應數據庫中的表值函數文件夾下能看到我們創建的函數

 

因為上述我們是查詢表compare.t_inner中的值,此時我們刪除該表看看。

此時我們會發現該表無法刪除出現上述錯誤。因為我們上述創建的UDF依賴於compare.t_inner表,所以現在無法刪除該表,該表引用了自定義函數GetId。下面我們修改上述我們在UDF中查詢的列val1為val3看看

在VIEW中的使用

USE TSQL2012
GO

IF OBJECT_ID('dbo.GetId') IS NOT NULL
    DROP FUNCTION dbo.GetId;
GO

CREATE VIEW GetId WITH SCHEMABINDING
AS
SELECT val1 FROM compare.t_inner

此時刪除表compare.t_inner依然會出現和UDF中的錯誤。在使用SCHEMABINDING約束時不能進行*操作,會出現如下圖錯誤:

USE TSQL2012
GO

IF OBJECT_ID('dbo.GetId') IS NOT NULL
    DROP FUNCTION dbo.GetId;
GO

CREATE VIEW GetId WITH SCHEMABINDING
AS
SELECT * FROM compare.t_inner

下面再看其他情況利用視圖到跨數據庫進行查詢,我們創建兩個數據庫並分別在對應數據庫創建一個測試表。

CREATE DATABASE TEST1
CREATE DATABASE TEST2
GO
-- Table1
USE Test1
GO
CREATE TABLE TABLE1 (ID INT)
GO
USE Test2
GO
-- Table2
CREATE TABLE TABLE2 (ID INT)
GO
USE Test1
GO

接下來通過執行SCHEMABINDING來創建視圖

CREATE VIEW CrossDBView
WITH SCHEMABINDING
AS
SELECT t1.ID AS t1id, t2.ID AS t2id
FROM Test1.dbo.Table1 t1
INNER JOIN Test2.dbo.Table2 t2 ON t1.ID = t2.ID
GO

上述指定SCHEMABINDING出現錯誤也就是說在跨數據庫查詢時會出現錯誤,對於引用對象僅限於兩部分名稱。到這里我們為在視圖和UDF中使用SCHEMABINDING作出如下結論:

(1)在視圖和UDF中使用SCHEMABINDING時必須滿足兩個要求,第一個是不允許在SELECT子句中使用*,第二個則是當引用對象時必須使用架構限定的兩部分名稱。

(2)在視圖上創建索引時必須指定SCHEMABINDING。

如上講了這么多關於SCHEMABINDING使用的限制,可以算是缺點吧,難道就沒優點了么,如果沒優點我們也不會講了,當然也沒必要給出SCHEMABINDING的使用了。當指定SCHEMABINDING時能提高UDF和視圖的查詢性能,當對象指定架構對象時,在查詢計划中不會產生不必要的Spoll操作。我們看如下例子:

CREATE FUNCTION dbo.ComputeNum(@i int)  
RETURNS int  
BEGIN  
  RETURN @i * 2 + 50  
END  

上述我們沒有提供SCHEMABINDING選項,此時UDF不會訪問任何數據庫對象,當一個函數和視圖沒有SCHEMABINDING選項時就無法確保底層的數據庫對象是什么,所以此時會去訪問每個正在執行的UDF,為了避免這種性能問題,我們通過指定SCHEMABINDING是安全的並且不會去遍歷訪問每一個正在運行的UDF。所以在視圖和UDF中一般建議指定SCHEMABINDING選項。

總結 

本節我們討論了在UDF和視圖中指定SCHEMABINDING的問題,其實對視圖查詢還是有諸多限制,大部分情況下利用常規查詢和存儲過程來實現更加靈活。我們下節看看APPLY運算符的使用,簡短的內容,深入的理解,我們下節再會。


免責聲明!

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



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