建立使用者定義函數 (Database Engine)

本主題描述如何使用 Transact-SQL 在 SQL Server 中建立使用者定義函數。

本主題內容

開始之前

限制事項

  • 使用者定義函數不能用來執行修改資料庫狀態的動作。

  • 使用者定義函數不得包含具有資料表當做其目標的 OUTPUT INTO 子句。

  • 使用者定義函數無法傳回多個結果集。 如果您需要傳回多個結果集,請使用預存程序。

  • 使用者定義函數中限制錯誤處理。 UDF 不支援 TRY... @ERROR CATCH 或 RAISERROR。

  • 使用者定義函數無法呼叫預存程序,但是可以呼叫擴充預存程序。

  • 使用者定義函數無法利用動態 SQL 或暫存資料表。 允許使用資料表變數。

  • 使用者定義函數中不允許使用 SET 陳述式。

  • 不允許使用 FOR XML 子句

  • 使用者定義函數可以具有巢狀結構;也就是說,某個使用者定義函數可以呼叫另一個使用者定義函數。 被呼叫的函數開始執行時,巢狀層級會遞增;被呼叫的函數完成執行時,巢狀層級會遞減。 使用者定義函數所建立的巢狀結構最多可以有 32 個層級。 超過巢狀層級上限會導致整個呼叫函數鏈結失敗。 依照 32 個層級巢狀限制,Transact-SQL 使用者定義函數之 Managed 程式碼的任何參考都算是一個層級。 從 Managed 程式碼內叫用的方法,不列入這項限制。

  • 下列 Service Broker 陳述式不能併入 Transact-SQL 使用者定義函數的定義中:

    • BEGIN DIALOG CONVERSATION

    • END CONVERSATION

    • GET CONVERSATION GROUP

    • MOVE CONVERSATION

    • RECEIVE

    • SEND

安全性

權限

需要資料庫中的 CREATE FUNCTION 權限,以及此函數建立所在之結構描述上的 ALTER 權限。 如果此函數指定使用者定義型別,則需要該型別的 EXECUTE 權限。

純量函數

下列範例會在 AdventureWorks2012 資料庫中建立多重語句純量函式。 這個函數使用了一個輸入值 ProductID,並傳回單一資料值,也就是指定產品的彙總存貨量。

IF OBJECT_ID (N'dbo.ufnGetInventoryStock', N'FN') IS NOT NULL  
    DROP FUNCTION ufnGetInventoryStock;  
GO  
CREATE FUNCTION dbo.ufnGetInventoryStock(@ProductID int)  
RETURNS int   
AS   
-- Returns the stock level for the product.  
BEGIN  
    DECLARE @ret int;  
    SELECT @ret = SUM(p.Quantity)   
    FROM Production.ProductInventory p   
    WHERE p.ProductID = @ProductID   
        AND p.LocationID = '6';  
     IF (@ret IS NULL)   
        SET @ret = 0;  
    RETURN @ret;  
END;  
GO  
  

以下範例會使用 ufnGetInventoryStock 函數來傳回 ProductModelID 介於 75 和 80 之間的產品目前的存貨量。

SELECT ProductModelID, Name, dbo.ufnGetInventoryStock(ProductID)AS CurrentSupply  
FROM Production.Product  
WHERE ProductModelID BETWEEN 75 and 80;  
  

資料表值函式

下列範例會在 AdventureWorks2012 資料庫中建立內嵌資料表值函式。 這個函數使用了一個輸入參數,也就是客戶 (商店) 識別碼,並傳回 ProductIDName資料行,以及從年初至今將每項產品銷售給商店的彙總銷售額 YTD Total

IF OBJECT_ID (N'Sales.ufn_SalesByStore', N'IF') IS NOT NULL  
    DROP FUNCTION Sales.ufn_SalesByStore;  
GO  
CREATE FUNCTION Sales.ufn_SalesByStore (@storeid int)  
RETURNS TABLE  
AS  
RETURN   
(  
    SELECT P.ProductID, P.Name, SUM(SD.LineTotal) AS '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  
    JOIN Sales.Customer AS C ON SH.CustomerID = C.CustomerID  
    WHERE C.StoreID = @storeid  
    GROUP BY P.ProductID, P.Name  
);  
  

下例會叫用這個函數並指定客戶識別碼 602。

SELECT * FROM Sales.ufn_SalesByStore (602);  
  

下列範例會在 AdventureWorks2012 資料庫中建立資料表值函式。 這個函數使用單一輸入參數 EmployeeID ,並傳回一份清單,列出這位指定員工的所有直接或間接下屬。 然後叫用此函數並指定員工識別碼 109。

IF OBJECT_ID (N'dbo.ufn_FindReports', N'TF') IS NOT NULL  
    DROP FUNCTION dbo.ufn_FindReports;  
GO  
CREATE FUNCTION dbo.ufn_FindReports (@InEmpID INTEGER)  
RETURNS @retFindReports TABLE   
(  
    EmployeeID int primary key NOT NULL,  
    FirstName nvarchar(255) NOT NULL,  
    LastName nvarchar(255) NOT NULL,  
    JobTitle nvarchar(50) NOT NULL,  
    RecursionLevel int NOT NULL  
)  
--Returns a result set that lists all the employees who report to the   
--specific employee directly or indirectly.*/  
AS  
BEGIN  
WITH EMP_cte(EmployeeID, OrganizationNode, FirstName, LastName, JobTitle, RecursionLevel) -- CTE name and columns  
    AS (  
        SELECT e.BusinessEntityID, e.OrganizationNode, p.FirstName, p.LastName, e.JobTitle, 0 -- Get the initial list of Employees for Manager n  
        FROM HumanResources.Employee e   
INNER JOIN Person.Person p   
ON p.BusinessEntityID = e.BusinessEntityID  
        WHERE e.BusinessEntityID = @InEmpID  
        UNION ALL  
        SELECT e.BusinessEntityID, e.OrganizationNode, p.FirstName, p.LastName, e.JobTitle, RecursionLevel + 1 -- Join recursive member to anchor  
        FROM HumanResources.Employee e   
            INNER JOIN EMP_cte  
            ON e.OrganizationNode.GetAncestor(1) = EMP_cte.OrganizationNode  
INNER JOIN Person.Person p   
ON p.BusinessEntityID = e.BusinessEntityID  
        )  
-- copy the required columns to the result of the function   
   INSERT @retFindReports  
   SELECT EmployeeID, FirstName, LastName, JobTitle, RecursionLevel  
   FROM EMP_cte   
   RETURN  
END;  
GO  
-- Example invocation  
SELECT EmployeeID, FirstName, LastName, JobTitle, RecursionLevel  
FROM dbo.ufn_FindReports(1);  
  

另請參閱

使用者定義的函式
CREATE FUNCTION (Transact-SQL)