Skip to content

Bits of .NET

Daily micro-tips for C#, SQL, performance, and scalable backend engineering.

  • Asp.Net Core
  • C#
  • SQL
  • JavaScript
  • CSS
  • About
  • ErcanOPAK.com
  • No Access
  • Privacy Policy
SQL

SQL Query Optimization: The 5 Tricks That Saved Our Database from Collapse

- 23.02.26 | 23.02.26 - ErcanOPAK

💥 The Database Meltdown

9 AM Monday. Database CPU: 98%. Queries timing out. Users can’t log in. Boss is screaming. You have 1 hour to fix it before the CEO finds out.

The 5 Performance Killers (And How to Fix Them)

💣 Killer #1: SELECT *

❌ SLOW (transfers 50 columns, 10MB)
SELECT * FROM Users WHERE Status = 'Active';

✅ FAST (transfers 2 columns, 100KB)
SELECT UserId, Name FROM Users WHERE Status = 'Active';

Result: 100x less data transferred
Network time: 5s → 0.05s

💣 Killer #2: N+1 Query Problem

❌ SLOW (1001 queries!)
-- Get all orders
SELECT * FROM Orders;

-- For each order, get customer (in application code loop)
SELECT * FROM Customers WHERE CustomerId = 123;  -- x1000

Total: 1001 queries, 10 seconds

✅ FAST (1 query with JOIN)
SELECT 
    o.*,
    c.Name,
    c.Email
FROM Orders o
INNER JOIN Customers c ON o.CustomerId = c.CustomerId;

Total: 1 query, 0.1 seconds = 100x faster

💣 Killer #3: Missing Indexes

❌ SLOW (full table scan - reads 1M rows)
SELECT * FROM Orders 
WHERE CustomerId = 12345 
  AND Status = 'Shipped';

Execution plan: Table Scan
Time: 8 seconds

✅ FAST (index seek - reads 10 rows)
-- Add composite index
CREATE INDEX IX_Orders_Customer_Status 
ON Orders(CustomerId, Status);

Execution plan: Index Seek
Time: 0.02 seconds = 400x faster!
💡 Index Order Matters!

Index on (CustomerId, Status) is FAST for this query.
Index on (Status, CustomerId) would be SLOW (wrong column order).
Rule: Most selective column first (CustomerId more unique than Status)

💣 Killer #4: Functions on Indexed Columns

❌ SLOW (can't use index - applies UPPER to every row)
SELECT * FROM Users 
WHERE UPPER(Email) = 'USER@EXAMPLE.COM';

Index on Email is useless!

✅ FAST (uses index)
-- Store email lowercase, search lowercase
SELECT * FROM Users 
WHERE Email = 'user@example.com';

-- Or use computed column with index
ALTER TABLE Users ADD EmailLower AS LOWER(Email) PERSISTED;
CREATE INDEX IX_Users_EmailLower ON Users(EmailLower);

SELECT * FROM Users WHERE EmailLower = 'user@example.com';

💣 Killer #5: OR Conditions

❌ SLOW (can't efficiently use indexes)
SELECT * FROM Products 
WHERE CategoryId = 5 OR Price > 1000;

Database must scan entire table

✅ FAST (use UNION - separate index seeks)
SELECT * FROM Products WHERE CategoryId = 5
UNION
SELECT * FROM Products WHERE Price > 1000;

Each query uses its own index efficiently!
Problem Before Fix After Fix Improvement
SELECT * 5 seconds 0.05s 100x
N+1 Queries 10 seconds 0.1s 100x
Missing Index 8 seconds 0.02s 400x
Function on Column 6 seconds 0.05s 120x
OR Conditions 4 seconds 0.1s 40x

🔍 How to Find Slow Queries

-- SQL Server: Find top 10 slowest queries
SELECT TOP 10
    total_elapsed_time / execution_count / 1000 AS avg_ms,
    execution_count,
    SUBSTRING(st.text, (qs.statement_start_offset/2)+1,
        ((CASE qs.statement_end_offset
            WHEN -1 THEN DATALENGTH(st.text)
            ELSE qs.statement_end_offset
        END - qs.statement_start_offset)/2) + 1) AS query_text
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) st
ORDER BY total_elapsed_time / execution_count DESC;

-- Check if query is using indexes (Execution Plan)
SET STATISTICS IO ON;
SELECT * FROM Orders WHERE CustomerId = 123;
-- Look for "Table Scan" (bad) vs "Index Seek" (good)

💡 Quick Wins Checklist

  • ✓ Index foreign keys (90% of JOIN columns need indexes)
  • ✓ Index WHERE clause columns (especially in frequent queries)
  • ✓ Use EXPLAIN/Execution Plan on every slow query
  • ✓ Monitor database metrics (CPU, disk I/O, wait stats)
  • ✓ Set query timeout (kill runaway queries)
  • ✓ Enable slow query log (catch problems early)

“Our database was dying. Added 3 indexes, rewrote 5 queries. Database CPU dropped from 95% to 20%. Crisis averted in 45 minutes. These 5 tricks literally saved our jobs.”

— Database Administrator, SaaS Company
⚠️ Don’t Over-Index!

Every index speeds up reads but slows down writes. Too many indexes:

  • Slows INSERT/UPDATE/DELETE (each must update all indexes)
  • Uses disk space (each index = copy of columns)
  • Confuses query optimizer (too many choices)

Rule of thumb: 3-5 indexes per table max. Index ONLY frequently-queried columns.

Related posts:

Partial Indexes Save Space

Get the First and Last Word from a String or Sentence in SQL

SQL Query to Find and Replace text in a stored procedures

Post Views: 3

Post navigation

.NET Core Background Services: Running Tasks Without Blocking Your API
C# LINQ: The Performance Traps That Cost Us 90% Speed (And How to Fix Them)

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

April 2026
M T W T F S S
 12345
6789101112
13141516171819
20212223242526
27282930  
« Mar    

Most Viewed Posts

  • Get the User Name and Domain Name from an Email Address in SQL (950)
  • How to add default value for Entity Framework migrations for DateTime and Bool (858)
  • Get the First and Last Word from a String or Sentence in SQL (836)
  • How to select distinct rows in a datatable in C# (805)
  • How to make theater mode the default for Youtube (752)
  • Add Constraint to SQL Table to ensure email contains @ (578)
  • How to enable, disable and check if Service Broker is enabled on a database in SQL Server (564)
  • Average of all values in a column that are not zero in SQL (531)
  • How to use Map Mode for Vertical Scroll Mode in Visual Studio (489)
  • Find numbers with more than two decimal places in SQL (447)

Recent Posts

  • C#: Use Init-Only Setters for Immutable Objects After Construction
  • C#: Use Expression-Bodied Members for Concise Single-Line Methods
  • C#: Enable Nullable Reference Types to Eliminate Null Reference Exceptions
  • C#: Use Record Types for Immutable Data Objects
  • SQL: Use CTEs for Readable Complex Queries
  • SQL: Use Window Functions for Advanced Analytical Queries
  • .NET Core: Use Background Services for Long-Running Tasks
  • .NET Core: Use Minimal APIs for Lightweight HTTP Services
  • Git: Use Cherry-Pick to Apply Specific Commits Across Branches
  • Git: Use Interactive Rebase to Clean Up Commit History Before Merge

Most Viewed Posts

  • Get the User Name and Domain Name from an Email Address in SQL (950)
  • How to add default value for Entity Framework migrations for DateTime and Bool (858)
  • Get the First and Last Word from a String or Sentence in SQL (836)
  • How to select distinct rows in a datatable in C# (805)
  • How to make theater mode the default for Youtube (752)

Recent Posts

  • C#: Use Init-Only Setters for Immutable Objects After Construction
  • C#: Use Expression-Bodied Members for Concise Single-Line Methods
  • C#: Enable Nullable Reference Types to Eliminate Null Reference Exceptions
  • C#: Use Record Types for Immutable Data Objects
  • SQL: Use CTEs for Readable Complex Queries

Social

  • ErcanOPAK.com
  • GoodReads
  • LetterBoxD
  • Linkedin
  • The Blog
  • Twitter
© 2026 Bits of .NET | Built with Xblog Plus free WordPress theme by wpthemespace.com