Search results matching tags 'Best Practices', 'T-SQL', and 'intellisense'http://sqlblog.com/search/SearchResults.aspx?o=DateDescending&tag=Best+Practices,T-SQL,intellisense&orTags=0Search results matching tags 'Best Practices', 'T-SQL', and 'intellisense'en-USCommunityServer 2.1 SP2 (Build: 61129.1)My stored procedure &quot;best practices&quot; checklisthttp://sqlblog.com/blogs/aaron_bertrand/archive/2008/10/30/my-stored-procedure-best-practices-checklist.aspxThu, 30 Oct 2008 21:40:00 GMT21093a07-8b3d-42db-8cbf-3350fcbf5496:9728AaronBertrandWhen developing stored procedures, there seems to be a lot of emphasis on "get it done fast." Which means type all lower case, pay little attention to formatting, and sometimes throw best practices out the window.
Personally, I would rather front-load my development time; I think that the costs I pay in initial development far outweigh what I might have paid in maintenance down the road. Making readable and maintainable code that also
performs well and is delivered in a timely manner is something that a lot of us strive for, but we don't always have the luxury. But I have found that it is very easy to fall into the good kind of development habits.
<p>A popular adage is, "you can have it fast, cheap, or good. Pick two." I contend that if you develop habits like these and use them in all of your database programming, the time difference between following those methods and
doing it the "lazy" way will be negligible at most; and so, fast and good go hand in hand, rather than trade off for one another.
</p>
<p>Once in a while this "disorder" slows me down. I come across code that someone else wrote (almost exclusively it is someone I no longer work with), and I can't even bear to look at it without first re-writing it. Here is a fake
but realistic example of the kinds of procedures I see:
</p>
<blockquote>
<table bgcolor="#eeeeee" cellpadding="0" cellspacing="0">
<tr>
<td>
<pre style="padding:10px 20px;-moz-background-clip:border;-moz-background-origin:padding;font-size:12px;font-family:consolas,lucida console,courier new,courier;-moz-background-inline-policy:continuous;"><font color="blue">create&nbsp;proc&nbsp;</font><font color="black">foo</font><font color="gray">(</font><font color="#434343">@i&nbsp;</font><font color="blue">int</font><font color="gray">,</font><font color="#434343">@bar&nbsp;</font><font color="blue">int=</font><font color="gray">null,</font><font color="#434343">@hr&nbsp;</font><font color="blue">int output</font><font color="black"></font><font color="gray">,</font><font color="#434343">@xd&nbsp;</font><font color="blue">datetime</font><font color="gray">)&nbsp;</font><font color="blue">as<br>declare&nbsp;</font><font color="#434343">@c&nbsp;</font><font color="blue">varchar<br>declare&nbsp;</font><font color="#434343">@s&nbsp;</font><font color="magenta">nchar</font><font color="gray">(</font><font color="black">2</font><font color="gray">)<br></font><font color="blue">declare&nbsp;</font><font color="#434343">@x&nbsp;</font><font color="blue">int<br>set&nbsp;</font><font color="#434343">@grok</font><font color="blue">=</font><font color="red">'Beverly'<br></font><font color="blue">set&nbsp;</font><font color="#434343">@korg</font><font color="blue">=</font><font color="red">'MA'<br></font><font color="blue">set&nbsp;</font><font color="#434343">@x</font><font color="blue">=</font><font color="black">5<br></font><font color="blue">select&nbsp;</font><font color="black">customers.customerid</font><font color="gray">,</font><font color="black">firstname</font><font color="gray">,</font><font color="black">lastname</font><font color="gray">,</font><font color="black">orderdate&nbsp;</font><font color="blue">from&nbsp;</font><font color="black">customers&nbsp;</font><font color="blue">join&nbsp;</font><font color="black">orders&nbsp;</font><font color="blue">on<br></font><font color="black">customers.customerid</font><font color="blue">=</font><font color="black">orders.customerid&nbsp;</font><font color="blue">where&nbsp;</font><font color="black">status</font><font color="blue">=</font><font color="#434343">@i&nbsp;</font><font color="gray">or&nbsp;</font><font color="black">status</font><font color="gray">&lt;=</font><font color="#434343">@bar&nbsp;</font><font color="gray">and&nbsp;</font><font color="black">orderdate</font><font color="gray">&lt;=</font><font color="#434343">@xd<br></font><font color="blue">set&nbsp;</font><font color="#434343">@hr&nbsp;</font><font color="blue">=&nbsp;</font><font color="magenta">@@rowcount <br></font><font color="blue">select&nbsp;</font><font color="black">customers.customerid</font><font color="gray">,</font><font color="magenta">count</font><font color="gray">(*)&nbsp;</font><font color="blue">from&nbsp;</font><font color="black">customers&nbsp;</font><font color="gray">left&nbsp;</font><font color="blue">join&nbsp;</font><font color="black">orders&nbsp;</font><font color="blue">on&nbsp;</font><font color="black"><br>customers.customerid</font><font color="blue">=</font><font color="black">orders.customerid&nbsp;</font><font color="blue">where&nbsp;</font><font color="black">customers.city</font><font color="blue">=</font><font color="#434343">@c&nbsp;</font><font color="gray">and&nbsp;</font><font color="black">customers.state</font><font color="blue">=</font><font color="#434343">@s&nbsp;</font><font color="blue"><br>group&nbsp;by&nbsp;</font><font color="black">customers.customerid&nbsp;</font><font color="blue">having&nbsp;</font><font color="magenta">count</font><font color="gray">(*)&gt;=</font><font color="#434343">@x<br></font><font color="blue">return&nbsp;</font><font color="gray">(</font><font color="magenta">@@rowcount</font><font color="gray">)</font></pre></td>
</tr>
</table>
</blockquote>
<p>This kind of feels like the 5th grade all over again, but when I get handed code like this, I start immediately visualizing one of those "find all of the things wrong with this picture" exercises, and feel compelled to fix them all.
So, what is wrong with the above sample, you may ask? Well, let me go through my own personal (and quite subjective) subconscious checklist of best practices when I write my own stored procedures. I have never tried to
list these all at once, so I may be all over the place, but hopefully I will justify why I choose to have these items on my checklist in the first place.
</p>
<p>======================
</p>
<p><b>Upper casing T-SQL keywords and built-in functions</b>
</p>
<p>I always use CREATE PROCEDURE and not create procedure or Create Procedure. Same goes for all of the code throughout my objects... you will always see SELECT, FROM, WHERE and not select, from, where. I just find if
much more readable when all of the keywords are capitalized. It's not that hard for me to hold down the shift key while typing these words, and there are even IDEs that will do this kind of replacement for you (for example, <a href="http://www.apexsql.com/sql_tools_edit.asp" title="http://www.apexsql.com/sql_tools_edit.asp" target="_blank">Apex
SQLEdit</a> has a handy "mis-spelled keyword replacement" feature that I think could be used for this purpose also). This is probably one of the few areas where Celko and I
actually agree. :-)
</p>
<p>======================
</p>
<p><b>Using a proper and consistent naming scheme</b>
</p>
<p>Obviously "foo" is a horribly ridiculous name for a procedure, but I have come across many that were equally nondescript. I like to name my objects using {target}_{verb}. So for example, if I have a Customers table, I would
have procedures such as:
</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;dbo.Customer_Create
<br>&nbsp;&nbsp;&nbsp;&nbsp;dbo.Customer_Update
<br>&nbsp;&nbsp;&nbsp;&nbsp;dbo.Customer_Delete
<br>&nbsp;&nbsp;&nbsp;&nbsp;dbo.Customer_GetList
<br>&nbsp;&nbsp;&nbsp;&nbsp;dbo.Customer_GetDetails
</p>
<p>This allows them to sort nicely in Object Explorer / Object Explorer Details, and also narrows down my search quickly in an IntelliSense (or <a href="http://www.red-gate.com/products/SQL_Prompt/index.htm" title="http://www.red-gate.com/products/SQL_Prompt/index.htm" target="_blank">SQLPrompt</a>) auto-
complete list. If I have a stored procedures named in the style dbo.GetCustomerList, they get mixed up in the list with dbo.GetClientList and dbo.GetCreditList. You could argue that maybe these should be organized by
schema, but in spite of all the buzz, I have not developed a need or desire to use schemas in this way. For most of the applications I develop, ownership/schema is pretty simple and doesn't need to be made more complex.
</p>
<p>Of course I NEVER name stored procedures using the sp_ prefix. See Brian Moran's <a href="http://www.sqlmag.com/Article/ArticleID/23011/sql_server_23011.html" title="Should I Use the sp_ Prefix for Procedure Names?" target="_blank">article in SQL Server Magazine</a> back in 2001. Or just ask
anybody. :-) I also avoid other identifying object prefixes (like usp_). I don't know that I've ever been in a situation where I couldn't tell that some object was a procedure, or a function, or a table, and where the name really
would have helped me all that much. This is especially true for the silly (but common) "tbl" prefix on tables. I don't want to get into that here, but I've always scratched my head at that one. Views may be the only place
where I think this is justified, but then it should be a v or View_ prefix on the views only; no need to also identify tables... if it doesn't have a v or View_ prefix, it's a table!
</p>
<p>More important than coming up with a proper naming scheme (because that is mostly subjective), it is much more important that you apply your naming scheme consistently. Nobody wants to see procedures named
dbo.Customer_Create, dbo.Update_Customer and dbo.GetCustomerDetails.
</p>
<p>======================
</p>
<p><b>Using the schema prefix</b>
</p>
<p>I always specify the schema prefix when creating stored procedures. This way I know that it will be dbo.procedure_name no matter who I am logged in as when I create it. Similarly, my code always has the schema prefix on
all object references. This prevents the database engine from checking for an object under my schema first, and also avoids the issue where multiple plans are cached for the exact same statement/batch just because they were executed by users with different default schemas.<br></p>
<p>======================
</p>
<p><b>Using parentheses around parameter list</b>
</p>
<p>I am not a big fan of using parentheses around the parameter list. I can't really explain it, as I am a proponent of consistency, and this is the syntax required when creating user-defined functions. But I wanted to mention it
because you will not see any of my stored procedures using this syntax. I'm open to change if you can suggest a good enough reason for me to do so.
</p>
<p>======================
</p>
<p><b>Lining up parameter names, data types, and default values</b>
</p>
<p>I find this much easier to read:
</p>
<blockquote>
<table bgcolor="#eeeeee" cellpadding="0" cellspacing="0">
<tr>
<td>
<pre style="padding:10px 20px;-moz-background-clip:border;-moz-background-origin:padding;font-size:12px;font-family:consolas,lucida console,courier new,courier;-moz-background-inline-policy:continuous;"><font color="blue">CREATE&nbsp;PROCEDURE&nbsp;</font><font color="black">dbo.User_Update<br>&nbsp;&nbsp; </font><font color="#434343">@CustomerID&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="blue">INT</font><font color="gray">,<br>&nbsp;&nbsp; </font><font color="#434343">@FirstName&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="blue">VARCHAR</font><font color="gray">(</font><font color="black">32</font><font color="gray">)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="blue">=&nbsp;</font><font color="gray">NULL,<br>&nbsp;&nbsp; </font><font color="#434343">@LastName&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="blue">VARCHAR</font><font color="gray">(</font><font color="black">32</font><font color="gray">)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="blue">=&nbsp;</font><font color="gray">NULL,<br>&nbsp;&nbsp; </font><font color="#434343">@Password&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="blue">VARCHAR</font><font color="gray">(</font><font color="black">16</font><font color="gray">)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="blue">=&nbsp;</font><font color="gray">NULL,<br>&nbsp;&nbsp; </font><font color="#434343">@EmailAddress&nbsp;&nbsp;&nbsp;</font><font color="blue">VARCHAR</font><font color="gray">(</font><font color="black">320</font><font color="gray">)</font><font color="blue">&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;</font><font color="gray">NULL,<br>&nbsp;&nbsp; </font><font color="#434343">@Active&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="blue">BIT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="blue">=&nbsp;</font><font color="black">1</font><font color="gray">,<br>&nbsp;&nbsp; </font><font color="#434343">@LastLogin&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="blue">SMALLDATETIME&nbsp;&nbsp;&nbsp;</font><font color="blue">=&nbsp;</font><font color="gray">NULL<br></font><font color="blue">AS<br>BEGIN</font><br>...</pre></td>
</tr>
</table>
</blockquote>
<p>...than this:
</p>
<blockquote>
<table bgcolor="#eeeeee" cellpadding="0" cellspacing="0">
<tr>
<td>
<pre style="padding:10px 20px;-moz-background-clip:border;-moz-background-origin:padding;font-size:12px;font-family:consolas,lucida console,courier new,courier;-moz-background-inline-policy:continuous;"><font color="blue">CREATE&nbsp;PROCEDURE&nbsp;</font><font color="black">dbo.User_Update<br></font><font color="#434343">@CustomerID&nbsp;</font><font color="blue">INT</font><font color="gray">,<br></font><font color="#434343">@FirstName&nbsp;</font><font color="blue">VARCHAR</font><font color="gray">(</font><font color="black">32</font><font color="gray">)&nbsp;</font><font color="blue">=&nbsp;</font><font color="gray">NULL,<br></font><font color="#434343">@LastName&nbsp;</font><font color="blue">VARCHAR</font><font color="gray">(</font><font color="black">32</font><font color="gray">)&nbsp;</font><font color="blue">=&nbsp;</font><font color="gray">NULL,<br></font><font color="#434343">@Password&nbsp;</font><font color="blue">VARCHAR</font><font color="gray">(</font><font color="black">16</font><font color="gray">)&nbsp;</font><font color="blue">=&nbsp;</font><font color="gray">NULL,<br></font><font color="#434343">@EmailAddress&nbsp;</font><font color="blue">VARCHAR</font><font color="gray">(</font><font color="black">320</font><font color="gray">)&nbsp;</font><font color="blue">=&nbsp;</font><font color="gray">NULL,<br></font><font color="#434343">@Active&nbsp;</font><font color="blue">BIT&nbsp;</font><font color="blue">=&nbsp;</font><font color="black">1</font><font color="gray">,<br></font><font color="#434343">@LastLogin&nbsp;</font><font color="blue">SMALLDATETIME&nbsp;</font><font color="blue">=&nbsp;</font><font color="gray">NULL<br></font><font color="blue">AS<br>BEGIN<br></font>...<br></pre></td>
</tr>
</table>
</blockquote>
<p>======================
</p>
<p><b>Using spaces and line breaks liberally</b>
</p>
<p>This is a simple one, but in all comparison operators I like to see spaces between column/variable and operator. So instead of @foo int=null or where @foo&gt;1 I would rather see @foo INT = NULL or WHERE @foo &gt; 1.
</p>
<p>I also tend to place at least a carriage return between individual statements, especially in stored procedures where many statements spill over multiple lines.
</p>
<p>Both of these are just about readability, nothing more. While in some interpreted languages like JavaScript, size is king, and compressing / obfuscating code to make it as small as possible does provide some benefit, in T-
SQL you would be hard-pressed to find a case where this comes into play. So, I lean to the side of readability.
</p>
<p>======================</p>
<p><b>Avoiding data type / function prefixes on column / parameter names</b>
</p>
<p>I often see prefixes like @iCustomerID, @prmInputParameter, @varLocalVariable, @strStringVariable. I realize why people do it, I just think it muddies things up. It also makes it much harder to change the data type of a
column when not only do you have to change all the variable/parameter declarations but you also have to change @iVarName to @bigintVarName, etc. Otherwise the purpose of the prefixed variable name loses most of its
benefit. So, just name the variable for what it is. If you have a column EmailAddress VARCHAR(320), then make your variable/parameter declaration @EmailAddress VARCHAR(320). No need to use @strEmailAddress ... if
you need to find out the data type, just go to the declaration line!
</p>
<p>======================
</p>
<p><b>Using lengths on parameters, even when optional</b>
</p>
<p>I occasionally see people define parameters and local variables as char or varchar, without specifying a length. This is very dangerous, as in many situations you will get silent truncation at 30 characters, and in a few obscure
ones, you will get silent truncation at 1 character. This can mean data loss, which is not very good at all. I have asked that this silent truncation at least become consistent throughout the product (see <a href="http://connect.microsoft.com/SQL/feedback/ViewFeedback.aspx?FeedbackID=267605" title="http://connect.microsoft.com/SQL/feedback/ViewFeedback.aspx?FeedbackID=267605" target="_blank">Connect #267605</a>), but nothing has happened yet. Fellow MVP Erland Sommarskog has gone so far as to ask for the length declaration to become
mandatory (see <a href="http://connect.microsoft.com/SQL/feedback/ViewFeedback.aspx?FeedbackID=244395" title="http://connect.microsoft.com/SQL/feedback/ViewFeedback.aspx?FeedbackID=244395" target="_blank">Connect #244395</a>) and, failing that, feels that this should be something that raises a warning when using his
proposed SET STRICT_CHECKS ON setting (see <a href="http://www.sommarskog.se/strict_checks.html#nodefaultlength" title="http://www.sommarskog.se/strict_checks.html#nodefaultlength" target="_blank">http://www.sommarskog.se/strict_checks.html#nodefaultlength</a>).
</p>
<p>======================
</p>
<p><b>Listing output parameters last</b>
</p>
<p>My habit is to list OUTPUT parameters last. I am not sure why that is exactly, except that it is the order that I conceptually think about the parameters... in then out, not the other way around.
</p>
<p>======================
</p>
<p><b>Using BEGIN / END liberally</b>
</p>
<p>I have seen many people write stuff like this:
</p>
<blockquote>
<table bgcolor="#eeeeee" cellpadding="0" cellspacing="0">
<tr>
<td>
<pre style="padding:10px 20px;-moz-background-clip:border;-moz-background-origin:padding;font-size:12px;font-family:consolas,lucida console,courier new,courier;-moz-background-inline-policy:continuous;"><font color="blue">CREATE&nbsp;PROCEDURE&nbsp;</font><font color="black">dbo.ProcedureA<br></font><font color="blue">AS<br>&nbsp;&nbsp;&nbsp;SELECT&nbsp;</font><font color="gray">*&nbsp;</font><font color="blue">FROM&nbsp;</font><font color="black">foo</font><font color="gray">;<br>&nbsp;&nbsp; </font><font><font color="blue">GO<br></font></font><font color="black">&nbsp;&nbsp; </font><font color="blue">SELECT&nbsp;</font><font color="gray">*&nbsp;</font><font color="blue">FROM&nbsp;</font><font color="black">bar</font><font color="gray">;<br></font><font><font color="blue">GO</font></font></pre></td>
</tr>
</table>
</blockquote>
<p>They create the procedure, maybe don't notice the extra resultset from bar (or shrug it off), and then wonder why they only get results from foo when they run the procedure. If they had done this:
</p>
<blockquote>
<table bgcolor="#eeeeee" cellpadding="0" cellspacing="0">
<tr>
<td>
<pre style="padding:10px 20px;-moz-background-clip:border;-moz-background-origin:padding;font-size:12px;font-family:consolas,lucida console,courier new,courier;-moz-background-inline-policy:continuous;"><font color="blue">CREATE&nbsp;PROCEDURE&nbsp;</font><font color="black">dbo.ProcedureA<br></font><font color="blue">AS<br>BEGIN<br>&nbsp;&nbsp;&nbsp;SELECT&nbsp;</font><font color="gray">*&nbsp;</font><font color="blue">FROM&nbsp;</font><font color="black">foo</font><font color="gray">;<br>&nbsp;&nbsp; </font><font><font color="blue">GO<br></font></font><font color="black">&nbsp;&nbsp; </font><font color="blue">SELECT&nbsp;</font><font color="gray">*&nbsp;</font><font color="blue">FROM&nbsp;</font><font color="black">bar</font><font color="gray">;<br></font><font color="blue">END<br></font><font><font color="blue">GO</font></font><br></pre></td>
</tr>
</table>
</blockquote>
<p>Because GO is not a T-SQL keyword but rather a batch separator for tools like Query Analyzer and SSMS, they would have received these error messages, one from each batch: <br></p>
<blockquote>
<table bgcolor="#eeeeee" cellpadding="0" cellspacing="0">
<tr>
<td>
<pre style="padding:10px 20px;-moz-background-clip:border;-moz-background-origin:padding;font-size:12px;font-family:consolas,lucida console,courier new,courier;-moz-background-inline-policy:continuous;">Msg 102, Level 15, State 1, Procedure ProcedureA, Line 4<br>Incorrect syntax near ';'.<br>Msg 102, Level 15, State 1, Line 2<br>Incorrect syntax near 'END'.</pre></td>
</tr>
</table>
</blockquote>
<p>Yes, errors are bad, and all that, but I would rather have this brought to my face when I try to compile the procedure, then later on when the first user tries to call it.
</p>
<p>======================
</p>
<p><b>Using statement terminators</b>
</p>
<p>I have quickly adapted to the habit of ending all statements with proper statement terminators (;). This was always a habit in languages like JavaScript (where it is optional) and C# (where it is not). But as T-SQL gets more
and more extensions (e.g. CTEs) that require it, I see it becoming a requirement eventually. Maybe I won't even be working with SQL Server by the time that happens, but if I am, I'll be ready. It's one extra keystroke and
guarantees that my code will be forward-compatible.
</p>
<p>======================
</p>
<p><b>Using SET NOCOUNT ON</b>
</p>
<p>I always add SET NOCOUNT ON; as the very first line of the procedure (after BEGIN of course). This prevents DONE_IN_PROC messages from needlessly being sent back to the client after every row-affecting statement, which
increases network traffic and in many cases can fool applications into believing there is an additional recordset available for consumption. <br></p>
<ul><span style="font-weight:bold;">NOTE</span><br>I do not advocate blindly throwing SET NOCOUNT ON into all of your existing stored procedures. If you have existing applications they might actually already be working around the "extra recordset" problem, or there may be .NET applications that are using its result. If you code with SET NOCOUNT ON from the start, and keep track of rows affected in output parameters when necessary, this should never be an issue. Roy Ashbrook got beat up about this topic at a Tampa code camp last summer, and <a href="http://drowningintechnicaldebt.com/blogs/royashbrook/archive/2007/07/17/why-quot-set-nocount-on-quot-sucks.aspx" target="_blank">wrote about it here</a>.<br><br>
</ul>
<p>======================
</p>
<p><b>Using local variables</b>
</p>
<p>When possible, I always use a single DECLARE statement to initialize all of my local variables. Similarly, I try to use a single SELECT to apply values to those variables that are being used like local constants. I see code like
this:
</p>
<blockquote>
<table bgcolor="#eeeeee" cellpadding="0" cellspacing="0">
<tr>
<td>
<pre style="padding:10px 20px;-moz-background-clip:border;-moz-background-origin:padding;font-size:12px;font-family:consolas,lucida console,courier new,courier;-moz-background-inline-policy:continuous;"><font color="blue">declare&nbsp;</font><font color="#434343">@foo&nbsp;</font><font color="blue">int<br>declare&nbsp;</font><font color="#434343">@bar&nbsp;</font><font color="blue">int<br>declare&nbsp;</font><font color="#434343">@x&nbsp;</font><font color="blue">int<br>set&nbsp;</font><font color="#434343">@foo&nbsp;</font><font color="blue">=&nbsp;</font><font color="black">5<br></font><font color="blue">set&nbsp;</font><font color="#434343">@bar&nbsp;</font><font color="blue">=&nbsp;</font><font color="black">6<br></font><font color="blue">set&nbsp;</font><font color="#434343">@x&nbsp;</font><font color="blue">=&nbsp;</font><font color="gray">-</font><font color="black">1</font></pre></td>
</tr>
</table>
</blockquote>
<p>And then some more declare and set statements later on in the code. I find it much harder to track down variables in longer and more complex procedures when the declaration and/or assignments can happen anywhere... I
would much rather have as much of this as possible occurring in the beginning of the code. So for the above I would rather see: <br></p>
<blockquote>
<table bgcolor="#eeeeee" cellpadding="0" cellspacing="0">
<tr>
<td>
<pre style="padding:10px 20px;-moz-background-clip:border;-moz-background-origin:padding;font-size:12px;font-family:consolas,lucida console,courier new,courier;-moz-background-inline-policy:continuous;"><font color="blue">DECLARE <br>&nbsp;&nbsp;&nbsp;</font><font color="#434343">@foo&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="blue">INT</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;</font><font color="#434343">@bar&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="blue">INT</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;</font><font color="#434343">@x&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="blue">INT</font><font color="gray">;<br><br></font><font color="blue">SELECT<br>&nbsp;&nbsp;&nbsp;</font><font color="#434343">@foo&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="blue">=&nbsp;</font><font color="black">5</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;</font><font color="#434343">@bar&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="blue">=&nbsp;</font><font color="black">6</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;</font><font color="#434343">@x&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="blue">=&nbsp;</font><font color="gray">-</font><font color="black">1</font><font color="gray">;</font></pre></td>
</tr>
</table>
</blockquote>
<p>As a bonus, in SQL Server 2008, the syntax now supports changing the above into a single statement:
</p>
<blockquote>
<table bgcolor="#eeeeee" cellpadding="0" cellspacing="0">
<tr>
<td>
<pre style="padding:10px 20px;-moz-background-clip:border;-moz-background-origin:padding;font-size:12px;font-family:consolas,lucida console,courier new,courier;-moz-background-inline-policy:continuous;"><font color="blue">DECLARE<br>&nbsp;&nbsp;&nbsp;</font><font color="#434343">@foo&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="blue">INT&nbsp;=&nbsp;</font><font color="black">5</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;</font><font color="#434343">@bar&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="blue">INT&nbsp;=&nbsp;</font><font color="black">6</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;</font><font color="#434343">@x&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="blue">INT&nbsp;=&nbsp;</font><font color="gray">-</font><font color="black">1</font><font color="gray">;</font></pre></td>
</tr>
</table>
</blockquote>
<p>So much nicer. However, it still leaves a lot to be desired: I also always use meaningful variables names, rather than @i, @x, etc.</p>
<p>Also, some people like listing the commas at the beginning of each new line, e.g.: <br></p>
<blockquote>
<table bgcolor="#eeeeee" cellpadding="0" cellspacing="0">
<tr>
<td>
<pre style="padding:10px 20px;-moz-background-clip:border;-moz-background-origin:padding;font-size:12px;font-family:consolas,lucida console,courier new,courier;-moz-background-inline-policy:continuous;"><font color="blue">DECLARE<br>&nbsp;&nbsp;&nbsp;</font><font color="#434343">@foo&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="blue">INT&nbsp;=&nbsp;</font><font color="black">5<br>&nbsp;&nbsp;&nbsp;</font><font color="gray">,</font><font color="#434343">@bar&nbsp;&nbsp;&nbsp;</font><font color="blue">INT&nbsp;=&nbsp;</font><font color="black">6<br>&nbsp;&nbsp;&nbsp;</font><font color="gray">,</font><font color="#434343">@x&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="blue">INT&nbsp;=&nbsp;</font><font color="gray">-</font><font color="black">1</font><font color="gray">;</font></pre></td>
</tr>
</table>
</blockquote>
<p>Not just in variable declarations, but also in parameter lists, columns lists, etc. While I will agree that this makes it easier to comment out individual lines in single steps, I find the readability suffers greatly.
</p>
<p>======================
</p>
<p><b>Using table aliases</b>
</p>
<p>I use aliases a lot. Nobody wants to read (never mind type) this, even though I have seen *many* examples of it posted to the public SQL Server newsgroups:
</p>
<blockquote>
<table bgcolor="#eeeeee" cellpadding="0" cellspacing="0">
<tr>
<td>
<pre style="padding:10px 20px;-moz-background-clip:border;-moz-background-origin:padding;font-size:12px;font-family:consolas,lucida console,courier new,courier;-moz-background-inline-policy:continuous;"><font color="blue">SELECT <br>&nbsp;&nbsp;&nbsp;</font><font color="black">dbo.table_X_with_long_name.column1</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;</font><font color="black">dbo.table_X_with_long_name.column2</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;</font><font color="black">dbo.table_X_with_long_name.column3</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;</font><font color="black">dbo.table_X_with_long_name.column4</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;</font><font color="black">dbo.table_X_with_long_name.column5</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;</font><font color="black">dbo.table_H_with_long_name.column1</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;</font><font color="black">dbo.table_H_with_long_name.column2</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;</font><font color="black">dbo.table_H_with_long_name.column3</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;</font><font color="black">dbo.table_H_with_long_name.column4<br></font><font color="blue">FROM<br>&nbsp;&nbsp;&nbsp;</font><font color="black">dbo.table_X_with_long_name<br></font><font color="blue">INNER&nbsp;JOIN<br>&nbsp;&nbsp;&nbsp;</font><font color="black">dbo.table_H_with_long_name<br></font><font color="blue">ON<br>&nbsp;&nbsp;&nbsp;</font><font color="black">dbo.table_X_with_long_name.column1&nbsp;</font><font color="blue">=&nbsp;</font><font color="black">dbo.table_H_with_long_name.column1<br>&nbsp;&nbsp;&nbsp;</font><font color="gray">OR&nbsp;</font><font color="black">dbo.table_X_with_long_name.column1&nbsp;</font><font color="blue">=&nbsp;</font><font color="black">dbo.table_H_with_long_name.column1<br>&nbsp;&nbsp;&nbsp;</font><font color="gray">OR&nbsp;</font><font color="black">dbo.table_X_with_long_name.column1&nbsp;</font><font color="blue">=&nbsp;</font><font color="black">dbo.table_H_with_long_name.column1<br></font><font color="blue">WHERE<br>&nbsp;&nbsp;&nbsp;</font><font color="black">dbo.table_X_with_long_name.column1&nbsp;</font><font color="gray">&gt;=&nbsp;</font><font color="black">5<br>&nbsp;&nbsp;&nbsp;</font><font color="gray">AND&nbsp;</font><font color="black">dbo.table_X_with_long_name.column1&nbsp;</font><font color="gray">&lt;&nbsp;</font><font color="black">10</font><font color="gray">;</font></pre></td>
</tr>
</table>
</blockquote>
<p>But as long as you alias sensibly, you can make this a much more readable query: <br></p>
<blockquote>
<table bgcolor="#eeeeee" cellpadding="0" cellspacing="0">
<tr>
<td>
<pre style="padding:10px 20px;-moz-background-clip:border;-moz-background-origin:padding;font-size:12px;font-family:consolas,lucida console,courier new,courier;-moz-background-inline-policy:continuous;"><font color="blue">SELECT <br>&nbsp;&nbsp;&nbsp;</font><font color="black">X.column1</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;</font><font color="black">X.column2</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;</font><font color="black">X.column3</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;</font><font color="black">X.column4</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;</font><font color="black">X.column5</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;</font><font color="black">H.column1</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;</font><font color="black">H.column2</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;</font><font color="black">H.column3</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;</font><font color="black">H.column4<br></font><font color="blue">FROM<br>&nbsp;&nbsp;&nbsp;</font><font color="black">dbo.table_X_with_long_name&nbsp;</font><font color="blue">AS&nbsp;</font><font color="black">X<br></font><font color="blue">INNER&nbsp;JOIN<br>&nbsp;&nbsp;&nbsp;</font><font color="black">dbo.table_H_with_long_name&nbsp;</font><font color="blue">AS&nbsp;</font><font color="black">H<br></font><font color="blue">ON<br>&nbsp;&nbsp;&nbsp;</font><font color="black">X.column1&nbsp;</font><font color="blue">=&nbsp;</font><font color="black">H.column1<br>&nbsp;&nbsp;&nbsp;</font><font color="gray">OR&nbsp;</font><font color="black">X.column2&nbsp;</font><font color="blue">=&nbsp;</font><font color="black">H.column2<br>&nbsp;&nbsp;&nbsp;</font><font color="gray">OR&nbsp;</font><font color="black">X.column3&nbsp;</font><font color="blue">=&nbsp;</font><font color="black">H.column3<br></font><font color="blue">WHERE<br>&nbsp;&nbsp;&nbsp;</font><font color="black">X.column1&nbsp;</font><font color="gray">&gt;=&nbsp;</font><font color="black">5<br>&nbsp;&nbsp;&nbsp;</font><font color="gray">AND&nbsp;</font><font color="black">X.column1&nbsp;</font><font color="gray">&lt;&nbsp;</font><font color="black">10</font><font color="gray">;</font></pre></td>
</tr>
</table>
</blockquote>
<p>The "AS" when aliasing tables is optional; I have been trying very hard to make myself use it (only because the standard defines it that way). When writing multi-table queries, I don't give tables meaningless shorthand like a,
b, c or t1, t2, t3. This might fly for simple queries, but if the query becomes more complex, you will regret it when you have to go back and edit it.
</p>
<p>======================
</p>
<p><b>Using column aliases</b>
</p>
<p>I buck against the trend here. A lot of people prefer to alias expressions / columns using this syntax:
</p>
<blockquote>
<table bgcolor="#eeeeee" cellpadding="0" cellspacing="0">
<tr>
<td>
<pre style="padding:10px 20px;-moz-background-clip:border;-moz-background-origin:padding;font-size:12px;font-family:consolas,lucida console,courier new,courier;-moz-background-inline-policy:continuous;"><font color="blue">SELECT&nbsp;</font><font color="black">[column&nbsp;expression]&nbsp;</font><font color="blue">AS&nbsp;</font><font color="black">alias</font></pre></td>
</tr>
</table>
</blockquote>
<p>I much prefer: <br></p>
<blockquote>
<table bgcolor="#eeeeee" cellpadding="0" cellspacing="0">
<tr>
<td>
<pre style="padding:10px 20px;-moz-background-clip:border;-moz-background-origin:padding;font-size:12px;font-family:consolas,lucida console,courier new,courier;-moz-background-inline-policy:continuous;"><font color="blue">SELECT&nbsp;</font><font color="black">alias&nbsp;</font><font color="blue">=&nbsp;</font><font color="black">[column&nbsp;expression]</font></pre></td>
</tr>
</table>
</blockquote>
<p>The reason is that all of my column names are listed down the left hand side of the column list, instead of being at the end. It is much easier to scan column names when they are vertically aligned.
</p>
<p>In addition, I always use column aliases for expressions, even if right now I don't need to reference the column by an alias. This prevents me from having to deal with multiple errors should I ever need to move the query into a
subquery, or cte, or derived table, etc.
</p>
<p>======================
</p>
<p><b>Using consistent formatting</b>
</p>
<p>I am very fussy (some co-workers use a different word) about formatting. I like my queries to be consistently readable and laid out in a predictable way. So for a join that includes a CTE and a subquery, this is how it would
look:
</p>
<blockquote>
<table bgcolor="#eeeeee" cellpadding="0" cellspacing="0">
<tr>
<td>
<pre style="padding:10px 20px;-moz-background-clip:border;-moz-background-origin:padding;font-size:12px;font-family:consolas,lucida console,courier new,courier;-moz-background-inline-policy:continuous;"><font color="blue">WITH&nbsp;</font><font color="black">cte&nbsp;</font><font color="blue">AS <br></font><font color="gray">(<br>&nbsp;&nbsp;&nbsp; </font><font color="blue">SELECT <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><font color="black">t.col1</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><font color="black">t.col2</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><font color="black">t.col3<br>&nbsp;&nbsp; </font><font color="blue">FROM<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><font color="black">dbo.sometable&nbsp;</font><font color="blue">AS&nbsp;</font><font color="black">t<br></font><font color="gray">)<br></font><font color="blue">SELECT<br>&nbsp;&nbsp;&nbsp; </font><font color="black">cte.col1</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp; </font><font color="black">cte.col2</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp; </font><font color="black">cte.col3</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp; </font><font color="black">c.col4<br></font><font color="blue">FROM<br>&nbsp;&nbsp; </font><font color="black"> cte<br></font><font color="blue">INNER&nbsp;JOIN<br>&nbsp;&nbsp;&nbsp; </font><font color="black">dbo.Customers&nbsp;</font><font color="blue">AS&nbsp;</font><font color="black">c<br>&nbsp;&nbsp;&nbsp; </font><font color="blue">ON&nbsp;</font><font color="black">c.CustomerID&nbsp;</font><font color="blue">=&nbsp;</font><font color="black">cte.col1<br></font><font color="blue">WHERE&nbsp;</font><font color="gray">EXISTS<br>(<br>&nbsp;&nbsp;&nbsp; </font><font color="blue">SELECT&nbsp;</font><font color="black">1<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><font color="blue">FROM&nbsp;</font><font color="black">dbo.Orders o<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><font color="blue">WHERE&nbsp;</font><font color="black">o.CustomerID&nbsp;</font><font color="blue">= <font color="black">c</font></font><font color="black">.CustomerID<br></font><font color="gray">)<br>AND&nbsp;</font><font color="black">c.Status&nbsp;</font><font color="blue">=&nbsp;</font><font color="red">'LIVE'</font><font color="gray">;</font></pre></td>
</tr>
</table>
</blockquote>
<p>Keeping all of the columns in a nice vertical line, and visually separating each table in the join and each where clause. Inside a subquery or derived table, I am less strict about the visual separation, though I still put each fundamental portion
on its own line. And I always use SELECT 1 in this type of EXISTS() clause, instead of SELECT * or SELECT COUNT(*), to make it immediately clear to others that the query inside does NOT retrieve data.
</p>
<p>======================
</p>
<p><b>Matching case of underlying objects / columns</b>
</p>
<p>I always try to match the case of the underlying object, as I can never be too certain that my application will always be on a case-sensitive collation. Going back and correcting the case throughout all of my modules will be a
royal pain, at best. This is much easier if you are using SQL Server 2008 Management Studio against a SQL Server 2008 instance, or have invested in Red-Gate's SQL Prompt, as you will automatically get the correct case when selecting from the auto-complete list.
</p>
<p>======================
</p>
<p><b>Qualifying column names with table/alias prefix</b>
</p>
<p>I always qualify column names when there is more than one table in the query. Heck, sometimes I even use aliases when there is only one table in the query, to ease my maintenance later should the query become more
complex. I won't harp on this too much, as fellow MVP Alex Kuznetsov <a href="http://sqlblog.com/blogs/alexander_kuznetsov/archive/2008/10/25/defensive-database-programming-qualifying-column-names.aspx" title="http://sqlblog.com/blogs/alexander_kuznetsov/archive/2008/10/25/defensive-database-programming-qualifying-column-names.aspx" target="_blank">treated this subject</a> a few days ago.<br></p>
<p>======================
</p>
<p><b>Using RETURN and OUTPUT appropriately</b></p>
<p>I never use RETURN to provide any data back to the client (e.g. the SCOPE_IDENTITY() value or @@ROWCOUNT). This should be used exclusively for returning stored procedure status, such as ERROR_NUMBER() / @@ERROR. If you need to return data to the caller, use a resultset or an OUTPUT parameter.</p>
<p>======================
</p>
<p><b>Avoiding keyword shorthands</b>
</p>
<p>I always use full keywords as opposed to their shorthand equivalents. "BEGIN TRAN" and "CREATE PROC" might save me a few keystrokes, and I'm sure the shorthand equivalents are here to stay, but something just doesn't
feel right about it. Same with the parameters for built-in functions like DATEDIFF(), DATEADD() and DATEPART(). Why use WK or DW when you can use WEEK or WEEKDAY? (I also never understood why WEEKDAY become
DW in shorthand, instead of WD, which is not supported. DW likely means DAYOFWEEK but that is an ODBC function and not supported directly in T-SQL at all. That in and of itself convinced me that it is better to take the
expensive hit of typing five extra characters to be explicit and clear.) Finally, I always explicitly say "INNER JOIN or "LEFT OUTER JOIN"... never just "join" or "left join." Again, no real good reason behind that, just habit.
</p>
<p>======================
</p>
<p><b>Using parentheses liberally around AND / OR blocks</b>
</p>
<p>I always group my clauses when mixing AND and OR. Leaving it up to the optimizer to determine what "x=5 AND y = 4 OR b = 3" really means is not my cup of tea. I wrote a <a href="http://databases.aspfaq.com/general/why-do-i-get-weird-results-when-using-both-and-and-or-in-a-query.html" title="http://databases.aspfaq.com/general/why-do-i-get-weird-results-when-using-both-and-and-or-in-a-query.html" target="_blank">very short article about this</a> a few years ago.
</p>
<p>======================
</p>
<p>So, after all of that, given the procedure I listed at the start of the article, what would I end up with? Assuming I am using SQL Server 2008, and that I can update the calling application to use the right procedure name, to use
sensible input parameter names, and to stop using return values instead of output parameters: <br></p>
<blockquote>
<table bgcolor="#eeeeee" cellpadding="0" cellspacing="0">
<tr>
<td>
<pre style="padding:10px 20px;-moz-background-clip:border;-moz-background-origin:padding;font-size:12px;font-family:consolas,lucida console,courier new,courier;-moz-background-inline-policy:continuous;"><font color="blue">CREATE&nbsp;PROCEDURE&nbsp;</font><font color="black">dbo.Customer_GetOlderOrders<br>&nbsp;&nbsp;&nbsp;</font><font color="#434343">@OrderStatus&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="blue">INT</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;</font><font color="#434343">@MaxOrderStatus&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="blue">INT&nbsp;=&nbsp;</font><font color="gray">NULL,<br>&nbsp;&nbsp;&nbsp;</font><font color="#434343">@OrderDate&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="blue">SMALLDATETIME</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;</font><font color="#434343">@RC1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="blue">INT OUTPUT</font><font color="black"></font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;</font><font color="#434343">@RC2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="blue">INT OUTPUT</font><font color="black"><br></font><font color="blue">AS<br>BEGIN<br>&nbsp;&nbsp;&nbsp;SET NOCOUNT ON</font><font color="blue"></font><font color="gray">;<br><br>&nbsp;&nbsp;&nbsp;</font><font color="blue">DECLARE<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#434343">@City&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="blue">VARCHAR</font><font color="gray">(</font><font color="black">32</font><font color="gray">)&nbsp;</font><font color="blue">=&nbsp;</font><font color="red">'Beverly'</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#434343">@State&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="blue">CHAR</font><font color="gray"> (</font><font color="black">2</font><font color="gray">)&nbsp;&nbsp;&nbsp; </font><font color="blue">=&nbsp;</font><font color="red">'MA'</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#434343">@MinOrderCount&nbsp;&nbsp;</font><font color="blue">INT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;</font><font color="black">5</font><font color="gray">;<br><br>&nbsp;&nbsp;&nbsp;</font><font color="blue">SELECT<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="black">c.CustomerID</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="black">c.FirstName</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="black">c.LastName</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="black">c.OrderDate<br>&nbsp;&nbsp;&nbsp;</font><font color="blue">FROM<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="black">dbo.Customers c<br>&nbsp;&nbsp;&nbsp;</font><font color="blue">INNER&nbsp;JOIN<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="black">dbo.Orders&nbsp;o<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><font color="blue">ON </font><font color="black">c.CustomerID&nbsp;</font><font color="blue">=&nbsp;</font><font color="black">o.CustomerID<br>&nbsp;&nbsp;&nbsp;</font><font color="blue">WHERE&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="gray">(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="black">o.OrderStatus&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="blue">=&nbsp;</font><font color="#434343">@OrderStatus<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="gray">OR&nbsp;</font><font color="black">o.OrderStatus&nbsp;&nbsp;&nbsp; </font><font color="gray">&lt;=&nbsp;</font><font color="#434343">@MaxOrderStatus<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="gray">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;AND&nbsp;</font><font color="black">o.OrderDate&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><font color="gray">&lt;=&nbsp;</font><font color="#434343">@MaxOrderDate</font><font color="gray">;<br><br>&nbsp;&nbsp;&nbsp;</font><font color="blue">SET&nbsp;</font><font color="#434343">@RC1&nbsp;</font><font color="blue">=&nbsp;</font><font color="magenta">@@ROWCOUNT</font><font color="gray">;<br><br>&nbsp;&nbsp;&nbsp;</font><font color="blue">SELECT<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="black">c.CustomerID</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="black">OrderCount&nbsp;</font><font color="blue">=&nbsp;</font><font color="magenta">COUNT</font><font color="gray">(*)<br>&nbsp;&nbsp;&nbsp;</font><font color="blue">FROM<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="black">dbo.Customers&nbsp;c<br>&nbsp;&nbsp;&nbsp;</font><font color="gray">LEFT&nbsp;</font><font color="gray">OUTER&nbsp;</font><font color="blue">JOIN<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="black">dbo.Orders&nbsp;o<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="blue">ON&nbsp;</font><font color="black">c.CustomerID&nbsp;</font><font color="blue">=&nbsp;</font><font color="black">o.CustomerID<br>&nbsp;&nbsp;&nbsp;</font><font color="blue">WHERE<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="black">c.City&nbsp;</font><font color="blue">=&nbsp;</font><font color="#434343">@City<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="gray">AND&nbsp;</font><font color="black">c.State&nbsp;</font><font color="blue">=&nbsp;</font><font color="#434343">@State<br>&nbsp;&nbsp;&nbsp;</font><font color="blue">GROUP&nbsp;BY<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="black">c.CustomerID<br>&nbsp;&nbsp;&nbsp;</font><font color="blue">HAVING<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="magenta">COUNT</font><font color="gray">(*)&nbsp;&gt;=&nbsp;</font><font color="#434343">@MinOrderCount</font><font color="gray">;<br><br>&nbsp;&nbsp;&nbsp;</font><font color="blue">SET&nbsp;</font><font color="#434343">@RC2&nbsp;</font><font color="blue">=&nbsp;</font><font color="magenta">@@ROWCOUNT</font><font color="gray">;
<br>&nbsp;&nbsp;&nbsp;</font><font color="blue">RETURN</font><font color="gray">;<br></font><font color="blue">END<br></font><font color="black">GO</font></pre></td>
</tr>
</table>
</blockquote>
<p>Okay, so it LOOKS like a lot more code, because the layout is more vertical. But you tell me. Copy both procedures to SSMS or Query Analyzer, and which one is easier to read / understand? And is it worth the three minutes it took me to convert the original query?&nbsp; It took me a few hours to convert this list from my subconscious to you, so hopefully I have helped you pick up at least one good habit.&nbsp; And if you think any of these are BAD habits, please drop a line and let me know why!
</p>