2013年5月21日 星期二

User Defined Functions

Functions are programmable objects that are used to perform calculations that can be returned to a calling application or integrated into a result set. Functions can access data and return results, but they cannot make any modifications.

You can create your own functions, referred to as user-defined functions, and store the functions in any database for which you have CREATE FUNCTION authority.

簡介使用者自訂函式(User-Defined Functions)

要建立自訂函式,首先要具有 CREATE FUNCTION 的權限。 自訂函式是用來執行運算用的,不允許變更資料庫的狀態。下表是在自訂函式中不允許操作的項目:

  • Perform an action that changes the state of an instance or database
  • Modify data in a table
  • Call a function that has an external effect, such as the RAND function
  • Create or access temporary tables
  • Execute code dynamically

自訂函式可以回傳一個純量值或是一個資料表,而資料表又分成嵌入資料表多重陳述式資料表

  • 純量值函式(scalar function )
  • 嵌入資料表值函式(inline table-valued function)
  • 多重陳述式資料表值函式(multi-statement table-valued function)

當新增自訂函式時,可以在選單中看到以下選擇:

純量值函式(scalar function )

使用純量值函式注意事項:

  • RETURN 子句後接一個 single value
  • 要用 BEGIN/END 包住程式碼
  • 回傳一個純量值,使用時,把函式當常數值用即可。

定義

CREATE FUNCTION [ schema_name. ] function_name 
( 
	[ 
        { @parameter_name [ AS ][ type_schema_name. ] parameter_data_type [ = default ] [ READONLY ] } 
        [ ,...n ] 
    ]
)
RETURNS return_data_type
    [ WITH <function_option> [ ,...n ] ]
    [ AS ]
    BEGIN
	    <function_body>
	    RETURN scalar_expression
    END
[ ; ]

範例

CREATE FUNCTION fu_GetQuater ( @date as datetime )
RETURNS nvarchar(3)
BEGIN
    DECLARE @return nvarchar(3);
    SELECT @return = '第' + cast(DatePart(QQ,@date) as nvarchar(1)) + '季' 
    RETURN @return
END

SELECT dbo.fu_GetQuater(OrderDate),* FROM ORDERS
SELECT dbo.fu_GetQuater(GETDATE())

嵌入資料表值函式(inline table-valued function)

使用嵌入資料表值函式注意事項:

  • 使用 SELECT 陳述式回傳查詢的結果集。
  • 無法使用 DECLARE 宣告變數。
  • RETURN 子句後接一個 SELECT statement
  • 回傳一個結果集,可以自成一個檢視(VIEW),使用時把函式當成 view 或 table 用。

定義

CREATE FUNCTION [ schema_name. ] function_name 
( 
	[ 
        { @parameter_name [ AS ][ type_schema_name. ] parameter_data_type [ = default ] [ READONLY ] } 
        [ ,...n ] 
    ]
)
RETURNS TABLE
    [ WITH <function_option> [ ,...n ] ]
    [ AS ]
    RETURN 
    [ ( ] 
        select_stmt 
    [ ) ]
[ ; ]

範例

CREATE FUNCTION fu_GetEmps_ILTV( @DepNo as int )
RETURNS TABLE
RETURN 
(
	SELECT EmpNo, EmpName, DepNo FROM Emp WHERE DepNo=@DepNo
)

SELECT * FROM fu_GetEmps_ILTV(1)

SELECT D.DepName, E.EmpName 
FROM fu_GetEmps_ILTV(1) E
INNER JOIN Dep D ON E.DepNo=D.DepNo

多重陳述式資料表值函式(multi-statement table-valued function)

使用多重陳述式資料表值函式注意事項

  • 定義一個 TABLE 型別的變數,將所有陳述式的結果都放進這個變數,最後回傳。
  • RETURN 子句放在函式結束的地方,後面不接東西。
  • 要用 BEGIN/END 包住程式碼

定義

CREATE FUNCTION [ schema_name. ] function_name 
( 
	[ 
        { @parameter_name [ AS ][ type_schema_name. ] parameter_data_type [ = default ] [ READONLY ] } 
        [ ,...n ] 
    ]
)
RETURNS @return_variable TABLE <table_type_definition>
    [ WITH <function_option> [ ,...n ] ]
    [ AS ]
    BEGIN 
        function_body 
        RETURN
    END
[ ; ]

範例

CREATE FUNCTION fu_GetEmps_MSTV( @DepNo as int )
RETURNS @mytable TABLE(
	[EmpNo] [int],
	[EmpName] [nvarchar](50),
	[DepNo] [int]
)
As
BEGIN
	INSERT	@MYTable
	SELECT EmpNo, EmpName, DepNo FROM Emp WHERE DepNo=@DepNo
	
	RETURN
END

SELECT * FROM fu_GetEmps_MSTV(1)

SELECT D.DepName, E.EmpName 
FROM fu_GetEmps_MSTV(1) E
INNER JOIN Dep D ON E.DepNo=D.DepNo

自訂函式的叫用方法

自訂函式除了可以使用 SELECT 陳述式叫用外,下列狀況也都可以使用:

  • A SELECT list
  • A WHERE clause
  • An expression
  • A CHECK or DEFAULT constraint
  • A FROM clause with the CROSS/OUTER APPLY function
--A SELECT list
SELECT dbo.fu_GetQuater(OrderDate), COUNT(1)
FROM ORDERS
WHERE YEAR(OrderDate)=1997
GROUP BY dbo.fu_GetQuater(OrderDate)

--A WHERE clause
SELECT * FROM ORDERS WHERE dbo.fu_GetQuater(OrderDate)=N'第1季'

--CROSS/OUTER APPLY function
SELECT D.DepName, E.EmpName 
FROM fu_GetEmps(1) E
INNER JOIN Dep D ON E.DepNo=D.DepNo

總結

彙整一下三種函式的使用方法,下圖為範例資料:

Create Function fu_GetGrade(@StudentNo int,@ClassID int)
Returns int
Begin

	DECLARE @Grade int; 
	
	SELECT @Grade = Grade FROM tblGrade 
	WHERE StudentNo=@StudentNo AND ClassID=@ClassID
	
	Return @Grade;
End
Create Function fu_GetGradeTable(@ClassID int)
Returns TABLE
As
Return
(
	SELECT * FROM tblGrade 
	WHERE ClassID=@ClassID
)
Create Function fu_GetStudentGrade(@StudentNo int)
Returns @r 
TABLE(
	StudentNo int,
	ClassID int,
	Grade int
)
AS
BEGIN
	INSERT @r
	SELECT StudentNo,ClassID,Grade FROM tblGrade 
	WHERE StudentNo=@StudentNo
	
	RETURN
END

叫用方法

--scalar 函式			
SELECT dbo.fu_tblGrade(1,1)

--inline 函式	
SELECT * from dbo.fu_GetGradeTable(1)

--multi-statement 函式		
SELECT * from dbo.fu_GetStudentGrade(1)

Using APPLY

簡單講 APPLY 就是用來 JOIN 一般資料表和 table-valued function 回傳的資料表。

使用 INNER JOIN

既然 table-valued function 回傳的是一個資料表,那麼你可以這麼使用

SELECT S.StudentName, G.ClassID, G.Grade
FROM tblStudent S
INNER JOIN dbo.fu_GetStudentGrade(1) G ON S.StudentNO=G.StudentNo

使用 CROSS APPLY

不過 SQL 提供 APPLY 運算子,可以讓這件事做起來更方便。

SELECT S.StudentName, G.ClassID, G.Grade
FROM tblStudent S
CROSS APPLY fu_GetStudentGrade(S.StudentNO) G
WHERE S.StudentNo=1

沒有留言:

張貼留言