1. 程式人生 > >SQL 構建自定義函數(UDF)

SQL 構建自定義函數(UDF)

可選 date 性能問題 defined where子句 stat 單表 簡單 efault

UDF:user-defined functions,用戶自定義函數。

基本原理:

UDF是一個例程,它接受參數、執行操作並返回該操作的結果。根據定義,結果可以是標量值(單個)或表。

UDF的優點:

  1. UDF可以把復雜的邏輯嵌入到查詢中。UDF可以為復雜的表達式創建新函數。
  2. UDF可以運用在一個表達式或SELECT語句的FROM子句中,並且還可以綁定到架構。此外,UDF還可以接受參數。UDF有助於實施一致性和可重用性。

UDF的缺點:

該函數一旦誤用會產生潛在的性能問題。必須針對WHERE子句的每一行執行的任何函數,不管是用戶定義的函數還是系統函數,都將減慢執行速度。

UDF的類型:

UDF主要有3種類型(Management Studio把內聯表值函數與多語句表值函數放到了一個組中):

  1. 標量函數
  2. 內聯表值函數
  3. 多語句表值函數

一、標量函數


標量函數是返回一個具體值的函數。函數可以接收多個參數、執行計算然後返回一個值。返回值通過RETURN命令返回。用戶定義的函數中的每個可能代碼路徑都以RETURN命令結尾。

標量函數可以運用於SQL Server中的任何表達式,甚至在CHECK約束的表達式中也可以使用(但不推薦這種用法)。

  • 函數限制

標題函數必須是確定性的,也就是說標量函數必須反復地為相同的輸入參數返回相同的值。因此,如newid()函數和rand()函數不允許出現在標量函數中。不允許用戶定義標量函數更新數據庫、調用存儲過程或調用DBCC命令,唯一的例外是可以更新表變量。用戶定義函數不能返回BLOB(二進制大型對象)數據,如text、next、timestamp和image數據類型變量。也不能返回表變量可cursor數據類型。對於錯誤處理,UDF也不包含TRY...CATCH或RAISERROR。

UDF可以調用嵌套深度為32層以內的其他用戶定義函數,或者遞歸調用自己到32層的深度。當然,這只是理論限制,嵌套函數會嚴重影響性能,應盡可能避免使用嵌套函數。

  • 創建方法

1 CREATE FUNCTION FunctionName (InputParameters) 
2 RETURNS DataType 
3 AS
4 BEGIN
5   Code;
6   RETURN Expression;
7 END;

InputParameters輸入參數包含數據類型定義。參數可以設置默認值(Parameter = default ),需要註意的是在UDF中有默認值的參數並不能成為可選參數,為在調用函數時請求到默認值,需要把關鍵字DEFAULT傳遞到函數的默認值參數位置。

示例1:下面的UDF執行一個簡單的數學計算,其中第二個參數帶有默認值。

CREATE FUNCTION dbo.ufnCalculate
(@Numer_a numeric(5,2),
 @Numer_b numeric(5,2) = 1.0)
RETURNS numeric(5,2)
AS
BEGIN
  RETURN @Numer_a / @Numer_b ;
END;
GO

select dbo.ufnCalculate(15.3 , 6.54),
       dbo.ufnCalculate(9.0 , DEFAULT);

結果:
------  ------
2.38      9.00

示例2:計算並返回某個時間所在月份的天數。

CREATE FUNCTION [dbo].[GetMonthDay](@date datetime)
RETURNS int
AS 
BEGIN
  DECLARE @date1 datetime
  SELECT @date1 =Dateadd(MM,1,@date)
  RETURN day(Dateadd(DD,-day(@date1),@date1))
END;
  • 調用標量函數

在接受單值的表達式中,標量函數可用於任何地方。用戶定義的標量函數必須通過一個最少有兩部分的名稱(所有者.函數名)來調用。

下面的腳本演示了在數據庫的訂單表中調用示例2中的函數及其返回值。

SELECT S.BIL_DD,dbo.GetMonthDay(BIL_DD) as DAYS_M 
FROM Orders S

結果
BIL_DD        DAYS_M
------        ------
2019-01-31     31
2019-02-15     28

二、內聯表值函數


與視圖相似,內聯表值函數也是為一個存儲的SELECT語句封裝。內聯表值函數保留了視圖的優點,還添加了一些參數。

創建方法

內聯表值用戶定義函數沒有BEGIN / END主體。SELECT語句是作為一個虛擬數據表返回的:

CREATE FUNCTION FunctionName (InputParameters)
RETURNS Table 
AS 
RETURN (Select Statement);

示例:下面的示例返回某個客戶所訂購產品的匯總情況。

CREATE FUNCTION dbo.ufnGetProductTotalByCust (@custNo varchar (10))
RETURNS Table 
AS
RETURN(
SELECT H.CUS_NO,B.PRD_NO,SUM(B.QTY) as TOTAL_PRD 
FROM TF_POS AS B       --訂單貨品明細表
LEFT JOIN MF_POS  AS H --訂單客戶信息表
       ON H.OS_NO=B.OS_NO 
WHERE H.CUS_NO=@custNo  
GROUP BY H.CUS_NO,B.PRD_NO );
GO

調用內聯表值函數

通過dbo.ufnGetProductTotalByCust查詢客戶代號為"CT060228" 的產品匯總數據,函數出現在SELECT語句的FROM部分:

SELECT PRD_NO,TOTAL_PRD FROM 
dbo.ufnGetProductTotalByCust(CT060228) 
ORDER BY PRD_NO DESC 

返回結果(部分):

PRD_NO           TOTAL_PRD
------------   ------------------------
10910030006	5792.00000000
10910040003	10776.00000000
10912060014	11442.00000000
10913040009	9276.00000000
11410030028	900.00000000
......

與視圖的關系

與視圖相比,內聯表值函數的優勢在於其可以使用參數。而視圖不包含參數,而且在運行時想要限制結果需要把WHERE子句添加到調用視圖的SELECT語句中來實現。

示圖的調用示例,假設已經存在視圖dbo.vwProductTotalByCust,調用視圖時,在SELECT語句中添加了一個WHERE子句限制:

SELECT * FROM dbo.vwProductTotalByCust WHERE cus_no=‘CT060228‘

關聯方法

SQL 構建自定義函數(UDF)