Saturday, February 21, 2026

Query Tunning

                                                 Query Tunning



Let consider table design.

CREATE TABLE Employee

(

    EmployeeID        INT IDENTITY(1,1) PRIMARY KEY,

    EmployeeCode      VARCHAR(20) NOT NULL UNIQUE,

    FirstName         VARCHAR(100) NOT NULL,

    LastName          VARCHAR(100) NULL,

    FullName          AS (FirstName + ' ' + ISNULL(LastName,'')) PERSISTED,

    Email             VARCHAR(150) NULL,

    MobileNo          VARCHAR(15) NULL,

    Gender            CHAR(1) NULL,      -- M/F/O

    DateOfBirth       DATE NULL,

    JoinDate          DATE NOT NULL,

    DepartmentID      INT NOT NULL,

    DesignationID     INT NOT NULL,

    ManagerID         INT NULL,

    EmploymentType    VARCHAR(20) NULL,  -- Permanent/Contract

    IsActive          BIT NOT NULL DEFAULT 1,

    CreatedDate       DATETIME2 DEFAULT GETDATE(),

    CreatedBy         VARCHAR(50),

    ModifiedDate      DATETIME2 NULL,

    ModifiedBy        VARCHAR(50)

);



✔️ Check Actual Execution Plan

Always start with:

SET STATISTICS IO ON;
SET STATISTICS TIME ON;

Look for:

  • Table scans

  • Key lookups

  • High logical reads

  • Parallelism operators

Rule: High logical reads = performance problem.


✔️ Avoid SELECT *

Bad:

SELECT * FROM Employee;

Better:

SELECT EmpID, Name, Dept FROM Employee;

Why:

  • Reduces I/O

  • Uses covering indexes better.


🔎 2️⃣ Index Tuning

Clustered vs Non-Clustered

  • Clustered → data storage order

  • Nonclustered → lookup structure

Good Index Design

CREATE INDEX IX_Employee_Dept_Date
ON Employee(DeptID, JoinDate);

Tips:

  • Put highly filtered columns first

  • Include columns to avoid lookup:

CREATE INDEX IX_Sales
ON Sales(OrderDate)
INCLUDE (Amount, CustomerID);

❌ Over-Indexing Problems

Too many indexes cause:

  • Slow INSERT/UPDATE

  • Extra maintenance cost

Check unused indexes:

SELECT *
FROM sys.dm_db_index_usage_stats;

🧠 3️⃣ WHERE Clause Optimization

Bad (non-SARGable)

WHERE YEAR(OrderDate) = 2025

Good (SARGable)

WHERE OrderDate >= '2025-01-01'
AND OrderDate < '2026-01-01'

Reason:

  • Allows index seek.


🚀 4️⃣ JOIN Optimization

Always Join on Indexed Columns

Bad:

ON A.Name = B.Name

Good:

ON A.ID = B.ID

Check Join Type

  • Nested Loop → small data

  • Hash Match → large sets

  • Merge Join → sorted data

If hash joins appear too often → missing indexes.


🧱 5️⃣ Temp Table vs Table Variable

✔ Use Temp tables for large data:

CREATE TABLE #Temp (...);

Why:

  • Supports statistics

  • Better execution plans

Table variables are slower for large datasets.


🔥 6️⃣ Parameter Sniffing Fixes

Problem:
Bad execution plan reused.

Solutions:

OPTION (RECOMPILE)

OR

DECLARE @LocalVar INT = @Param;

OR optimize with proper indexes.


⚙️ 7️⃣ Parallelism (Important for You)

You already asked about CXPACKET before 👍

Check:

SELECT * FROM sys.dm_os_wait_stats;

Guidelines:

  • Cost Threshold for Parallelism → usually 40–50

  • MAXDOP → based on CPU cores.

Too much parallelism = CPU 100%.


📊 8️⃣ IO & Wait Analysis

Find Top Expensive Queries

SELECT TOP 10
total_worker_time,
execution_count,
query_hash
FROM sys.dm_exec_query_stats
ORDER BY total_worker_time DESC;

Common Wait Types

WaitMeaningAction
CXPACKETParallelismTune MAXDOP
PAGEIOLATCHDisk slowImprove I/O
SOS_SCHEDULER_YIELDCPU pressureQuery tuning
LCK_M_*BlockingIndex/Query fix

🧹 9️⃣ Data Type Optimization

Bad:

NVARCHAR(MAX)

Better:

NVARCHAR(100)

Large datatypes = more memory + tempdb usage.

No comments:

Post a Comment