APPLY relational operator
Ένας από τους κυριότερους λόγους που πολλοί developers καταφεύγουν στους cursors όταν γράφουν T-SQL κώδικα είναι η αδυναμία να σκεφτούν τη λύση του προβλήματος με set-oriented τρόπο. Μια κλασική περίπτωση που συμβαίνει αυτό είναι το «θέλω για κάθε εγγραφή από τον πίνακα Χ να συμβαίνει κάτι στο πίνακα Υ».
Ο APPLY operator είναι ένας νέος relational operator που έρχεται να βοηθήσει όταν αντιμετωπίζουμε τέτοια προβλήματα, ώστε να μην χρειαστεί να καταφύγουμε σε cursors. Χρησιμοποιείται στο FROM clause και μας επιτρέπει να εφαρμόσουμε ένα table expression για κάθε εγγραφή του εξωτερικού πίνακα, όπου table expression μπορεί να είναι ένα view, ένας πίνακας ή ένα table function.
Ας δούμε ένα παράδειγμα:
Θέλουμε ένα report όπου για κάθε κατηγορία προϊόντων θα εμφανίζονται τα 3 πιο ακριβά προϊόντα.
Αρχικά, ορίζουμε το table function το οποίο έχει ως παράμετρο το CategoryID και βάσει αυτού φέρνει με TOP(3) τα τρία ακριβότερα προϊόντα.
CREATE FUNCTION MostExpensiveProducts(@CatID int)
RETURNS TABLE AS
RETURN
SELECT TOP (3) ProductID, ProductName, UnitPrice
FROM dbo.Products
WHERE CategoryID = @CatID
ORDER BY UnitPrice DESC
Κατόπιν είμαστε έτοιμοι να γράψουμε το query μας:
SELECT CategoryName, MEP.ProductName, MEP.UnitPrice
FROM Categories
CROSS APPLY MostExpensiveProducts(CategoryID) AS MEP
Και το output για μερικούς-μερικούς που δεν έχουν εγκαταστήσει SQL Server 2005 ακόμη!
CategoryName ProductName UnitPrice
--------------- ---------------------------------------- ---------------------
Beverages Côte de Blaye 263.50
Beverages Ipoh Coffee 46.00
Beverages Chang 19.00
Condiments Vegie-spread 43.90
Condiments Northwoods Cranberry Sauce 40.00
Condiments Sirop d'érable 28.50
Confections Sir Rodney's Marmalade 81.00
Confections Tarte au sucre 49.30
Confections Schoggi Schokolade 43.90
Dairy Products Raclette Courdavault 55.00
Dairy Products Queso Manchego La Pastora 38.00
Dairy Products Gudbrandsdalsost 36.00
Grains/Cereals Gnocchi di nonna Alice 38.00
Grains/Cereals Wimmers gute Semmelknödel 33.25
Grains/Cereals Gustaf's Knäckebröd 21.00
Meat/Poultry Thüringer Rostbratwurst 123.79
Meat/Poultry Mishi Kobe Niku 97.00
Meat/Poultry Alice Mutton 39.00
Produce Manjimup Dried Apples 53.00
Produce Rössle Sauerkraut 45.60
Produce Uncle Bob's Organic Dried Pears 30.00
Seafood Carnarvon Tigers 62.50
Seafood Ikura 31.00
Seafood Gravad lax 26.00
Το σημαντικό εδώ είναι ότι έχουμε τη δυνατότητα να περνάμε τα πεδία από την εγγραφή του αριστερού πίνακα προς τον δεξιό πίνακα, δηλαδή για κάθε εγγραφή του πίνακα Categories περνάμε το CategoryID ως παράμετρο στο table-function. Επίσης, αν υποθέσουμε ότι είχαμε κάποια κατηγορία προϊόντων, χωρίς όμως προϊόντα, θα μπορούσαμε να χρησιμοποιήσουμε το OUTER APPLY ώστε να εμφανιστεί και αυτή η κατηγορία, με nulls όμως στα πεδία ProductName και UnitPrice.