SQL 構建自定義函數(UDF)
UDF:user-defined functions,用戶自定義函數。
基本原理:
UDF是一個例程,它接受參數、執行操作並返回該操作的結果。根據定義,結果可以是標量值(單個)或表。
UDF的優點:
- UDF可以把復雜的邏輯嵌入到查詢中。UDF可以為復雜的表達式創建新函數。
- UDF可以運用在一個表達式或SELECT語句的FROM子句中,並且還可以綁定到架構。此外,UDF還可以接受參數。UDF有助於實施一致性和可重用性。
UDF的缺點:
該函數一旦誤用會產生潛在的性能問題。必須針對WHERE子句的每一行執行的任何函數,不管是用戶定義的函數還是系統函數,都將減慢執行速度。
UDF的類型:
UDF主要有3種類型(Management Studio把內聯表值函數與多語句表值函數放到了一個組中):
- 標量函數
- 內聯表值函數
- 多語句表值函數
一、標量函數
標量函數是返回一個具體值的函數。函數可以接收多個參數、執行計算然後返回一個值。返回值通過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)