SQL  

SQL Commenting Best Practices: Benefits, Standards & Examples

Writing SQL code is not just about making it work — it’s also about making it understandable, maintainable, and audit-friendly. Proper commenting is a critical part of writing professional SQL Code. In this post, we’ll explore the benefits of SQL comments, standard commenting practices, and examples for different SQL objects, including tables, procedures, functions, triggers, sequences, and indexes.

Why Comment in SQL Code?

Commenting SQL code brings several benefits:

  1. Improves Readability: Helps developers understand your code without digging into every line.

  2. Facilitates Maintenance: Makes it easier to fix bugs or enhance features later.

  3. Audit & Documentation: Useful in enterprise environments for tracking who created or modified objects.

  4. Reduces Human Error: Helps teams follow standards and avoid mistakes.

  5. Supports Collaboration: Makes it easier for multiple developers to work on the same database.

Commenting Standards

A good SQL commenting standard should include:

  • Header Block Comments: Metadata about the object: author, date, description, and notes.

  • Inline Comments: Explain non-obvious logic or why a piece of code exists.

  • Consistency: Same style across all objects (tables, triggers, procedures, etc.).

  • Avoid Obvious Comments: Explain why instead of what.

  • Audit Information: Optional: who created or modified the object, when, and why.

Effective Commenting for Tables, Functions, Triggers, Indexes, and Sequences

1. Tables

/******************************************************************************************
* Table Name   : dbo.Roles
* Description  : Stores system roles with audit columns.
* Author       : Raj Bhatt
* Created On   : 2025-10-09
* Last Modified: 2025-10-09
* Notes:
*   - Includes CreatedBy, CreatedDate, UpdatedBy, UpdatedDate columns for auditing.
******************************************************************************************/

CREATE TABLE dbo.Roles (
    RoleId INT IDENTITY(1,1) PRIMARY KEY,           -- Unique identifier for the role
    RoleName VARCHAR(50) NOT NULL,                  -- Name of the role
    Description VARCHAR(255) NULL,                  -- Optional description of the role
    CreatedBy VARCHAR(100) NULL,                    -- User who created the record
    CreatedDate DATETIME DEFAULT GETDATE(),         -- Timestamp when record was created
    UpdatedBy VARCHAR(100) NULL,                    -- User who last updated the record
    UpdatedDate DATETIME NULL                        -- Timestamp when record was last updated
);

Important points

  • Use block comments for headers.

  • Use inline comments for columns.

  • Include author, creation date, and purpose.

2. Functions

/******************************************************************************************
* Function Name : fn_GetRoleName
* Description   : Returns the RoleName for a given RoleId.
* Author        : Raj Bhatt
* Created On    : 2025-10-09
******************************************************************************************/
CREATE FUNCTION dbo.fn_GetRoleName (@RoleId INT)
RETURNS VARCHAR(50)
AS
BEGIN
    DECLARE @RoleName VARCHAR(50);

    -- Fetch RoleName from Roles table
    SELECT @RoleName = RoleName
    FROM dbo.Roles
    WHERE RoleId = @RoleId;

    RETURN @RoleName;
END;
GO

Always think about where and how the function will be used. If it’s called millions of times in a query, performance optimization is critical

3. Triggers

/******************************************************************************************
* Trigger Name : trg_Update_Roles
* Table Name   : dbo.Roles
* Description  : Automatically updates UpdatedDate when a record in Roles is modified.
* Author       : Raj Bhatt
* Created On   : 2025-10-09
* Last Modified: 2025-10-09
* Notes:
*   - UpdatedBy must be set manually.
*   - Ensures audit consistency across updates.
******************************************************************************************/

CREATE TRIGGER trg_Update_Roles
ON dbo.Roles
AFTER UPDATE
AS
BEGIN
    SET NOCOUNT ON;  -- Prevent "rows affected" messages for better performance

    BEGIN TRY
        -- Update the UpdatedDate to current timestamp for all modified rows
        UPDATE r
        SET r.UpdatedDate = GETDATE()
        FROM dbo.Roles AS r
        INNER JOIN inserted AS i ON r.RoleId = i.RoleId;
    END TRY
    BEGIN CATCH
        -- Error handling: raise meaningful error message
        DECLARE @ErrorMessage NVARCHAR(4000) = ERROR_MESSAGE();
        DECLARE @ErrorSeverity INT = ERROR_SEVERITY();
        DECLARE @ErrorState INT = ERROR_STATE();

        RAISERROR('Error in trigger trg_Update_Roles: %s', @ErrorSeverity, @ErrorState, @ErrorMessage);
    END CATCH;
END;
GO

4. Sequences

/******************************************************************************************
* Sequence Name : seq_OrderId
* Description   : Generates unique OrderId for Orders table.
* Author        : Raj Bhatt
* Created On    : 2025-10-09
******************************************************************************************/
CREATE SEQUENCE dbo.seq_OrderId
    START WITH 1
    INCREMENT BY 1;

5. Indexes

/******************************************************************************************
* Index Name   : IX_Roles_RoleName
* Table Name   : dbo.Roles
* Description  : Non-clustered index on RoleName for faster search.
* Author       : Raj Bhatt
* Created On   : 2025-10-09
******************************************************************************************/
CREATE NONCLUSTERED INDEX IX_Roles_RoleName
ON dbo.Roles (RoleName);

6. Stored Procedures

/******************************************************************************************
* Procedure Name : sp_GetRoleById
* Description    : Retrieves role details by RoleId.
* Author         : Raj Bhatt
* Created On     : 2025-10-09
* Last Modified  : 2025-10-09
* Parameters:
*   @RoleId INT - Role identifier
* Returns:
*   Role details from dbo.Roles
******************************************************************************************/
CREATE PROCEDURE dbo.sp_GetRoleById
    @RoleId INT
AS
BEGIN
    SET NOCOUNT ON;  -- Prevent "rows affected" messages

    -- Select role information
    SELECT RoleId, RoleName, Description, CreatedBy, CreatedDate, UpdatedBy, UpdatedDate
    FROM dbo.Roles
    WHERE RoleId = @RoleId;
END;
GO

SQL Code Smells & Rules for Tables, Functions, Triggers, Indexes and Sequences

1. Tables

Code Smells

  • Poor naming conventions: Table names like tbl1, data, or temp are vague.

  • Too many columns: More than 50–100 columns might indicate the table isn’t normalized.

  • Unused columns: Columns never queried or used in transactions.

  • Repeated data: Indicates denormalization or missing relationships.

  • No primary key: Leads to duplicate rows and poor indexing.

  • Excessive nullable columns: Hard to enforce data integrity.

Best Practices

  • Use clear, singular, meaningful names: Employee, ProductOrder.

  • Normalize data to at least 3NF unless justified.

  • Always define primary keys and appropriate foreign keys.

  • Use data types appropriately: don’t use VARCHAR(255) for small text.

  • Limit NULLs; use default values where applicable.

  • Add comments to describe table's purpose:

2. Functions

Code Smells

  • Functions that do too much (multiple responsibilities).

  • Functions that access tables unnecessarily inside loops.

  • Functions with side effects (modifying data).

  • Poor naming (func1, doSomething).

Best Practices

  • Functions should be pure (no side effects).

  • One function → one purpose.

  • Use a schema prefix to avoid ambiguity.

  • Add comments explaining input/output:

3. Triggers

Code Smells

  • Triggers that perform complex logic or call other triggers → hard to maintain.

  • Silent failures: errors not logged.

  • Multiple triggers for the same action → order dependency issues.

  • Triggers updating the same table → risk of recursion.

Best Practices

  • Keep triggers small and specific.

  • Prefer using constraints or stored procedures instead of triggers if possible.

  • Log errors and operations.

  • Use AFTER vs INSTEAD OF carefully.

4. Indexes

Code Smells

  • Too many indexes → slows down writes.

  • Unused indexes → waste of storage.

  • Non-selective indexes → low performance gain.

  • Indexing columns that are frequently updated → high maintenance cost.

Best Practices

  • Index frequently queried columns used in WHERE, JOIN, and ORDER BY.

  • Avoid indexing columns with low cardinality (like gender).

  • Monitor index usage and remove unused indexes.

  • Name indexes consistently: IX_Table_Column.

5. Sequences

Code Smells

  • Using sequences unnecessarily for small tables.

  • Sequences with large gaps → indicate poor management.

  • Sequences without ownership or naming standards.

Best Practices

  • Use sequences for unique, incremental values across tables.

  • Define start, increment, min, mand ax values.

  • Always name sequences clearly: SEQ_EmployeeID.

6. Stored Procedures

Common SQL Smells

  • Too long / complex procedures – doing multiple unrelated tasks in one SP.

  • Hard-coded values – making the procedure inflexible and non-reusable.

  • No parameters or overuse of global variables – reduces modularity.

  • Missing error handling – errors are swallowed or unlogged.

  • Excessive dynamic SQL – may lead to SQL injection or maintenance issues.

  • Returning result sets instead of using output parameters when needed – inconsistent usage.

  • Tightly coupled logic – SP depends heavily on other SPs or tables, making it hard to maintain.

  • Unused or deprecated SPs – bloats the database.

Best Practices

  • One procedure, one purpose – keep SPs focused and small.

  • Use parameters – avoid hard-coded values; makes SP reusable.

  • Error handling – use TRY…CATCH to log or handle errors gracefully.

  • Use schema prefix & meaningful names – e.g., usp_InsertEmployee.

  • Avoid excessive dynamic SQL – use static queries where possible; if dynamic SQL is needed, validate input.

  • Document logic – add comments for inputs, outputs, and special cases.

  • Return status codes or output parameters instead of always returning full result sets.

  • Use sequences or identity columns appropriately – only when unique incremental IDs are required.

  • Avoid unnecessary loops – leverage set-based operations for performance.

  • Maintainability – regularly review SPs and remove unused or deprecated ones.

This guide can serve as a reference for developers, DBAs, and reviewers to maintain clean, maintainable SQL code and avoid performance pitfalls.

Proper SQL commenting is not optional — it’s a professional standard. It improves readability, maintainability, and auditing. Following these standards ensures that your database is robust, team-friendly, and future-proof. Commenting may take a few extra minutes while writing code, but it saves hours during debugging and maintenance.

Thank you for taking the time to read this post. I hope it has provided you with a clear understanding of SQL Commenting Best Practices: Benefits, Standards & Examples. Proper SQL commenting and following coding standards may seem small, but they make a big difference in creating maintainable, readable, and professional database code. By implementing these best practices, you ensure your projects are team-friendly, robust, and easier to manage in the long run.

Happy coding and keep your SQL Code clean !!!