{"id":2422,"date":"2019-05-07T16:00:50","date_gmt":"2019-05-07T14:00:50","guid":{"rendered":"https:\/\/www.sqlinthewild.co.za\/?p=2422"},"modified":"2019-05-06T18:27:31","modified_gmt":"2019-05-06T16:27:31","slug":"in-line-scalar-functions-in-sql-server-2019","status":"publish","type":"post","link":"https:\/\/www.sqlinthewild.co.za\/index.php\/2019\/05\/07\/in-line-scalar-functions-in-sql-server-2019\/","title":{"rendered":"In-line scalar functions in SQL Server 2019"},"content":{"rendered":"\n<p>Yes, yes, yes, finally!<\/p>\n\n\n\n<p>It\u2019s hardly a secret that I\u2019m not a fan of scalar user-defined functions. I  refer to them as \u2018developer pit-traps\u2019 due to the amount of times I\u2019ve seen  developers absolutely wreck their database performance by over-using them (or using them at all).<\/p>\n\n\n\n<p>The main problem with them is that they haven\u2019t been in-line, meaning the \nfunction gets evaluated on every single row, and the overhead from doing so is \nusually terrible. <\/p>\n\n\n\n<p>One of the improvements in SQL Server 2019 is that scalar user-defined  functions now are in-line. Not all of them, there are conditions that have to be met. Most scalar UDFs that I\u2019ve seem in client systems will meet them, the not  referencing table variables will probably be the main limiting factor.<\/p>\n\n\n\n<p>The full requirements are laid out in the documentation: <a href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/relational-databases\/user-defined-functions\/scalar-udf-inlining\">https:\/\/docs.microsoft.com\/en-us\/sql\/relational-databases\/user-defined-functions\/scalar-udf-inlining<\/a><\/p>\n\n\n\n<p>I\u2019m going to use the same function that I used when I evaluated natively-compiled functions (<a href=\"https:\/\/www.sqlinthewild.co.za\/index.php\/2016\/01\/12\/natively-compiled-user-defined-functions\/\">https:\/\/www.sqlinthewild.co.za\/index.php\/2016\/01\/12\/natively-compiled-user-defined-functions\/<\/a>),  and run it against a table with 860k rows in it, both in compat mode 140 (SQL  Server 2017) and compat mode 150 (SQL Server 2019)<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nCREATE FUNCTION dbo.DateOnly (@Input DATETIME)\n  RETURNS DATETIME\nAS\nBEGIN\n  RETURN DATEADD(dd, DATEDIFF (dd, 0, @Input), 0);\nEND\nGO\n<\/pre><\/div>\n\n\n<p>As in the earlier post, I\u2019ll use extended events to catch the performance \ncharacteristics. <\/p>\n\n\n\n<p>First, something to compare against. The query, without functions, is:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nSELECT DATEADD(dd, DATEDIFF (dd, 0, TransactionDate), 0) FROM Transactions\n<\/pre><\/div>\n\n\n<p>This takes, on average,&nbsp;&nbsp;343ms to run, and 320ms of CPU time.<\/p>\n\n\n\n<p>The results of the first test are impressive.<\/p>\n\n\n\n<table class=\"wp-block-table\"><tbody><tr><td>Compat Mode<\/td><td>Duration (ms)<\/td><td>CPU (ms)<\/td><\/tr><tr><td>140<\/td><td>10 666<\/td><td>8594<\/td><\/tr><tr><td>150<\/td><td>356<\/td><td>353<\/td><\/tr><\/tbody><\/table>\n\n\n\n<p>I keep having people ask about SCHEMABINDING, so same test again, with the \nfunction recreated WITH SCHEMABINDING<\/p>\n\n\n\n<table class=\"wp-block-table\"><tbody><tr><td>Compat Mode<\/td><td>Duration (ms)<\/td><td>CPU (ms)<\/td><\/tr><tr><td>140<\/td><td>5448<\/td><td>3818<br><\/td><\/tr><tr><td>150<\/td><td>325<\/td><td>320<\/td><\/tr><\/tbody><\/table>\n\n\n\n<p>Better, but still over an order of magnitude slower than the query without \nthe function in SQL 2017 and earlier.<\/p>\n\n\n\n<p>Last test, what about something with data access? I\u2019ll switch to my Shipments and ShipmentDetails tables for this. The base query without the function is:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nSELECT s.ShipmentID, \n    (SELECT SUM(Mass) AS TotalMass FROM ShipmentDetails sd WHERE sd.ShipmentID = s.ShipmentID) TotalShipmentMass\nFROM Shipments s;\n<\/pre><\/div>\n\n\n<p>I\u2019m writing it with a subquery instead of a join to keep it as similar as \npossible to the version with the function. It should be the same as if I had \nused a join though. That query takes, on average, 200ms, with 145ms CPU time. \n<\/p>\n\n\n\n<p>There are 26240 rows in the Shipments table, and on average 34 detail rows per shipment. The function is:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nCREATE FUNCTION dbo.ShipmentMass(@ShipmentID INT)\nRETURNS NUMERIC(10,2)\nAS\nBEGIN\n    DECLARE @ShipmentMass NUMERIC(10,2);\n    SELECT @ShipmentMass = SUM(Mass) FROM ShipmentDetails sd WHERE sd.ShipmentID = @ShipmentID;\n\n    RETURN @ShipmentMass;\n\nEND\n<\/pre><\/div>\n\n\n<p>And the results are:<\/p>\n\n\n\n<table class=\"wp-block-table\"><tbody><tr><td>Compat Mode<\/td><td>Duration (ms)<\/td><td>CPU (ms)<\/td><\/tr><tr><td>140<\/td><td>961 211 (16 minutes)<\/td><td>959 547<\/td><\/tr><tr><td>150<\/td><td>3280<\/td><td>3272<\/td><\/tr><\/tbody><\/table>\n\n\n\n<p>The test under compat mode 140 had to be run overnight. 9 hours to run the query 25 times\u2026 And people wonder why I complain about scalar user-defined functions  in systems.<\/p>\n\n\n\n<p>Under compat mode 150 with the inline function it\u2019s way better (3 seconds vs \n16 minutes for a single execution), but it\u2019s still over an order of magnitude \nslower than the same query with the subquery. I\u2019ll test this again after RTM, \nbut for the moment it look like my guidance for functions for SQL 2019 going \nforward is going to be that scalar functions that don\u2019t access data are fine, \nbut scalar functions that do should still be replaced by inline table-valued \nfunctions or no function at all, wherever possible.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Yes, yes, yes, finally! It\u2019s hardly a secret that I\u2019m not a fan of scalar user-defined functions. I refer to them as \u2018developer pit-traps\u2019 due to the amount of times I\u2019ve seen developers absolutely wreck their database performance by over-using&#8230; <a class=\"read-more-button\" href=\"https:\/\/www.sqlinthewild.co.za\/index.php\/2019\/05\/07\/in-line-scalar-functions-in-sql-server-2019\/\">(Read more)<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[25,15,17],"tags":[],"class_list":["post-2422","post","type-post","status-publish","format-standard","hentry","category-performance","category-sql-server","category-t-sql"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p7h6n-D4","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.sqlinthewild.co.za\/index.php\/wp-json\/wp\/v2\/posts\/2422","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.sqlinthewild.co.za\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sqlinthewild.co.za\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sqlinthewild.co.za\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sqlinthewild.co.za\/index.php\/wp-json\/wp\/v2\/comments?post=2422"}],"version-history":[{"count":2,"href":"https:\/\/www.sqlinthewild.co.za\/index.php\/wp-json\/wp\/v2\/posts\/2422\/revisions"}],"predecessor-version":[{"id":2424,"href":"https:\/\/www.sqlinthewild.co.za\/index.php\/wp-json\/wp\/v2\/posts\/2422\/revisions\/2424"}],"wp:attachment":[{"href":"https:\/\/www.sqlinthewild.co.za\/index.php\/wp-json\/wp\/v2\/media?parent=2422"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sqlinthewild.co.za\/index.php\/wp-json\/wp\/v2\/categories?post=2422"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sqlinthewild.co.za\/index.php\/wp-json\/wp\/v2\/tags?post=2422"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}