5 SQL Server Mistakes Developers Should Catch Before Production

Clean, performant, and maintainable SQL often comes down to catching small issues before they become production problems.

After years of reviewing pull requests and troubleshooting SQL Server performance issues, I’ve seen the same patterns show up repeatedly. These are not usually syntax errors. The code runs, returns data, and may even pass testing. That is what makes them easy to miss.

Here are five common SQL Server mistakes I see, why they matter, and what to watch for before the code reaches production.


1) Implicit Conversions Due to Data Type Mismatches

Implicit conversions are easy to miss because SSMS does not always make them obvious while you are writing the query. They commonly happen when SQL Server has to compare mismatched data types, such as DATE to DATETIME2, or VARCHAR to NVARCHAR. The performance impact, however, can be very noticeable. This can prevent SQL Server from using an index seek efficiently, and that speed decrease can sometimes be drastic.

The best way to prevent this is to check the data types you’re comparing in JOIN and WHERE clauses. When designing new tables, also take the time to confirm what the person before you has already done. If you are assuming a column is an INT, VARCHAR, or DATE, it only takes a second to confirm and it can save you pain down the road.


Reading the execution plan will also show you warnings for this.


2) Incorrect Error Handling in Stored Procedures

A common mistake I see in stored procedures is assuming that a simple TRY/CATCH block is enough.

It’s a good start, but once transactions are involved, you need to be more careful. If an error occurs partway through the procedure and the transaction is not handled correctly, you can end up with an open transaction, an uncommittable transaction, or changes that do not get rolled back the way you expected.

A safer pattern is to use SET XACT_ABORT ON, wrap the work in a transaction, roll back inside the CATCH block when needed, and then re-throw the error so the calling process still knows something went wrong.


SET XACT_ABORT ON;

BEGIN TRY
    BEGIN TRANSACTION;

    -- Do your work here

    COMMIT;
END TRY
BEGIN CATCH
    IF @@TRANCOUNT > 0
        ROLLBACK;

    THROW;
END CATCH;

The important part is that the stored procedure should not quietly fail or leave the database in a weird state. Either the full unit of work succeeds, or it rolls back cleanly and raises the error back to the caller.

This is especially important for ETL, reporting loads, and any stored procedure that changes data across multiple tables. A weak error handling pattern may work fine during testing, but it can become a painful production issue when one step fails and the rest of the process keeps going like nothing happened.


3) Overusing ORDER BY

I use ORDER BY often, so you are probably thinking: why is it here? Well it’s one thing to use ORDER BY when you are doing ad-hoc work in SSMS and another to place it everywhere. I often have seen ORDER BY get left in during promotion of code, or in cases where SSMS was doing the work to order it, only for it to be placed into an order like page on a website where the user immediately changed the sort order.

ORDER BY clauses can be expensive because sorting may require additional memory. If SQL Server does not get enough memory for the sort, it may spill to TempDB, which can slow the query down significantly.

Use ORDER BY intentionally. If the returned order matters, especially for paging, reports, exports, or TOP queries, it belongs in SQL. But if it was only added to make testing easier in SSMS, or the application immediately applies its own sorting, it may be wasted work.


4) Functions on Columns in WHERE Clauses

Applying functions to indexed columns in a WHERE clause can prevent SQL Server from using indexes efficiently.

So wherever possible, design your tables so related columns use matching data types and reasonable sizes. If absolutely needed to use a filter, do it on the right side of the operator.

Avoid: WHERE LEFT(CustomerCode, 3) = 'ABC'
Better: WHERE CustomerCode LIKE 'ABC%'


You can help mitigate how impactful they are as well, if you can do a sub-query or a CTE first to narrow down how many rows & columns are impacted by the function.


5) Improperly Sized Columns

It’s well known that disk space can be impacted by large NVARCHAR or CHAR columns. NVARCHAR uses more storage than VARCHAR, and CHAR is always the set length and pads with spaces.

However, even large VARCHAR columns can impact query performance. Certain larger column sizes cannot be used in the key portion of a nonclustered index and may need to be placed in the INCLUDE section instead, which can limit your tuning options down the road.

Furthermore, SQL Server will assume when making the execution plan that even VARCHAR columns are half full for every row when it goes to estimate how much memory it needs. This can lead to excessive memory grants, making your query the famous “noisy college house” neighbour on the server, blasting music and leaving less memory available for everything else running in production. I rather doubt you’re lucky enough to be the only request happening on a production database.

So always make sure to take time and plan with reasonable lengths and column data types for your tables. You’ll thank yourself down the road.


None of these mistakes usually break code immediately. That is what makes them easy to miss during development and code review. But over time, they can lead to slower queries, harder troubleshooting, larger memory grants, and more painful production issues.

Catching them early is one of the easiest ways to keep SQL Server code cleaner, faster, and easier to maintain.

Next
Next

API to SQL: A Simple Azure Data Factory ETL Example