Tối Ưu Hóa Câu Lệnh SELECT Trong SQL Server
· 5 min read
Giới thiệu
Tối ưu hóa câu lệnh SELECT là một trong những kỹ năng quan trọng nhất của một DBA hoặc developer làm việc với SQL Server. Trong bài viết này, chúng ta sẽ tìm hiểu các kỹ thuật và best practices để tối ưu hóa câu lệnh SELECT, giúp cải thiện hiệu suất truy vấn và giảm tải cho hệ thống.
1. Sử dụng Index hiệu quả
1.1. Tạo Index phù hợp
-- Tạo index cho cột thường xuyên được sử dụng trong WHERE
CREATE INDEX IX_Customers_Email ON Customers(Email);
-- Tạo composite index cho nhiều cột
CREATE INDEX IX_Orders_CustomerDate ON Orders(CustomerID, OrderDate);
1.2. Tránh Index Scan
-- Không tốt: Sẽ scan toàn bộ index
SELECT * FROM Customers WHERE Email LIKE '%@gmail.com';
-- Tốt hơn: Sử dụng điều kiện chính xác
SELECT * FROM Customers WHERE Email = 'example@gmail.com';
2. Tối ưu hóa JOIN
2.1. Sử dụng INNER JOIN thay vì LEFT JOIN khi có thể
-- Không tốt
SELECT o.OrderID, c.CustomerName
FROM Orders o
LEFT JOIN Customers c ON o.CustomerID = c.CustomerID;
-- Tốt hơn
SELECT o.OrderID, c.CustomerName
FROM Orders o
INNER JOIN Customers c ON o.CustomerID = c.CustomerID;
2.2. Thứ tự JOIN
-- Tốt: Bắt đầu với bảng có ít dữ liệu nhất
SELECT o.OrderID, c.CustomerName, p.ProductName
FROM OrderDetails od
INNER JOIN Orders o ON od.OrderID = o.OrderID
INNER JOIN Customers c ON o.CustomerID = c.CustomerID
INNER JOIN Products p ON od.ProductID = p.ProductID;
3. Sử dụng SELECT hiệu quả
3.1. Chỉ SELECT các cột cần thiết
-- Không tốt
SELECT * FROM Customers;
-- Tốt hơn
SELECT CustomerID, CustomerName, Email FROM Customers;
3.2. Sử dụng TOP với ORDER BY
-- Tốt: Sử dụng TOP với ORDER BY
SELECT TOP 10 OrderID, OrderDate, TotalAmount
FROM Orders
ORDER BY OrderDate DESC;
4. Tối ưu hóa WHERE
4.1. Sử dụng điều kiện SARGable
-- Không tốt: Không SARGable
SELECT * FROM Orders
WHERE YEAR(OrderDate) = 2024;
-- Tốt hơn: SARGable
SELECT * FROM Orders
WHERE OrderDate >= '2024-01-01' AND OrderDate < '2025-01-01';
4.2. Tránh sử dụng hàm trong WHERE
-- Không tốt
SELECT * FROM Products
WHERE LOWER(ProductName) = 'laptop';
-- Tốt hơn
SELECT * FROM Products
WHERE ProductName = 'Laptop';
5. Sử dụng Common Table Expressions (CTE)
WITH MonthlySales AS (
SELECT
YEAR(OrderDate) AS Year,
MONTH(OrderDate) AS Month,
SUM(TotalAmount) AS TotalSales
FROM Orders
GROUP BY YEAR(OrderDate), MONTH(OrderDate)
)
SELECT
Year,
Month,
TotalSales,
AVG(TotalSales) OVER (ORDER BY Year, Month ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS MovingAverage
FROM MonthlySales;
6. Tối ưu hóa Subquery
6.1. Sử dụng EXISTS thay vì IN
-- Không tốt
SELECT CustomerID, CustomerName
FROM Customers
WHERE CustomerID IN (SELECT CustomerID FROM Orders);
-- Tốt hơn
SELECT CustomerID, CustomerName
FROM Customers c
WHERE EXISTS (SELECT 1 FROM Orders o WHERE o.CustomerID = c.CustomerID);
6.2. Sử dụng JOIN thay vì Subquery
-- Không tốt
SELECT
CustomerID,
CustomerName,
(SELECT COUNT(*) FROM Orders WHERE Orders.CustomerID = Customers.CustomerID) AS OrderCount
FROM Customers;
-- Tốt hơn
SELECT
c.CustomerID,
c.CustomerName,
COUNT(o.OrderID) AS OrderCount
FROM Customers c
LEFT JOIN Orders o ON c.CustomerID = o.CustomerID
GROUP BY c.CustomerID, c.CustomerName;
7. Sử dụng Table Variables và Temporary Tables
7.1. Table Variables
DECLARE @TempOrders TABLE (
OrderID INT,
CustomerID INT,
OrderDate DATE
);
INSERT INTO @TempOrders
SELECT OrderID, CustomerID, OrderDate
FROM Orders
WHERE OrderDate >= DATEADD(MONTH, -1, GETDATE());
7.2. Temporary Tables
CREATE TABLE #TempOrders (
OrderID INT,
CustomerID INT,
OrderDate DATE
);
INSERT INTO #TempOrders
SELECT OrderID, CustomerID, OrderDate
FROM Orders
WHERE OrderDate >= DATEADD(MONTH, -1, GETDATE());
8. Sử dụng Execution Plan
8.1. Phân tích Execution Plan
-- Bật Execution Plan
SET SHOWPLAN_TEXT ON;
GO
-- Truy vấn cần phân tích
SELECT o.OrderID, c.CustomerName, p.ProductName
FROM Orders o
INNER JOIN Customers c ON o.CustomerID = c.CustomerID
INNER JOIN Products p ON o.ProductID = p.ProductID
WHERE o.OrderDate >= '2024-01-01';
-- Tắt Execution Plan
SET SHOWPLAN_TEXT OFF;
GO
9. Best Practices
-
Sử dụng Stored Procedures
- Tái sử dụng code
- Tối ưu hóa execution plan
- Bảo mật tốt hơn
-
Tránh CURSOR
- Sử dụng set-based operations
- Hiệu suất tốt hơn
- Code dễ bảo trì hơn
-
Sử dụng Parameter Sniffing
- Tối ưu hóa execution plan
- Tránh recompilation không cần thiết
-
Maintenance
- Cập nhật statistics thường xuyên
- Rebuild index định kỳ
- Monitor query performance
Kết luận
Tối ưu hóa câu lệnh SELECT trong SQL Server là một quá trình liên tục. Bằng cách áp dụng các kỹ thuật và best practices được đề cập trong bài viết này, bạn có thể cải thiện đáng kể hiệu suất của các truy vấn và giảm tải cho hệ thống.
Tài liệu tham khảo
- Microsoft SQL Server Documentation
- "SQL Server Performance Tuning" - Grant Fritchey
- "SQL Server Query Performance Tuning" - Sajal Dam
- "Pro SQL Server 2019 Administration" - Peter A. Carter