建立用戶定義函數。這是一個已保存 Transact-SQL 或公共語言運行時 (CLR) 例程,該例程可返回一個值。用戶定義函數不能用於執行修改數據庫狀態的操做。與系統函數同樣,用戶定義函數可從查詢中調用。標量函數和存儲過程同樣,可以使用 EXECUTE 語句執行。html 用戶定義函數可以使用 ALTER FUNCTION 修改,使用 DROP FUNCTION 刪除。sql 語法
|
用戶定義函數爲標量值函數或表值函數。若是 RETURNS 子句指定了一種標量數據類型,則函數爲標量值函數。可使用多條 Transact-SQL 語句定義標量值函數。express
若是 RETURNS 子句指定 TABLE,則函數爲表值函數。根據函數主體的定義方式,表值函數可分爲內聯函數或多語句函數。有關詳細信息,請參閱 表值用戶定義函數。編程
下列語句在函數內有效:安全
用戶定義函數能夠嵌套;也就是說,用戶定義函數可相互調用。被調用函數開始執行時,嵌套級別將增長;被調用函數執行結束後,嵌套級別將減小。用戶定義函數的嵌套級別最多可達 32 級。若是超出最大嵌套級別數,整個調用函數鏈將失敗。架構
注意: |
---|
從 Transact-SQL 用戶定義函數對託管代碼的任何引用都將計入 32 級嵌套限制的一個級別。從託管代碼內部調用的方法不根據此限制進行計數。
|
在 SQL Server 的早期版本中,函數只能分爲肯定性函數和不肯定性函數兩類。在 SQL Server 2005 中,函數具備下列屬性。這些屬性的值肯定了函數是否可用於持久化計算列或索引計算列。ide
屬性 | 說明 | 注意 |
---|---|---|
IsDeterministic函數 |
函數是肯定性函數仍是不肯定性函數。ui |
肯定性函數中容許本地數據訪問。例如,若是每次使用一組特定輸入值和相同數據庫狀態調用函數時,函數都返回相同結果,則該函數將被標記爲肯定性函數。 |
IsPrecise |
函數是精確函數仍是不精確函數。 |
不精確函數包含浮點運算之類的運算。 |
IsSystemVerified |
SQL Server 可驗證函數的精度和肯定性屬性。 |
|
SystemDataAccess |
函數能夠訪問 SQL Server 的本地實例中的系統數據(系統目錄或虛擬系統表)。 |
|
UserDataAccess |
函數能夠訪問 SQL Server 的本地實例中的用戶數據。 |
包含用戶定義表和臨時表,但不包含表變量。 |
Transact-SQL 函數的精度和肯定性屬性由 SQL Server 自動肯定。有關詳細信息,請參閱用戶定義函數的設計指導原則。CLR 函數的數據訪問權限和肯定性屬性可由用戶指定。有關詳細信息,請參閱 Overview of CLR Integration Custom Attributes。
若要顯示這些屬性的當前值,請使用 OBJECTPROPERTYEX。
若是用戶定義函數具備下列屬性值,則能夠在索引中使用調用用戶定義函數的計算列:
有關詳細信息,請參閱爲計算列建立索引。
若是在函數中調用擴展存儲過程,則該過程不能向客戶端返回結果集。向客戶端返回結果集的任何 ODS API 都將返回 FAIL。擴展存儲過程能夠鏈接回 SQL Server 的實例;不過,該過程不該嘗試與調用擴展存儲過程的函數同時聯接到同一事務。
與經過批處理或存儲過程進行調用類似,擴展存儲過程在運行 SQL Server 的 Windows 安全賬戶的上下文中執行。存儲過程的全部者在授予用戶 EXECUTE 權限時應考慮這一點。
可在使用標量表達式的位置調用標量值函數。這包括計算列和 CHECK 約束定義。也可使用 EXECUTE 語句執行標量值函數。在容許表表達式的狀況下,可在 SELECT、INSERT、UPDATE 或 DELETE 語句的 FROM 子句中調用表值函數。有關詳細信息,請參閱執行用戶定義函數(數據庫引擎)。
若是在 CLR 函數中指定了參數,則這些參數應爲 SQL Server 類型,即之前爲 scalar_parameter_data_type 定義的類型。有關將 SQL Server 系統數據類型與 CLR 集成數據類型或 .NET Framework 公共語言運行時數據類型進行比較的信息,請參閱 SQL Server Data Types and Their .NET Framework Equivalents。
爲了使 SQL Server 在類中重載時引用正確方法,<method_specifier> 中指示的方法必須具備下列特徵:
若是 CLR 函數的返回數據類型指定表類型 (RETURNS TABLE),則 <method_specifier> 中方法的返回數據類型應爲 IEnumerator 或 IEnumerable 類型,且假定由函數建立者來實現接口。與 Transact-SQL 函數不一樣,CLR 函數不能在 <table_type_definition> 中包含 PRIMARY KEY、UNIQUE 或 CHECK 約束。 <table_type_definition> 中指定的列的數據類型,必須與 <method_specifier> 中的方法在執行時返回的結果集中的對應列的類型匹配。建立函數時不執行上述類型檢查。
有關對 CLR 函數編程的詳細信息,請參閱 CLR User-Defined Functions。
下列 Service Broker 語句不能包含在 Transact-SQL 用戶定義函數的定義中:
若要顯示 Transact-SQL 用戶定義函數的定義,請使用函數所在數據庫中的 sys.sql_modules 目錄視圖。
例如:
複製代碼 | |
---|---|
USE AdventureWorks; GO SELECT Definition FROM sys.sql_modules AS m JOIN sys.objects AS o ON m.object_id = o.object_id AND TYPE IN ('FN', 'IF', 'TF'); GO |
注意: |
---|
不能使用 sys.sql_modules 查看使用 ENCRYPTION 選項建立的函數定義;不過,可顯示有關加密函數的其餘信息。
|
若要顯示有關 CLR 用戶定義函數的信息,請使用函數所在數據庫中的 sys.assembly_modules 目錄視圖。
若要顯示有關用戶定義函數中定義的參數的信息,請使用函數所在數據庫中的 sys.parameters 目錄視圖。
若要顯示有關函數引用的對象的報表,請使用 sys.sql_dependencies。
用戶定義函數所屬的架構的名稱。
用戶定義函數的名稱。函數名稱必須符合有關標識符的規則,而且在數據庫中以及對其架構來講是惟一的。
注意: |
---|
即便未指定參數,函數名稱後也須要加上括號。
|
用戶定義函數的參數。可聲明一個或多個參數。
函數最多能夠有 1,024 個參數。執行函數時,若是未定義參數的默認值,則用戶必須提供每一個已聲明參數的值。
經過將 at 符號 (@) 用做第一個字符來指定參數名稱。參數名稱必須符合有關標識符的規則。參數是對應於函數的局部參數;其餘函數中可以使用相同的參數名稱。參數只能代替常量,而不能用於代替表名、列名或其餘數據庫對象的名稱。
注意: |
---|
在存儲過程或用戶定義函數中傳遞參數時,或在批語句中聲明和設置變量時,不會遵照 ANSI_WARNINGS。例如,若是將變量定義爲 char(3) 類型,而後將其值設置爲多於三個字符,則數據將截斷爲定義大小,而且 INSERT 或 UPDATE 語句能夠成功執行。
|
參數的數據類型及其所屬的架構,後者爲可選項。對於 Transact-SQL 函數,可使用除 timestamp 數據類型以外的全部數據類型(包括 CLR 用戶定義類型)。對於 CLR 函數,可使用除 text、ntext、image 和 timestamp 數據類型以外的全部數據類型(包括 CLR 用戶定義類型)。不能將非標量類型 cursor 和 table 指定爲 Transact-SQL 函數或 CLR 函數中的參數數據類型。
若是未指定 type_schema_name,則 SQL Server 2005 Database Engine 將按如下順序查找 scalar_parameter_data_type:
參數的默認值。若是定義了 default 值,則無需指定此參數的值便可執行函數。
注意: |
---|
能夠爲除 varchar(max) 和 varbinary(max) 數據類型以外的 CLR 函數指定默認參數值。
|
若是函數的參數有默認值,則該函數檢索默認值時必須指定 DEFAULT 關鍵字。此行爲與在存儲過程當中使用具備默認值的參數不一樣,在後一種狀況下,不提供參數一樣意味着使用默認值。
標量用戶定義函數的返回值。對於 Transact-SQL 函數,可使用除 timestamp 數據類型以外的全部數據類型(包括 CLR 用戶定義類型)。對於 CLR 函數,可使用除 text、ntext、image 和 timestamp 數據類型以外的全部數據類型(包括 CLR 用戶定義類型)。不能將非標量類型 cursor 和 table 指定爲 Transact-SQL 函數或 CLR 函數中的返回數據類型。
指定一系列定義函數值的 Transact-SQL 語句,這些語句在一塊兒使用不會產生負面影響(例如修改表)。function_body 僅用於標量函數和多語句表值函數。
在標量函數中,function_body 是一系列 Transact-SQL 語句,這些語句一塊兒使用的計算結果爲標量值。
在多語句表值函數中,function_body 是一系列 Transact-SQL 語句,這些語句將填充 TABLE 返回變量。
指定標量函數返回的標量值。
指定表值函數的返回值爲表。只有常量和 @local_variables 能夠傳遞到表值函數。
在內聯表值函數中,TABLE 返回值是經過單個 SELECT 語句定義的。內聯函數沒有關聯的返回變量。
在多語句表值函數中,@return_variable 是 TABLE 變量,用於存儲和彙總應做爲函數值返回的行。只能將 @return_variable 指定用於 Transact-SQL 函數,而不能用於 CLR 函數。
定義內聯表值函數的返回值的單個 SELECT 語句。
指定將程序集與函數綁定的方法。assembly_name 必須與 SQL Server 中當前數據庫內具備可見性的現有程序集匹配。class_name 必須是有效的 SQL Server 標識符,而且必須做爲類存在於程序集中。若是類具備以命名空間限定的名稱,該名稱使用句點 (.) 來分隔命名空間的各部分,則必須使用方括號 ([ ]) 或引號 (" ") 分隔類名稱。method_name 必須是有效的 SQL Server 標識符,而且必須做爲靜態方法存在於指定類中。
注意: |
---|
默認狀況下,SQL Server 不能執行 CLR 代碼。能夠建立、修改和刪除引用公共語言運行時模塊的數據庫對象;不過,只有在啓用 clr enabled 選項以後,才能在 SQL Server 中執行這些引用。若要啓用此選項,請使用 sp_configure。
|
定義 Transact-SQL 函數的表數據類型。表聲明包含列定義和列約束(或表約束)。表始終放在主文件組中。
定義 CLR 函數的表數據類型。表聲明僅包含列名稱和數據類型。表始終放在主文件組中。
<function_option>::= and <clr_function_option>::=
指定函數將具備如下一個或多個選項:
指示數據庫引擎 對包含 CREATE FUNCTION 語句文本的目錄視圖列進行加密。使用 ENCRYPTION 能夠防止將函數做爲 SQL Server 複製的一部分發布。不能爲 CLR 函數指定 ENCRYPTION。
指定將函數綁定到其引用的數據庫對象。若是其餘架構綁定對象也在引用該函數,此條件將防止對其進行更改。
只有發生下列操做之一時,纔會刪除函數與其引用對象的綁定:
只有知足如下條件時,函數才能綁定到架構:
不能爲 CLR 函數或引用別名數據類型的函數指定 SCHEMABINDING。
指定標量值函數的 OnNULLCall 屬性。若是未指定,則默認爲 CALLED ON NULL INPUT。這意味着即便傳遞的參數爲 NULL,也將執行函數體。
若是在 CLR 函數中指定了 RETURNS NULL ON NULL INPUT,它指示當 SQL Server 接收到的任何一個參數爲 NULL 時,它能夠返回 NULL,而無需實際調用函數體。若是 <method_specifier> 中指定的 CLR 函數的方法已具備指示 RETURNS NULL ON NULL INPUT 的自定義屬性,但 CREATE FUNCTION 語句指示 CALLED ON NULL INPUT,則優先採用 CREATE FUNCTION 語句指示的屬性。不能爲 CLR 表值函數指定 OnNULLCall 屬性。
指定用於執行用戶定義函數的安全上下文。因此,您能夠控制 SQL Server 使用哪個用戶賬戶來驗證針對該函數引用的任何數據庫對象的權限。
注意: |
---|
不能爲內聯用戶定義函數指定 EXECUTE AS。
|
有關詳細信息,請參閱EXECUTE AS 子句 (Transact-SQL)。
< column_definition >::=
定義表數據類型。表聲明包含列定義和約束。對於 CLR 函數,只能指定 column_name 和 data_type。
表中列的名稱。列名稱必須符合標識符規則,而且在表中必須是惟一的。column_name 能夠由 1 至 128 個字符組成。
指定列數據類型。對於 Transact-SQL 函數,可使用除 timestamp 以外的全部數據類型(包括 CLR 用戶定義類型)。對於 CLR 函數,可使用除 text、ntext、image、char、varchar、varchar(max) 和 timestamp 以外的全部數據類型(包括 CLR 用戶定義類型)。在 Transact-SQL 或 CLR 函數中,非標量類型 cursor 不能指定爲列數據類型。
指定當插入過程當中沒有顯式提供值時爲列提供的值。constant_expression 能夠是常量、NULL 或系統函數值。DEFAULT 定義能夠應用於除具備 IDENTITY 屬性的列以外的任何列。不能爲 CLR 表值函數指定 DEFAULT。
指定列的排序規則。若是未指定,則爲此列分配數據庫的默認排序規則。排序規則名稱既能夠是 Windows 排序規則名稱,也能夠是 SQL 排序規則名稱。有關排序規則的列表及詳細信息,請參閱 Windows 排序規則名稱 (Transact-SQL)和 SQL 排序規則名稱 (Transact-SQL)。
COLLATE 子句只能用來更改數據類型爲 char、varchar、nchar 和 nvarchar 的列的排序規則。
不能爲 CLR 表值函數指定 COLLATE。
指示新列是行的全局惟一標識符列。對於每一個表,只能將其中的一個 uniqueidentifier 列指定爲 ROWGUIDCOL 列。ROWGUIDCOL 屬性只能分配給 uniqueidentifier 列。
ROWGUIDCOL 屬性並不強制實現列中存儲的值的惟一性。該屬性也不會爲插入表的新行自動生成值。若要爲每列生成惟一值,請在 INSERT 語句中使用 NEWID 函數。能夠指定默認值;可是,不能將 NEWID 指定爲默認值。
指示新列是標識列。在爲表添加新行時,SQL Server 將爲該列提供惟一的增量值。標識列一般與 PRIMARY KEY 約束一塊兒使用,做爲表的惟一行標識符。能夠將 IDENTITY 屬性分配給 tinyint、smallint、int、bigint、decimal(p,0) 或 numeric(p,0) 列。每一個表只能建立一個標識列。不能將綁定默認值和 DEFAULT 約束用於標識列。必須同時指定 seed 和 increment,或者兩者都不指定。若是兩者都未指定,則取默認值 (1,1)。
不能爲 CLR 表值函數指定 IDENTITY。
要分配給表中第一行的整數值。
要加到表中後續行的 seed 值上的整數值。
< column_constraint >::= and < table_constraint>::=
爲指定列或表定義約束。對於 CLR 函數,容許的惟一約束類型爲 NULL。不容許命名約束。
肯定列中是否容許空值。嚴格講來,NULL 不是約束,但能夠像指定 NOT NULL 那樣指定它。不能爲 CLR 表值函數指定 NOT NULL。
一個約束,該約束經過惟一索引來強制指定列的實體完整性。在表值用戶定義函數中,只能對每一個表中的一列建立 PRIMARY KEY 約束。不能爲 CLR 表值函數指定 PRIMARY KEY。
一個約束,該約束經過惟一索引爲一個或多個指定列提供實體完整性。一個表能夠有多個 UNIQUE 約束。不能爲 CLR 表值函數指定 UNIQUE。
指示爲 PRIMARY KEY 或 UNIQUE 約束建立彙集索引仍是非彙集索引。PRIMARY KEY 約束使用 CLUSTERED,而 UNIQUE 約束使用 NONCLUSTERED。
只能爲一個約束指定 CLUSTERED。若是爲 UNIQUE 約束指定了 CLUSTERED,而且指定了 PRIMARY KEY 約束,則 PRIMARY KEY 使用 NONCLUSTERED。
不能爲 CLR 表值函數指定 CLUSTERED 和 NONCLUSTERED。
一個約束,該約束經過限制可輸入一列或多列中的可能值來強制實現域完整性。不能爲 CLR 表值函數指定 CHECK 約束。
返回 TRUE 或 FALSE 的邏輯表達式。
<computed_column_definition>::=
指定計算列。有關計算列的詳細信息,請參閱 CREATE TABLE (Transact-SQL)。
計算列的名稱。
定義計算列的值的表達式。
<index_option>::=
爲 PRIMARY KEY 或 UNIQUE 索引指定索引選項。有關索引選項的詳細信息,請參閱 CREATE INDEX (Transact-SQL)。
指定索引填充。默認值爲 OFF。
指定一個百分比,指示在建立或更改索引期間,數據庫引擎 對各索引頁的葉級填充的程度。fillfactor 必須爲介於 1 至 100 之間的整數值。默認值爲 0。
指定當對惟一彙集索引或惟一非彙集索引的多行插入事務中出現重複鍵值時的錯誤響應。默認值爲 OFF。
指定是否從新計算分佈統計信息。默認值爲 OFF。
指定是否容許行鎖。默認值爲 ON。
指定是否容許頁鎖。默認值爲 ON。
須要在數據庫中具備 CREATE FUNCTION 權限,並對建立函數時所在的架構具備 ALTER 權限。若是函數指定用戶定義類型,則須要對該類型具備 EXECUTE 權限。
如下示例將建立用戶定義函數 ISOweek
。此函數使用日期參數來計算 ISO 週數。要使此函數能正確計算,必須在調用該函數前調用 SET DATEFIRST 1
。
另外,該示例將顯示如何使用 EXECUTE AS
子句指定可在其中執行存儲過程的安全上下文。在該示例中,CALLER
選項指定該過程將在調用該過程的用戶的上下文中執行。您還能夠指定 SELF、OWNER 和 user_name 等其餘選項。有關詳細信息,請參閱EXECUTE AS 子句 (Transact-SQL)。
複製代碼 | |
---|---|
USE AdventureWorks; GO IF OBJECT_ID (N'dbo.ISOweek', N'FN') IS NOT NULL DROP FUNCTION dbo.ISOweek; GO CREATE FUNCTION dbo.ISOweek (@DATE datetime) RETURNS int WITH EXECUTE AS CALLER AS BEGIN DECLARE @ISOweek int SET @ISOweek= DATEPART(wk,@DATE)+1 -DATEPART(wk,CAST(DATEPART(yy,@DATE) as CHAR(4))+'0104') --Special cases: Jan 1-3 may belong to the previous year IF (@ISOweek=0) SET @ISOweek=dbo.ISOweek(CAST(DATEPART(yy,@DATE)-1 AS CHAR(4))+'12'+ CAST(24+DATEPART(DAY,@DATE) AS CHAR(2)))+1 --Special case: Dec 29-31 may belong to the next year IF ((DATEPART(mm,@DATE)=12) AND ((DATEPART(dd,@DATE)-DATEPART(dw,@DATE))>= 28)) SET @ISOweek=1 RETURN(@ISOweek) END; GO |
下面是函數調用。請注意,DATEFIRST
設置爲 1
。
複製代碼 | |
---|---|
SET DATEFIRST 1; SELECT dbo.ISOweek(CONVERT(DATETIME,'12/26/2004',101)) AS 'ISO Week'; |
下面是結果集:
複製代碼 | |
---|---|
ISO Week ---------------- 52 |
如下示例將返回內聯表值函數。對於銷售給商店的每一個產品,該函數返回三列,分別爲 ProductID
、Name
以及各個商店年初至今總數的累計 YTD Total
。
複製代碼 | |
---|---|
USE AdventureWorks; GO IF OBJECT_ID (N'Sales.fn_SalesByStore', N'IF') IS NOT NULL DROP FUNCTION Sales.fn_SalesByStore; GO CREATE FUNCTION Sales.fn_SalesByStore (@storeid int) RETURNS TABLE AS RETURN ( SELECT P.ProductID, P.Name, SUM(SD.LineTotal) AS 'YTD Total' FROM Production.Product AS P JOIN Sales.SalesOrderDetail AS SD ON SD.ProductID = P.ProductID JOIN Sales.SalesOrderHeader AS SH ON SH.SalesOrderID = SD.SalesOrderID WHERE SH.CustomerID = @storeid GROUP BY P.ProductID, P.Name ); GO |
若要調用該函數,請運行此查詢。
複製代碼 | |
---|---|
SELECT * FROM Sales.fn_SalesByStore (602); |
如下示例建立了表值函數 fn_FindReports(InEmpID)
。若是提供一個有效僱員 ID,該函數將返回一個表,該表對應於直接或間接向該僱員報告的全部僱員。該函數使用遞歸公用表表達式 (CTE) 來生成僱員的層次結構列表。有關遞歸 CTE 的詳細信息,請參閱 WITH common_table_expression (Transact-SQL)。
複製代碼 | |
---|---|
USE AdventureWorks; GO IF OBJECT_ID (N'dbo.fn_FindReports', N'TF') IS NOT NULL DROP FUNCTION dbo.fn_FindReports; GO CREATE FUNCTION dbo.fn_FindReports (@InEmpID INTEGER) RETURNS @retFindReports TABLE ( EmployeeID int primary key NOT NULL, Name nvarchar(255) NOT NULL, Title nvarchar(50) NOT NULL, EmployeeLevel int NOT NULL, Sort nvarchar (255) NOT NULL ) --Returns a result set that lists all the employees who report to the --specific employee directly or indirectly.*/ AS BEGIN WITH DirectReports(Name, Title, EmployeeID, EmployeeLevel, Sort) AS (SELECT CONVERT(Varchar(255), c.FirstName + ' ' + c.LastName), e.Title, e.EmployeeID, 1, CONVERT(Varchar(255), c.FirstName + ' ' + c.LastName) FROM HumanResources.Employee AS e JOIN Person.Contact AS c ON e.ContactID = c.ContactID WHERE e.EmployeeID = @InEmpID UNION ALL SELECT CONVERT(Varchar(255), REPLICATE ('| ' , EmployeeLevel) + c.FirstName + ' ' + c.LastName), e.Title, e.EmployeeID, EmployeeLevel + 1, CONVERT (Varchar(255), RTRIM(Sort) + '| ' + FirstName + ' ' + LastName) FROM HumanResources.Employee as e JOIN Person.Contact AS c ON e.ContactID = c.ContactID JOIN DirectReports AS d ON e.ManagerID = d.EmployeeID ) -- copy the required columns to the result of the function INSERT @retFindReports SELECT EmployeeID, Name, Title, EmployeeLevel, Sort FROM DirectReports RETURN END; GO -- Example invocation SELECT EmployeeID, Name, Title, EmployeeLevel FROM dbo.fn_FindReports(109) ORDER BY Sort; |
如下示例假定在本地計算機的默認位置安裝了 SQL Server Database Engine samples,而且已編譯了 StringManipulate.csproj 示例應用程序。有關詳細信息,請參閱 可以識別補充字符的字符串操做。
該示例將建立 CLR 函數 len_s
。在建立該函數以前,程序集 SurrogateStringFunction.dll
已在本地數據庫中註冊。
複製代碼 | |
---|---|
DECLARE @SamplesPath nvarchar(1024); -- You may have to modify the value of the this variable if you have --installed the sample someplace other than the default location. SELECT @SamplesPath = REPLACE(physical_name, 'Microsoft SQL Server/MSSQL.1/MSSQL/DATA/master.mdf', 'Microsoft SQL Server/90/Samples/Engine/Programmability/CLR/') FROM master.sys.database_files WHERE name = 'master'; CREATE ASSEMBLY [SurrogateStringFunction] FROM @SamplesPath + 'StringManipulate/CS/StringManipulate/bin/debug/SurrogateStringFunction.dll' WITH PERMISSION_SET = EXTERNAL_ACCESS; GO CREATE FUNCTION [dbo].[len_s] (@str nvarchar(4000)) RETURNS bigint AS EXTERNAL NAME [SurrogateStringFunction].[Microsoft.Samples.SqlServer.SurrogateStringFunction].[LenS]; GO |