Advanced Database Lab-Query Processing and Optimization (Prepared By: Daniel T)
Part-I
Step-by-Step Lab Activity
Objective
- Understanding what database performance
- Demonstrating performance improvement techniques
Scenario: E-Commerce Order Management System
A new e-commerce platform needs a database to track customers, products, and
orders. We will:
1. Design the database (tables & relationships).
2. Insert sample data.
3. Write queries (some inefficient, some optimized).
4. Analyze execution plans.
5. Apply optimization techniques (indexing, query rewriting, etc.).
General Tasks:
1. Create indexes on frequently queried columns.
2. Compare query performance before and after indexing.
3. Use Execution Plan to verify improvements.
1|Page
Advanced Database Lab-Query Processing and Optimization
Step 1: Database & Table Creation
Task: Create the database and tables
-- Create the database
CREATE DATABASE ECommerceDB;
GO
USE ECommerceDB;
GO
-- Create Customers table
CREATE TABLE Customers (
CustomerID INT PRIMARY KEY IDENTITY(1,1),
FirstName NVARCHAR(50),
LastName NVARCHAR(50),
Email NVARCHAR(100) UNIQUE,
RegistrationDate DATE
);
-- Create Products table
CREATE TABLE Products (
ProductID INT PRIMARY KEY IDENTITY(1,1),
ProductName NVARCHAR(100),
Category NVARCHAR(50),
Price DECIMAL(10, 2),
StockQuantity INT
);
-- Create Orders table
CREATE TABLE Orders (
OrderID INT PRIMARY KEY IDENTITY(1,1),
CustomerID INT FOREIGN KEY REFERENCES Customers(CustomerID),
OrderDate DATETIME,
TotalAmount DECIMAL(10, 2)
);
-- Create OrderDetails table
CREATE TABLE OrderDetails (
OrderDetailID INT PRIMARY KEY IDENTITY(1,1),
OrderID INT FOREIGN KEY REFERENCES Orders(OrderID),
ProductID INT FOREIGN KEY REFERENCES Products(ProductID),
Quantity INT,
UnitPrice DECIMAL(10, 2)
);
2|Page
Advanced Database Lab-Query Processing and Optimization
Step 2: Insert Sample Data
Task: Populate tables with test data
-- Insert Customers
INSERT INTO Customers (FirstName, LastName, Email, RegistrationD
ate)
VALUES
('John', 'Doe', '[email protected]', '2023-01-15'),
('Jane', 'Smith', '[email protected]', '2023-02-20'),
('Alice', 'Johnson', '[email protected]', '2023-03-10');
-- Insert Products
INSERT INTO Products (ProductName, Category, Price, StockQuantit
y)
VALUES
('Laptop', 'Electronics', 999.99, 50),
('Smartphone', 'Electronics', 699.99, 100),
('Headphones', 'Accessories', 99.99, 200);
-- Insert Orders
INSERT INTO Orders (CustomerID, OrderDate, TotalAmount)
VALUES
(1, '2023-04-01', 999.99),
(2, '2023-04-02', 799.98),
(3, '2023-04-03', 99.99);
-- Insert OrderDetails
INSERT INTO OrderDetails (OrderID, ProductID, Quantity, UnitPric
e)
VALUES
(1, 1, 1, 999.99),
(2, 2, 1, 699.99),
(2, 3, 1, 99.99),
(3, 3, 1, 99.99);
3|Page
Advanced Database Lab-Query Processing and Optimization
Step 3: Write & Analyze Queries (Before Optimization)
Query 1: Find Orders with Product Details (Inefficient)
-- Slow query (no indexes, uses scans)
SELECT
c.FirstName,
c.LastName,
o.OrderID,
o.OrderDate,
p.ProductName,
od.Quantity,
od.UnitPrice
FROM
Customers c
JOIN
Orders o ON c.CustomerID = o.CustomerID
JOIN
OrderDetails od ON o.OrderID = od.OrderID
JOIN
Products p ON od.ProductID = p.ProductID
WHERE
p.Category = 'Electronics';
Problem:
• Table scans on all joined tables.
• No indexes for WHERE p.Category = 'Electronics' .
Step 4: Optimize the Query
Solution 1: Add Indexes
-- Index on Category (speeds up WHERE clause)
CREATE INDEX IX_Products_Category ON Products(Category);
-- Index on CustomerID (speeds up JOIN)
CREATE INDEX IX_Orders_CustomerID ON Orders(CustomerID);
-- Index on OrderID (speeds up JOIN)
CREATE INDEX IX_OrderDetails_OrderID ON OrderDetails(OrderID);
-- Index on ProductID (speeds up JOIN)
CREATE INDEX IX_OrderDetails_ProductID ON OrderDetails(ProductID);
4|Page
Advanced Database Lab-Query Processing and Optimization
Solution 2: Rewrite the Query (If Needed)
-- Optimized query (uses indexes)
SELECT
c.FirstName,
c.LastName,
o.OrderID,
o.OrderDate,
p.ProductName,
od.Quantity,
od.UnitPrice
FROM
Orders o
JOIN
Customers c ON o.CustomerID = c.CustomerID
JOIN
OrderDetails od ON o.OrderID = od.OrderID
JOIN
Products p ON od.ProductID = p.ProductID
WHERE
p.Category = 'Electronics';
Improvements:
• Uses index seeks instead of scans.
• Faster JOINs due to indexed columns.
Step 5: Verify Optimization
Task: Compare Execution Plans
1. Run the original query (check for scans).
2. Run the optimized query (check for seeks).
3. Verify reduced logical reads in SSMS.
5|Page
Advanced Database Lab-Query Processing and Optimization
Step 6: Advanced Optimization (Optional)
Query 2: Aggregation Without Index (Slow)
-- Find total sales per category (slow)
SELECT
p.Category,
SUM(od.Quantity * od.UnitPrice) AS TotalSales
FROM
OrderDetails od
JOIN
Products p ON od.ProductID = p.ProductID
GROUP BY
p.Category;
Solution: Add a Covering Index
-- Covering index for aggregation
CREATE INDEX IX_OrderDetails_ProductID_Covering
ON OrderDetails(ProductID)
INCLUDE (Quantity, UnitPrice);
-- Re-run the query (now uses index)
Why It Works:
• Avoids key lookups by including all needed columns.
Step 7: Final Performance Check
Task: Use DMVs to Monitor Queries
-- Check top CPU-intensive queries
SELECT TOP 5
qs.execution_count,
qs.total_worker_time,
SUBSTRING(qt.text, (qs.statement_start_offset/2)+1,
((CASE qs.statement_end_offset
WHEN -1 THEN DATALENGTH(qt.text)
ELSE qs.statement_end_offset
END - qs.statement_start_offset)/2)+1) AS QueryText
FROM
sys.dm_exec_query_stats qs
CROSS APPLY
sys.dm_exec_sql_text(qs.sql_handle) AS qt
ORDER BY
qs.total_worker_time DESC;
6|Page
Advanced Database Lab-Query Processing and Optimization
Part-II
o Note: The Exercises below are prepared Based on the Database
AdvanureWorks2019, you can use 2014, 2016 or earlier versions of Database.
Exercise 1: Query Rewriting: Join Reordering
• Joining smaller tables first reduces intermediate result size.
• Original (large table first)
USE AdventureWorks2019
SELECT * FROM Sales.SalesOrderHeader soh
JOIN Sales.SalesOrderDetail sod ON soh.SalesOrderID =
sod.SalesOrderID;
Original Execution Plan Result
o Check the cost of each Parse Tree-node and compare with the
optimized one, what did you observe?
o Check the time difference from the actual execution plan in second.
o You might observe different result based
-- Optimized (filter first, then join)
SELECT * FROM Sales.SalesOrderDetail sod
JOIN Sales.SalesOrderHeader soh ON sod.SalesOrderID =
soh.SalesOrderID;
Optimized Execution Plan Result
7|Page
Advanced Database Lab-Query Processing and Optimization
Exercise 2: Query Rewriting: Avoiding functions on indexed columns
o Less optimal Query (can't use index on OrderDate)
SELECT SalesOrderID, OrderDate
FROM Sales.SalesOrderHeader
WHERE YEAR(OrderDate) = 2011 AND MONTH(OrderDate) = 6;
Execution Plan Result
Optimized Query
SELECT SalesOrderID, OrderDate
FROM Sales.SalesOrderHeader
WHERE OrderDate BETWEEN '2011-06-01' AND '2011-06-30';
Execution Plan Result from Optimized Query
--Exercise 3: Cost-Based (Index) Optimization
--Compare execution plans for:
-- Query Without index (not Optimized Query)
--Note: check if there exist an index on the Person.Person table of
Column LastName and disable it first before you execute the given
query.
SELECT * FROM Person.Person WHERE LastName =
'Smith';
Execution Plan Result from the Query without index is as shown below.
So, take a note of the result and compare its’s result with the next
query result with index.
8|Page
Advanced Database Lab-Query Processing and Optimization
o After creating an index
o Please note that first you need to disable the existing index on
LastName and FirstName by navigation to Person.Person table index.
In then create the index
CREATE INDEX IX_Person_LastName ON Person.Person(LastName);
SELECT * FROM Person.Person WHERE LastName = 'Smith';
Exercise 4: Join Optimization: Using proper join types
o The Qurty given below uses nested query too optimize the given
query, we need to use a proper join statement
9|Page
Advanced Database Lab-Query Processing and Optimization
o Please observe the result you will get by executing the two
queries and check their cost difference.
SELECT p.Name, p.ListPrice
FROM Production.Product p
WHERE p.ProductSubcategoryID IN (
SELECT ProductSubcategoryID
FROM Production.ProductSubcategory
WHERE Name = 'Mountain Bikes'
);
o Optimized Query (Run the Queries Sequentially and Observe their
Execution Plan
o Optimized with JOIN
SELECT p.Name, p.ListPrice
FROM Production.Product p
JOIN Production.ProductSubcategory ps
ON p.ProductSubcategoryID = ps.ProductSubcategoryID
WHERE ps.Name = 'Mountain Bikes';
10 | P a g e