Coding Standards (T-SQL Tuesday #151)
Foreword π︎
This month’s invitation is from Mala Mahadevan, and it’s about coding standards. Fortunately, I may forget several basic ones because my team probably eradicated them long ago.
I’ll go with a rapid-fire strategy, just a long list of things to do and avoid with only a brief explanation. Some of these I’ve already covered in my rant.
Don’t do this π︎
I’ll start with the don’t because the risk of doing something terrible usually outweighs doing something really well.
No scalar functions π︎
Lots of people underestimate just how lousy scalar functions are.
- You force the SQL to process row by agonizing row (RBAR).
- It’s invisible to
SET STATISTICS IO, TIME ON
, so you might not even know how bad it is. - It prevents parallelism - even if the scalar function is in a view’s column, you are not referencing.
- It can be hidden in Computed Columns or Check Constraints.
Even though the topic is already covered by many, I might blog about it later.
No Merge π︎
I’ll leave Aaron Bertrand and co to explain this.
No magic constants π︎
WHERE p.ProductType <> 4
What is 4
? Just set a variable (constant) from a lookup table. Or write a comment with an explanation.
It’s the least you can do.
No meaningless wrappers π︎
I mean brackets and parenthesis
WHERE
(
([p].[ProductType] <> 4)
AND ([p].[CreateDate] >= '2022-06-12 00:00:00.000')
)
could just be this
WHERE
p.ProductType <> 4
AND p.CreateDate >= '2022-06-12 00:00:00.000'
There is no need for this extra eye strain. I only use brackets when the identifier turns blue.
Parenthesis should be added to help readability. Also, when the order of operations might not be clear, like when AND
plus OR
is combined.
No business logic/validation in Triggers π︎
Stay away from the Triggers. I have only two use cases for them:
- Scaffolding during refactoring
- Sync two copies of the same table
INSTEAD OF
Trigger on a view)
- Enforcing audit columns (LastModificationDate, ModifiedBy)
- Even though now we can use Temporal tables
No capes! π︎
Sorry, that’s just my favourite Incredibles quote. Not sure how that got here.
No ordinals π︎
Apart from a quick ad-hoc query, never use this
GROUP BY 2, 4
ORDER BY 1, 3 DESC, 2
It’s very brittle. Someone will come and change the select order and screw up the logic.
It’s terrible for readability.
No old JOIN syntax π︎
Even though I cannot remember the last time I saw it, don’t use the ANSI-89 JOIN syntax.
SELECT *
FROM dbo.Customer AS c, dbo.OrderHeader AS oh
WHERE
oh.CustomerId = c.CustomerId
There is just no need to use this.
If you forget the join condition in the WHERE
clause, you have a Cartesian product (row count from c
Γ row count from oh
).
Always* do this π︎
On the other hand, I usually recommend these (asterisk applies).
Alias everything π︎
Especially table names. I prefer to use initials.
Nullability is specified π︎
When declaring table variables or temp tables, always specify if the column can be NULL
. It helps to set the expectations about the data.
All constraints are named π︎
Some schema compare tools are smart enough to disregard those. But if you script out two objects and compare them with a diff tool, you will find many differences if the constraints are not explicitly named.
Scripts should be idempotent π︎
I had to search the term definition the first time around. It means that the script can be run many times but do that thing you wanted only once.
Unless, of course, you rely on the script to break if something is amiss.
Only regular identifiers π︎
I’m not too fond of brackets, so don’t force me to use them.
You don’t have to type out brackets when using only regular identifiers.
As few hints as possible π︎
They say: “Change is the only constant”, but it’s not true. So are the hints. Once they’re used, they usually stay. It’s a slippery slope telling SQL how it should behave - unless you are Paul White.
XACT_ABORT everywhere π︎
SQL Server is not very consistent in handling transactions, so it needs all the help it can get. I’ll let Michael J Swart cover the rest.
Always be deterministic π︎
If you ever wondered why you get the same result on two different pages, it’s probably because of determinism. So I usually add a surrogate PK to the ORDER BY
clause when I want to make sure to be deterministic.
The TOP clause has parentheses π︎
A little-known fact is that the TOP operator accepts an expression. But it needs to be wrapped in parenthesis like so
[
TOP (expression) [PERCENT]
[ WITH TIES ]
]
Everything is schema-qualified π︎
Apart from the default schema issues, it helps differentiate real tables from table expressions.
But it can even lead to Compile locks blocking
One condition per line π︎
It makes it easier to read and move conditions around.
Do you agree with my standards? Let me know and thank you for reading.