DCSIMG
עמוד הבית| חבילות השירות שלנו| חומר חופשי| צור קשר
מתי לא להשתמש - User Defined Functions - UDF - בלוג היועצים של מיקרוסופט ישראל

בלוג היועצים של מיקרוסופט ישראל

מתי לא להשתמש - User Defined Functions - UDF

 

לכל מי שבא מעולם התכנות (כמוני למשל), שימוש בפונקציות נשמע כצורת הכתיבה הטובה ביותר. יש לכך כמובן הרבה סיבות כגון שימוש חוזר, Encapsulation, פשטות קוד ועוד.

אבל, בכתיבה של SQL  זה לא תמיד כדאי.

אז הנה דוגמא מהחיים (הדוגמאות רצות על AdventureWorks):

אנו רוצים לכל ספק את סכום ההזמנות, כמות הפריטים, סכום ממוצע לפריט ומשקל ממוצע לפריט

השיטה בעזרת פונקציות:

לעבור על טבלת הספקים, ההזמנות ושורות ההזמנה

קוד בעזרת פונקציות (הגדרות הפונקציות בנספח):

SELECT PV.VendorID, Purchasing.fn_SumQty(PV.VendorID) SumQty, Purchasing.fn_SumValue(PV.VendorID) SumValue ,

      Purchasing.fn_SumFreight(PV.VendorID) SumFreight,

      Purchasing.fn_SumValue(PV.VendorID) / Purchasing.fn_SumQty(PV.VendorID) AvgQty,

      Purchasing.fn_SumFreight(PV.VendorID) / Purchasing.fn_SumQty(PV.VendorID) AvgFreight

FROM Purchasing.ProductVendor PV

הביצועים - ברצפה (11 שניות, 200K Reads)

 

נעבור לשיטה חלופית (הסבר יבוא):

SELECT PV.VendorID, SumQty, SumValue, SumFreight, SumValue/SumQty AvgQty, SumFreight/SumQty AvgFreight

FROM Purchasing.ProductVendor PV

LEFT OUTER JOIN

(select VendorID,sum(Freight) SumFreight, sum(LineTotal) SumValue, sum(cast(OrderQty as int)) SumQty

from Purchasing.PurchaseOrderHeader H

inner join Purchasing.PurchaseOrderDetail D

on H.PurchaseOrderID = D.PurchaseOrderID

GROUP BY VendorID) HelpSumOrders

on PV.VendorID = HelpSumOrders.VendorID

הביצועים - מצויינים (0.2 שניות, Reads 112) - פי 55 יותר מהר, פחות מאלפית פעולות IO

 

אז מה הקסם:

  • פעולת ה JOIN הישירה מאפשרת לאופטימייזר לקרוא את הטבלאת פעם אחת ולא פעם לכל שורה
  • בנוסף, במימוש המקורי יש 5 קריאות לפונקציה לכל שורה!!

מסקנה:

אל תשמשו בפונקציות העושות פניות נוספות ל Database אלא אם מספר השורות נמוך!!

 

נספח פונקציות:

CREATE FUNCTION Purchasing.fn_SumFreight (@VendorID int) RETURNS money

AS

BEGIN

DECLARE @SumFreight MONEY

select @SumFreight= sum(Freight) from Purchasing.PurchaseOrderHeader

WHERE VendorID = @VendorID

RETURN (@SumFreight)

END

go

CREATE FUNCTION Purchasing.fn_SumValue (@VendorID int) RETURNS money

AS

BEGIN

DECLARE @SumLineTotal MONEY

select @SumLineTotal= sum(LineTotal) from Purchasing.PurchaseOrderDetail D

INNER JOIN Purchasing.PurchaseOrderHeader H ON H.PurchaseOrderID = D.PurchaseOrderID

WHERE VendorID = @VendorID

RETURN (@SumLineTotal)

END

GO

ALTER FUNCTION Purchasing.fn_SumQty (@VendorID int) RETURNS INT

AS

BEGIN

DECLARE @SumQty INT

select @SumQty= sum(cast(OrderQty as int)) from Purchasing.PurchaseOrderDetail D

INNER JOIN Purchasing.PurchaseOrderHeader H ON H.PurchaseOrderID = D.PurchaseOrderID

WHERE VendorID = @VendorID

RETURN (@SumQty)

END

 

תוכן התגובה

Tal Ben-Shalom כתב/ה:

אהבתי.

יש לך המלצות היכן עדיף להשתמש בפונקציות?

הרי אם פונקציות מבצעות תמיד "סריקה" על כל הרשומות, (כמעט) תמיד אפשר יהיה לייצר משפט SQL יעיל יותר, או לפחות שווה ערך מבחינת ביצועים.

האם המקום היחיד שכדאי להשצמש בפונקציות זה כאשר הפונקציה מבצעת מניפולצייה על נתוני הרשומה (ללא משפטי SQL נוספים)?

# May 23, 2010 9:31 PM

mikypuff כתב/ה:

אין הרבה פוסטים שאני קורא ואומר I couldn't agree more

אם הלוגיקה שאתה רוצה לממש יכולה להיבנות בעזרת SQL קלאסי, תמיד כדאי להשתמש בו ולנצל את יכולות ה-DB עד תום.

לשאלתו של טל, UDF נכנסות כאשר אתה רוצה לממש לוגיקה ואי אפשר לעשות את זה ב-SQL רגיל. הדוגמאות נדירות אבל קיימות.

# May 23, 2010 11:12 PM

Assaf Fraenkel כתב/ה:

כמה תשובות:

לא כתבתי שפונקציות מבצעות סריקה על כל הרשומות, אלא שלכל שורה מתבצעת קריאה (אחת או יותר) ל UDF. לכן, במקרים שבהם מספר השורות קטן בתוצאה, זה קביל וקריא.

inline functions הם בסדר (לא כאן המקום להרחיב מה הן) , שכן האופטימייזר "פותח" אותם

לפעמים אשתמש ב UDF כקופסא שחורה - אבל זה נושא לפוסט נוסף :)

אסף

# May 24, 2010 12:01 AM

Yossi Elkayam כתב/ה:

הי אסף , יופי של פוסט , האם זה גם מתייחס לפונקציות הנכתבות ב .NET עבור SQL ? והאם העבודה מול משתנה מסוג OUTPUT יכולה לסייע כאן?

תודה.

# May 28, 2010 11:32 AM

Assaf Fraenkel כתב/ה:

תודה יוסי (ובהזמנות זו גם לטל ומיקי)

גם פונקציות CLR יתנהגו דומה אם יש בהם קריאת מידע חיצוני.

לגבי output - בד"כ משתמשים בזה ב inline functions, אשר הינן לרוב בסדר גם ללא שימוש ב output. אבל, נכוןם ששימוש מושכל ב Output יכול לחסוך הרבה IO

# May 30, 2010 11:22 PM
שלח תגובה

(שדה חובה)  

(שדה חובה)  

(אופציונלי)

(שדה חובה) 

Please add 8 and 7 and type the answer here:


Enter the numbers above: