{"id":721,"date":"2004-06-16T15:53:01","date_gmt":"2004-06-16T15:53:01","guid":{"rendered":"\/blogs\/kimberly\/post\/Stored-Procedure-Parameters-giving-you-grief-in-a-multi-purpose-procedure.aspx"},"modified":"2013-01-11T23:14:01","modified_gmt":"2013-01-12T07:14:01","slug":"stored-procedure-parameters-giving-you-grief-in-a-multi-purpose-procedure","status":"publish","type":"post","link":"https:\/\/www.sqlskills.com\/blogs\/kimberly\/stored-procedure-parameters-giving-you-grief-in-a-multi-purpose-procedure\/","title":{"rendered":"Stored Procedure Parameters giving you grief in a multi-purpose procedure?"},"content":{"rendered":"<p><P>Well&#8230; the performance ramifications are&#8230; not good!<\/P><br \/>\n<P>Without seeing more of the proc I&#8217;d have to suggest that you avoid this like the plague! I think I can convince you with this simple example which shows why it&#8217;s better to have multiple procedures and limit the flexibility a bit OR use DSE = dynamic string execution (<EM>and unfortunately even recompilation issues don&#8217;t help &#8211; which I talked about <\/EM><a href=\"https:\/\/www.sqlskills.com\/blogs\/kimberly\/permalink.aspx?guid=147db68e-3b54-4a4b-b092-7e98ddb13250\"><EM>here<\/EM><\/a>)&#8230;<\/P><br \/>\n<P>And &#8211; if we go down the path of DSE I&#8217;m sure that will start a separate thread as far as &#8220;sql injection&#8221; issues\/attacks and security (<EM>the user executing the procedure <STRONG>will<\/STRONG> need permission to directly execute the statement which is in the DSE string<\/EM>). But &#8211; there are a few ways to protect parameters in a stored procedure. In this procedure, the parameters will not allow &#8220;injection&#8221; because of the addition of the <a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/tsqlref\/ts_qua-quz_8sdh.asp?frame=true\">QUOTENAME<\/a> function. This function is INVALUABLE for protecting&nbsp;isolated input values (<EM>which is another trick: isolate parameters if you can<\/EM>). <\/P><br \/>\n<P>In the following &#8220;multi-purpose&#8221; procedure it becomes obvious (after testing) that ONLY DSE solves the performance problem. I could still argue that more than one procedure could be beneficial especially as it wouldn&#8217;t have the access requirements that this one has (remember &#8211; users would have to have permissions to the base table). BUT that&#8217;s a lot more work to maintain\/call, etc. Regardless, one procedure with &#8220;all purpose parameters&#8221; is definitely NOT a good choice (without DSE). (<EM>And fyi &#8211; a future release might help with some of the permissions issues &#8211; that&#8217;s all I&#8217;ll say for now!<\/EM> :) <\/P><br \/>\n<P>OK &#8211; so see the <a href=\"https:\/\/www.sqlskills.com\/blogs\/kimberly\/content\/binary\/procedureparameters.sql\">ProcedureParameters.sql (4.3 KB)<\/a> procedure to play with this within the&nbsp;<A href=\"https:\/\/www.sqlskills.com\/blogs\/kimberly\/ct.ashx?id=147db68e-3b54-4a4b-b092-7e98ddb13250&amp;url=http%3a\/\/3.209.169.194\/blogs\/kimberly\/content\/binary\/CreditSampleDB.zip\" ?><STRONG><FONT color=#696969>CreditSampleDB.zip (55.79 KB)<\/FONT><\/STRONG><\/A>. You will need to modify some data to get rows to return to these queries; use the updates in the .sql script.<\/P><br \/>\n<BLOCKQUOTE dir=ltr style=\"MARGIN-RIGHT: 0px\"><br \/>\n<P>CREATE PROC dbo.GetMemberInfoParam<BR>(<BR>&nbsp;@Lastname&nbsp;varchar(30) = NULL,<BR>&nbsp;@Firstname&nbsp;varchar(30) = NULL,<BR>&nbsp;@member_no&nbsp;int = NULL<BR>)<BR>AS<BR>SET NOCOUNT ON<\/P><br \/>\n<P>SELECT m.* <BR>FROM dbo.member AS m<BR>WHERE (m.lastname LIKE @lastname OR @lastname IS NULL)<BR>&nbsp;AND (m.member_no = @member_no OR @member_no IS NULL)<BR>&nbsp;AND (m.firstname LIKE @firstname OR @firstname IS NULL)<BR>go<\/P><\/BLOCKQUOTE><br \/>\n<P>This next procedure uses dynamic string execution to build the exact and needed string. You could have built this from the client as well&#8230; and that would introduce other sql-injection issues as well. But &#8211; this is a simpe idea and I hope this helps.<\/P><br \/>\n<BLOCKQUOTE dir=ltr style=\"MARGIN-RIGHT: 0px\"><br \/>\n<P>CREATE PROC dbo.GetMemberInfoParamDSE<BR>(<BR>&nbsp;@Lastname&nbsp;varchar(30) = NULL,<BR>&nbsp;@Firstname&nbsp;varchar(30) = NULL,<BR>&nbsp;@member_no&nbsp;int = NULL<BR>)<BR>AS<BR>SET NOCOUNT ON<\/P><br \/>\n<P>IF @LastName IS NULL AND @FirstName IS NULL AND @Member_no IS NULL<BR>&nbsp;RAISERROR (&#8216;You must supply at least one parameter.&#8217;, 16, -1)<\/P><br \/>\n<P>DECLARE @ExecStr&nbsp;varchar(1000)<BR>&nbsp;&nbsp;, @MemberNoStr&nbsp;varchar(100)<\/P><br \/>\n<P>SELECT @ExecStr = &#8216;SELECT m.* FROM dbo.member AS m WHERE &#8216; <\/P><br \/>\n<P>IF @LastName IS NOT NULL<BR>&nbsp;SELECT @Lastname = &#8216;m.lastname LIKE &#8216; + QUOTENAME(@lastname, &#8221;&#8221;)<BR>IF @FirstName IS NOT NULL<BR>&nbsp;SELECT @Firstname = &#8216;m.firstname LIKE &#8216; + QUOTENAME(@firstname, &#8221;&#8221;)<BR>IF @Member_no IS NOT NULL<BR>&nbsp;SELECT @MemberNoStr = &#8216;m.member_no = &#8216; + convert(varchar(5), @member_no)<\/P><br \/>\n<P>SELECT @ExecStr = @ExecStr + ISNULL(@LastName, &#8216; &#8216;) <BR>&nbsp;+ <BR>&nbsp;CASE<BR>&nbsp;&nbsp;WHEN @LastName IS NOT NULL AND @FirstName IS NOT NULL<BR>&nbsp;&nbsp;&nbsp;THEN &#8216; AND &#8216;<BR>&nbsp;&nbsp;ELSE &#8216; &#8216;<BR>&nbsp;END<BR>&nbsp;+<BR>&nbsp;ISNULL(@FirstName, &#8216; &#8216;) <BR>&nbsp;+ <BR>&nbsp;CASE<BR>&nbsp;&nbsp;WHEN (@LastName IS NOT NULL OR @FirstName IS NOT NULL)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;AND @MemberNoStr IS NOT NULL<BR>&nbsp;&nbsp;&nbsp;THEN &#8216; AND &#8216;<BR>&nbsp;&nbsp;ELSE &#8216; &#8216;<BR>&nbsp;END<BR>&nbsp;+<BR>&nbsp;ISNULL(@MemberNoStr, &#8216; &#8216;)<\/P><br \/>\n<P>EXEC(@ExecStr)<BR>go<\/P><\/BLOCKQUOTE><br \/>\n<P dir=ltr>The first procedure generates a plan based on the MOST selective criteria (there&#8217;s more info in the script and you&#8217;ll see this in the showplan\/statistics io output that I describe &#8211; also in the script). The second procedure generates a good plan for each and every execution (as expected). I think this is a good example of what you can do! <\/P><br \/>\n<P>I&#8217;m going to put together one more entry to bring all of these points together as well. Stay tuned!<\/P><br \/>\n<P>Have fun!<\/P><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Well&#8230; the performance ramifications are&#8230; not good! Without seeing more of the proc I&#8217;d have to suggest that you avoid this like the plague! I think I can convince you with this simple example which shows why it&#8217;s better to have multiple procedures and limit the flexibility a bit OR use DSE = dynamic string [&hellip;]<\/p>\n","protected":false},"author":7,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[50,58,78],"tags":[],"class_list":["post-721","post","type-post","status-publish","format-standard","hentry","category-optimizing-procedural-code","category-resources","category-tips"],"_links":{"self":[{"href":"https:\/\/www.sqlskills.com\/blogs\/kimberly\/wp-json\/wp\/v2\/posts\/721","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.sqlskills.com\/blogs\/kimberly\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sqlskills.com\/blogs\/kimberly\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sqlskills.com\/blogs\/kimberly\/wp-json\/wp\/v2\/users\/7"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sqlskills.com\/blogs\/kimberly\/wp-json\/wp\/v2\/comments?post=721"}],"version-history":[{"count":0,"href":"https:\/\/www.sqlskills.com\/blogs\/kimberly\/wp-json\/wp\/v2\/posts\/721\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.sqlskills.com\/blogs\/kimberly\/wp-json\/wp\/v2\/media?parent=721"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sqlskills.com\/blogs\/kimberly\/wp-json\/wp\/v2\/categories?post=721"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sqlskills.com\/blogs\/kimberly\/wp-json\/wp\/v2\/tags?post=721"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}