Back to HomeSQL

SQL Advanced Techniques: Stored Procedures, Triggers, Transaction Control Practical Tutorial [2025]

14 min min read
#Stored Procedure#Trigger#Transaction#ACID#CTE#SQL Advanced#Database Development

SQL Advanced Techniques: Stored Procedures, Triggers, Transaction Control Practical Tutorial [2025]

Introduction: From SQL User to SQL Developer

Knowing SELECT, JOIN, and WHERE is just the beginning of SQL. True SQL experts also understand how to use advanced features like stored procedures, triggers, and transaction control.

Why are these skills important?

Imagine: Your e-commerce system processes hundreds of orders per second. Each order needs to simultaneously update inventory, create order records, and process payment. If any step fails, the entire transaction must be cancelled. This is the problem that transaction control solves.

This article will take you deep into SQL's advanced features, elevating you from "using SQL" to "mastering SQL."

If you're not familiar with basic syntax, we recommend first reading SQL Syntax Complete Tutorial.

Illustration 1: Database developer designing stored procedure architecture

SQL Advanced Features Overview

Value of Advanced Features

FeatureMain PurposeBenefits
Stored ProceduresEncapsulate business logicPerformance improvement, code reuse, security
TriggersAutomate data processingMaintain data consistency, automatic change logging
Transaction ControlEnsure data integrityPrevent partial updates, support rollback
CTESimplify complex queriesImprove readability, support recursive queries
CursorProcess data row by rowHandle scenarios that can't be batch processed

Recommended Learning Order

  1. Transaction Control: Most fundamental and important, every data change should understand this
  2. Stored Procedures: Most commonly used advanced feature
  3. CTE: Powerful tool for simplifying complex queries
  4. Triggers: Great helper for automated processing
  5. Cursor: Learn last, as there are usually better alternatives

Stored Procedures

What is a Stored Procedure?

A stored procedure is a pre-compiled SQL code block stored in the database. You can think of it as a "function" for the database.

Stored Procedure Advantages:

AdvantageDescription
Performance ImprovementPre-compiled, executes faster
Reduced Network TransferOnly parameters sent, not entire SQL
Code ReuseWrite once, call anywhere
SecurityCan grant execute permission only, not direct table access
MaintainabilityCentralized business logic management

CREATE PROCEDURE Basic Syntax

-- Create simple stored procedure
CREATE PROCEDURE GetAllEmployees
AS
BEGIN
    SELECT * FROM employees;
END;

-- Execute stored procedure
EXEC GetAllEmployees;

Stored Procedure with Parameters

-- Create stored procedure with input parameter
CREATE PROCEDURE GetEmployeesByDepartment
    @DepartmentName NVARCHAR(50)
AS
BEGIN
    SELECT name, salary, hire_date
    FROM employees
    WHERE department = @DepartmentName;
END;

-- Execute
EXEC GetEmployeesByDepartment @DepartmentName = 'Engineering';

OUTPUT Parameters

-- Create stored procedure with output parameters
CREATE PROCEDURE GetDepartmentStats
    @DepartmentName NVARCHAR(50),
    @EmployeeCount INT OUTPUT,
    @AvgSalary DECIMAL(10,2) OUTPUT
AS
BEGIN
    SELECT
        @EmployeeCount = COUNT(*),
        @AvgSalary = AVG(salary)
    FROM employees
    WHERE department = @DepartmentName;
END;

-- Execute and get output values
DECLARE @Count INT, @Avg DECIMAL(10,2);
EXEC GetDepartmentStats
    @DepartmentName = 'Engineering',
    @EmployeeCount = @Count OUTPUT,
    @AvgSalary = @Avg OUTPUT;

SELECT @Count AS EmployeeCount, @Avg AS AvgSalary;

Default Values and Optional Parameters

CREATE PROCEDURE SearchEmployees
    @Name NVARCHAR(50) = NULL,
    @MinSalary DECIMAL(10,2) = 0,
    @Department NVARCHAR(50) = NULL
AS
BEGIN
    SELECT *
    FROM employees
    WHERE (@Name IS NULL OR name LIKE '%' + @Name + '%')
      AND salary >= @MinSalary
      AND (@Department IS NULL OR department = @Department);
END;

-- Pass only some parameters
EXEC SearchEmployees @MinSalary = 50000;
EXEC SearchEmployees @Department = 'Engineering', @MinSalary = 60000;

Error Handling TRY...CATCH

CREATE PROCEDURE TransferMoney
    @FromAccount INT,
    @ToAccount INT,
    @Amount DECIMAL(10,2)
AS
BEGIN
    BEGIN TRY
        BEGIN TRANSACTION;

        -- Debit
        UPDATE accounts
        SET balance = balance - @Amount
        WHERE id = @FromAccount;

        -- Check if balance is sufficient
        IF (SELECT balance FROM accounts WHERE id = @FromAccount) < 0
        BEGIN
            RAISERROR('Insufficient balance', 16, 1);
        END

        -- Credit
        UPDATE accounts
        SET balance = balance + @Amount
        WHERE id = @ToAccount;

        COMMIT TRANSACTION;
        PRINT 'Transfer successful';
    END TRY
    BEGIN CATCH
        ROLLBACK TRANSACTION;
        PRINT 'Transfer failed: ' + ERROR_MESSAGE();
    END CATCH
END;

For more advanced stored procedure techniques, see SQL Stored Procedure Complete Tutorial.


Triggers

What is a Trigger?

A trigger is a special stored procedure that automatically executes when specific events (INSERT, UPDATE, DELETE) occur.

Trigger Use Cases:

  • Automatic change logging (Audit Log)
  • Maintain derived columns (like update timestamp)
  • Implement complex business rules
  • Synchronize updates to related tables

Trigger Types

TypeExecution TimingDescription
AFTERAfter event completesMost common, suitable for logging
INSTEAD OFReplaces original eventSuitable for complex validation, DML on Views

AFTER Trigger Example

-- Create audit log table
CREATE TABLE audit_log (
    id INT IDENTITY(1,1) PRIMARY KEY,
    table_name NVARCHAR(50),
    action NVARCHAR(10),
    record_id INT,
    old_values NVARCHAR(MAX),
    new_values NVARCHAR(MAX),
    changed_by NVARCHAR(50),
    changed_at DATETIME DEFAULT GETDATE()
);

-- Create AFTER UPDATE trigger
CREATE TRIGGER trg_employees_update
ON employees
AFTER UPDATE
AS
BEGIN
    INSERT INTO audit_log (table_name, action, record_id, old_values, new_values, changed_by)
    SELECT
        'employees',
        'UPDATE',
        i.id,
        (SELECT d.name, d.salary, d.department FROM deleted d WHERE d.id = i.id FOR JSON PATH),
        (SELECT i.name, i.salary, i.department FOR JSON PATH),
        SYSTEM_USER
    FROM inserted i;
END;

INSTEAD OF Trigger Example

-- Create View
CREATE VIEW v_active_employees AS
SELECT id, name, salary, department
FROM employees
WHERE status = 'active';

-- Allow INSERT on View
CREATE TRIGGER trg_v_active_employees_insert
ON v_active_employees
INSTEAD OF INSERT
AS
BEGIN
    INSERT INTO employees (name, salary, department, status)
    SELECT name, salary, department, 'active'
    FROM inserted;
END;

Trigger Considerations

Performance Impact:

  • Triggers execute on every DML operation
  • Complex triggers will significantly impact performance
  • Avoid using Cursors in triggers

Debugging Difficulty:

  • Triggers are "invisible" and hard to trace
  • Recommend adding appropriate logging in triggers

Avoid Trigger Chains:

  • Trigger A fires Trigger B, which fires Trigger A
  • Can cause infinite loops
-- Check if called by trigger (avoid chaining)
IF TRIGGER_NESTLEVEL() > 1
    RETURN;

Illustration 2: Trigger execution flow diagram

Transaction Control

Why Do We Need Transaction Control?

Imagine a bank transfer scenario:

  1. Deduct $1000 from Account A
  2. Deposit $1000 to Account B

What happens if step 1 succeeds but step 2 fails (e.g., Account B doesn't exist)?

Without transaction control: Account A loses $1000, but Account B doesn't gain it. The money "disappears."

With transaction control: The system automatically cancels the deduction from Account A, returning to the original state.

ACID Properties

Transactions must satisfy four properties, known as ACID:

PropertyEnglishDescription
AtomicityAtomicityAll succeed or all fail
ConsistencyConsistencyData remains consistent before and after transaction
IsolationIsolationTransactions don't interfere with each other
DurabilityDurabilityCompleted transactions are permanently saved

Basic Transaction Syntax

-- Begin transaction
BEGIN TRANSACTION;

-- Execute operations
UPDATE accounts SET balance = balance - 1000 WHERE id = 1;
UPDATE accounts SET balance = balance + 1000 WHERE id = 2;

-- Commit transaction (permanent)
COMMIT TRANSACTION;

-- Or cancel transaction (rollback all changes)
-- ROLLBACK TRANSACTION;

Transaction with Error Handling

BEGIN TRY
    BEGIN TRANSACTION;

    -- Debit
    UPDATE accounts SET balance = balance - 1000 WHERE id = 1;

    -- Simulate error
    IF (SELECT balance FROM accounts WHERE id = 1) < 0
        RAISERROR('Insufficient balance', 16, 1);

    -- Credit
    UPDATE accounts SET balance = balance + 1000 WHERE id = 2;

    -- All successful, commit transaction
    COMMIT TRANSACTION;
    PRINT 'Transaction successful';
END TRY
BEGIN CATCH
    -- Error occurred, rollback transaction
    IF @@TRANCOUNT > 0
        ROLLBACK TRANSACTION;

    PRINT 'Transaction failed: ' + ERROR_MESSAGE();
END CATCH

Isolation Levels

Isolation level determines how transactions affect each other. Higher levels mean better data consistency but worse concurrent performance.

Isolation LevelDirty ReadNon-Repeatable ReadPhantom ReadDescription
READ UNCOMMITTEDPossiblePossiblePossibleLowest, can read uncommitted data
READ COMMITTEDNoPossiblePossibleSQL Server default
REPEATABLE READNoNoPossibleLocks read rows
SERIALIZABLENoNoNoHighest, complete isolation
-- Set isolation level
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

BEGIN TRANSACTION;
-- Your operations
COMMIT TRANSACTION;

Deadlocks

When two transactions wait for each other to release resources, a deadlock occurs.

Deadlock Example:

Transaction A: Locks Table X, waiting for Table Y
Transaction B: Locks Table Y, waiting for Table X
→ Both wait forever, causing deadlock

Methods to Avoid Deadlocks:

  • All transactions access tables in the same order
  • Keep transaction time short
  • Use appropriate indexes
  • Avoid user interaction within transactions

Need Help with Database Architecture Design?

According to research, 60% of database performance issues are related to improper transaction design. Incorrect isolation level selection can lead to data inconsistency or severe performance bottlenecks.

CloudInsight Database Architecture Services

  • Transaction Design Consulting: Analyze business processes, design optimal transaction boundaries
  • Concurrency Issue Diagnosis: Find root causes of deadlocks and lock waits
  • Isolation Level Optimization: Balance data consistency and performance
  • Stored Procedure Review: Review existing code for performance and security

Our Expertise

  • Handled transaction optimization for TB-level databases
  • Experience with high-concurrency scenarios in finance and e-commerce
  • Provide specific code-level recommendations

👉 Schedule Free Database Architecture Consultation to Solve Concurrency and Performance Issues


Cursor Usage

What is a Cursor?

A Cursor allows you to process query results row by row, rather than processing the entire result set at once.

When to Use:

  • Need complex calculations per row
  • Operations that can't be done with a single SQL statement
  • Need to call external programs or stored procedures

Note: Cursor performance is usually poor; SET-based operations should be preferred.

Cursor Basic Syntax

-- Declare variables
DECLARE @EmployeeId INT, @Name NVARCHAR(50), @Salary DECIMAL(10,2);

-- Declare Cursor
DECLARE employee_cursor CURSOR FOR
    SELECT id, name, salary FROM employees WHERE department = 'Engineering';

-- Open Cursor
OPEN employee_cursor;

-- Read first row
FETCH NEXT FROM employee_cursor INTO @EmployeeId, @Name, @Salary;

-- Process row by row
WHILE @@FETCH_STATUS = 0
BEGIN
    -- Process each row
    PRINT 'Employee: ' + @Name + ', Salary: ' + CAST(@Salary AS NVARCHAR);

    -- Read next row
    FETCH NEXT FROM employee_cursor INTO @EmployeeId, @Name, @Salary;
END

-- Close and release Cursor
CLOSE employee_cursor;
DEALLOCATE employee_cursor;

Cursor Alternatives

In most cases, more efficient methods can replace Cursors:

Example: Batch Update (Don't use Cursor)

-- ❌ Using Cursor (poor performance)
DECLARE @Id INT;
DECLARE cur CURSOR FOR SELECT id FROM employees WHERE salary < 30000;
OPEN cur;
FETCH NEXT FROM cur INTO @Id;
WHILE @@FETCH_STATUS = 0
BEGIN
    UPDATE employees SET salary = salary * 1.1 WHERE id = @Id;
    FETCH NEXT FROM cur INTO @Id;
END
CLOSE cur;
DEALLOCATE cur;

-- ✅ Using SET-based operation (good performance)
UPDATE employees SET salary = salary * 1.1 WHERE salary < 30000;

CTE Common Table Expressions

What is CTE?

CTE (Common Table Expression) is a temporary named result set that exists only during the execution of a single query.

CTE Advantages:

  • Improve readability of complex queries
  • Support recursive queries
  • Can be referenced multiple times in the same query

CTE Basic Syntax

-- Basic CTE
WITH high_salary_employees AS (
    SELECT id, name, department, salary
    FROM employees
    WHERE salary > 70000
)
SELECT department, COUNT(*) AS high_earners
FROM high_salary_employees
GROUP BY department;

Multiple CTEs

WITH
-- First CTE: High salary employees
high_salary AS (
    SELECT * FROM employees WHERE salary > 70000
),
-- Second CTE: Department statistics
dept_stats AS (
    SELECT department, AVG(salary) AS avg_salary
    FROM employees
    GROUP BY department
)
-- Main query: Combine both CTEs
SELECT h.name, h.salary, d.avg_salary AS dept_avg
FROM high_salary h
JOIN dept_stats d ON h.department = d.department;

Recursive CTE

Recursive CTEs are suitable for processing hierarchical data, such as organization charts or category trees.

-- Query employee management hierarchy
WITH employee_hierarchy AS (
    -- Anchor: Top-level managers
    SELECT id, name, manager_id, 0 AS level
    FROM employees
    WHERE manager_id IS NULL

    UNION ALL

    -- Recursive: Find subordinates
    SELECT e.id, e.name, e.manager_id, h.level + 1
    FROM employees e
    INNER JOIN employee_hierarchy h ON e.manager_id = h.id
)
SELECT name, level
FROM employee_hierarchy
ORDER BY level, name;

Illustration 3: CTE vs subquery comparison

Practical Case: Order Processing System

Requirements

Build an order processing stored procedure that needs to:

  1. Check if inventory is sufficient
  2. Create order record
  3. Deduct inventory
  4. Log changes
  5. Rollback if any step fails

Complete Implementation

CREATE PROCEDURE CreateOrder
    @CustomerId INT,
    @ProductId INT,
    @Quantity INT,
    @OrderId INT OUTPUT
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @CurrentStock INT;
    DECLARE @UnitPrice DECIMAL(10,2);

    BEGIN TRY
        BEGIN TRANSACTION;

        -- Step 1: Check inventory (use UPDLOCK to avoid concurrency issues)
        SELECT @CurrentStock = stock_quantity, @UnitPrice = price
        FROM products WITH (UPDLOCK)
        WHERE id = @ProductId;

        IF @CurrentStock IS NULL
        BEGIN
            RAISERROR('Product does not exist', 16, 1);
        END

        IF @CurrentStock < @Quantity
        BEGIN
            RAISERROR('Insufficient inventory, current stock: %d', 16, 1, @CurrentStock);
        END

        -- Step 2: Create order
        INSERT INTO orders (customer_id, product_id, quantity, unit_price, total_amount, status, created_at)
        VALUES (@CustomerId, @ProductId, @Quantity, @UnitPrice, @UnitPrice * @Quantity, 'pending', GETDATE());

        SET @OrderId = SCOPE_IDENTITY();

        -- Step 3: Deduct inventory
        UPDATE products
        SET stock_quantity = stock_quantity - @Quantity
        WHERE id = @ProductId;

        -- Step 4: Log (can be handled by trigger, manual logging shown here)
        INSERT INTO order_logs (order_id, action, details, created_at)
        VALUES (@OrderId, 'CREATE', 'Order created successfully, quantity: ' + CAST(@Quantity AS NVARCHAR), GETDATE());

        -- All successful
        COMMIT TRANSACTION;

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

        -- Log error
        INSERT INTO error_logs (procedure_name, error_message, created_at)
        VALUES ('CreateOrder', ERROR_MESSAGE(), GETDATE());

        -- Re-throw error
        THROW;
    END CATCH
END;

Usage

DECLARE @NewOrderId INT;

EXEC CreateOrder
    @CustomerId = 1,
    @ProductId = 100,
    @Quantity = 5,
    @OrderId = @NewOrderId OUTPUT;

SELECT @NewOrderId AS NewOrderId;

FAQ

Q1: When should I use stored procedures?

Stored procedures are suitable for: scenarios requiring complex business logic encapsulation, repeatedly used SQL operations, performance requirements (pre-compiled), and access control needs (grant only execute permission). For simple CRUD operations, using ORM or SQL statements directly may be easier to maintain.

Q2: Do triggers affect database performance?

Yes. Triggers execute on every DML operation. If the trigger logic is complex or involves large data operations, it will noticeably impact performance. Recommendations: keep trigger logic simple, avoid using Cursors in triggers, avoid trigger chains, and consider using stored procedures instead of non-essential triggers.

Q3: What is a database deadlock? How to avoid it?

A deadlock is when two or more transactions wait for each other to release resources. For example: Transaction A locks Table X waiting for Y, Transaction B locks Table Y waiting for X. Avoidance methods: all transactions access resources in the same order, keep transaction time short, use appropriate indexes to reduce lock scope, and set reasonable lock timeout values.


Further Reading

After mastering advanced features, we recommend continuing with these topics:

Advanced Topics:

Basic Review:

Environment and Tools:


Advanced SQL Development Training

According to Stack Overflow research, developers proficient in stored procedures and transaction control earn on average 25% more than those who only know basic SQL.

CloudInsight SQL Advanced Training Courses

  • Stored Procedure Practice: From basics to error handling, performance optimization
  • Transaction Control Deep Dive: ACID properties, isolation levels, deadlock handling
  • Trigger Applications: Design patterns, performance considerations, best practices
  • Performance Tuning: Execution plan analysis, index strategy, query optimization

Course Features

  • Using real enterprise cases for teaching
  • 30-day technical consultation after completion
  • Certificate upon completion

👉 Schedule Free Consultation to Plan Advanced SQL Training for Your Team


References

  1. Microsoft, "Transact-SQL Stored Procedures" (2024)
  2. Microsoft, "CREATE TRIGGER (Transact-SQL)" (2024)
  3. Microsoft, "Transaction Statements (Transact-SQL)" (2024)
  4. Microsoft, "WITH common_table_expression" (2024)
  5. SQL Server Documentation, "Locking and Row Versioning" (2024)

Need Professional Cloud Advice?

Whether you're evaluating cloud platforms, optimizing existing architecture, or looking for cost-saving solutions, we can help

Book Free Consultation

Related Articles