SELECT SQL Server - Truy vấn dữ liệu siêu tốc, tránh lỗi hiệu năng

Lệnh SELECT trong SQL Server là một trong những câu lệnh cơ bản nhất nhưng cũng cực kỳ quan trọng đối với bất kỳ nhà phát triển hay quản trị viên cơ sở dữ liệu nào. Nắm vững SELECT không chỉ giúp bạn trích xuất dữ liệu mà còn là chìa khóa để tối ưu hóa hiệu suất ứng dụng.

Nếu bạn đang làm việc với SQL Server, việc hiểu rõ cách thức hoạt động và các tùy chọn của SELECT là điều bắt buộc.

Hiểu về dữ liệu và bảng trong SQL Server

Trong SQL Server, dữ liệu được tổ chức một cách logic trong các bảng. Mỗi bảng là một tập hợp các hàng (records) và cột (fields), tương tự như một bảng tính. Mỗi hàng đại diện cho một bản ghi duy nhất, trong khi mỗi cột đại diện cho một thuộc tính cụ thể của bản ghi đó.

Ví dụ, một bảng `Customers` có thể chứa các cột như `CustomerID`, `FirstName`, `LastName`, `Email`, và `Address`. Các lược đồ (schemas) được sử dụng để nhóm các bảng và đối tượng cơ sở dữ liệu khác một cách hợp lý, giúp quản lý và tổ chức dữ liệu dễ dàng hơn. Chẳng hạn, schema `sales` có thể chứa các bảng liên quan đến doanh số, còn `production` chứa các bảng liên quan đến sản xuất.

Lệnh SELECT trong SQL Server

Để tương tác và trích xuất dữ liệu từ các bảng này, chúng ta sử dụng câu lệnh SELECT.

  • Dữ liệu thường rất lớn và phân tán.
  • Dữ liệu được lưu trữ có cấu trúc trong các bảng, với hàng và cột rõ ràng.
  • Schemas giúp tổ chức các bảng theo nhóm logic.
  • Câu lệnh SQL, đặc biệt là SELECT, là công cụ chính để truy vấn và trích xuất dữ liệu.

Cú pháp cơ bản của lệnh SELECT

Cú pháp cơ bản nhất của SELECT khá đơn giản:

select
select_list;
from
schema_name.table.name;

Để lấy tất cả các cột từ một bảng, bạn có thể dùng dấu `*`:

Syntax -
select*
from
table_name;

Lưu ý quan trọng về SELECT *:

  • Trong môi trường sản phẩm thực tế, việc sử dụng `SELECT *` thường không được khuyến khích. Nó truy xuất tất cả dữ liệu, bao gồm cả những cột không cần thiết, dẫn đến lãng phí tài nguyên mạng và bộ nhớ.
  • Kết quả là ứng dụng của bạn có thể chạy chậm hơn đáng kể.
  • Nếu sau này có thêm cột mới vào bảng, `SELECT *` sẽ tự động truy xuất cả các cột đó, có thể gây ra lỗi hoặc hành vi không mong muốn trong ứng dụng nếu mã nguồn không được cập nhật để xử lý chúng. Luôn chỉ định rõ ràng các cột bạn cần.

Ở dạng đơn giản, cú pháp của lệnh SELECT như sau:

SELECT “biểu thức”
 FROM “bảng”
 [WHERE “điều kiện”];

Ở dạng đầy đủ, cú pháp của lệnh SELECT trong SQL Server sẽ bao gồm nhiều tùy chọn mạnh mẽ hơn:

SELECT [ ALL | DISTINCT ]
[ TOP (gia_tri_dau) [ PERCENT ] [ WITH TIES ] ]
“Biểu thức”
FROM “bảng”
[WHERE “điều kiện”]
[GROUP BY “biểu thức”]
[HAVING “điều kiện”]
[ORDER BY “biểu thức” [ ASC | DESC ]];

Giải thích các thành phần:

  • ALL: Mặc định, trả về tất cả các hàng phù hợp, bao gồm cả các giá trị trùng lặp.
  • DISTINCT: Lọc bỏ tất cả các giá trị trùng lặp khỏi tập kết quả, chỉ giữ lại các giá trị duy nhất.
  • TOP (gia_tri_dau): Giới hạn số lượng hàng trả về. Ví dụ, `TOP(10)` sẽ trả về 10 hàng đầu tiên.
  • PERCENT: Dùng kết hợp với `TOP`, trả về số hàng dựa trên tỷ lệ phần trăm của tổng số hàng. Ví dụ, `TOP(10) PERCENT` sẽ trả về 10% số hàng đầu tiên.
  • WITH TIES: Tùy chọn này chỉ có ý nghĩa khi dùng với `TOP` và `ORDER BY`. Nếu có các hàng có cùng giá trị trong cột `ORDER BY` tại điểm cắt của `TOP`, `WITH TIES` sẽ bao gồm tất cả các hàng đó, ngay cả khi nó làm tăng tổng số hàng vượt quá giá trị `TOP` ban đầu.
  • Biểu thức: Đại diện cho các cột hoặc các giá trị tính toán mà bạn muốn lấy về. Dùng `*` để chọn tất cả các cột.
  • Bảng: Bảng hoặc các bảng mà bạn muốn truy vấn dữ liệu. Ít nhất một bảng phải được liệt kê trong mệnh đề `FROM`.
  • WHERE “điều kiện”: Lọc các hàng dựa trên một hoặc nhiều điều kiện đã cho. Chỉ những hàng thỏa mãn điều kiện mới được đưa vào tập kết quả.
  • GROUP BY “biểu thức”: Nhóm các hàng có cùng giá trị trong một hoặc nhiều cột thành một nhóm duy nhất. Thường được sử dụng với các hàm tổng hợp (COUNT, SUM, AVG, MAX, MIN).
  • HAVING “điều kiện”: Tương tự như `WHERE` nhưng được áp dụng cho các nhóm dữ liệu được tạo bởi `GROUP BY`. Dùng để lọc các nhóm dựa trên các điều kiện tổng hợp.
  • ORDER BY “biểu thức”: Sắp xếp tập kết quả theo thứ tự tăng dần (`ASC`) hoặc giảm dần (`DESC`) dựa trên một hoặc nhiều cột. `ASC` là mặc định.

Ví dụ thực tế về lệnh SELECT trong SQL Server

Hãy cùng xem xét các ví dụ cụ thể để hiểu rõ hơn về cách sử dụng lệnh SELECT. Giả sử chúng ta có bảng dữ liệu Tech Hacks như sau:


+-------------+--------------+-------------+-------+
|IDChuyenmuc  |Muccon        |Chuyenmuclon |Sobai  |
+-------------+--------------+-------------+-------+
|    1        |SQL Server    |Lap trinh    |101    |
|    2        |Facebook      |Mang xa hoi  |152    |
|    3        |Python        |Lap trinh    |111    |
|    4        |JavaScript    |Lap trinh    |122    |
|    5        |Chrome        |Web          |94     |
|    6        |Instagram     |Mang xa hoi  |165    |
+-------------+--------------+-------------+-------+

Chọn tất cả các trường trong bảng với điều kiện

Ví dụ này sử dụng `SELECT *` để lấy tất cả các cột từ bảng Tech Hacks, lọc ra những bản ghi có `Sobai` lớn hơn 111 và sắp xếp kết quả theo `Sobai` tăng dần.

SELECT * FROM [Tech Hacks]
  WHERE Sobai > 111
  ORDER BY Sobai ASC;

Kết quả sẽ là:


+-------------+--------------+-------------+-------+
|IDChuyenmuc  |Muccon        |Chuyenmuclon |Sobai  |
+-------------+--------------+-------------+-------+
|    4        |JavaScript    |Lap trinh    |122    |
|    2        |Facebook      |Mang xa hoi  |152    |
|    6        |Instagram     |Mang xa hoi  |165    |
+-------------+--------------+-------------+-------+

Chọn một số trường cụ thể với nhiều điều kiện

Ở ví dụ này, chúng ta chỉ chọn các cột `IDChuyenmuc`, `Chuyenmuclon`, `Sobai`. Các điều kiện lọc bao gồm `IDChuyenmuc` lớn hơn 2 VÀ `Chuyenmuclon` là 'Lap trinh'. Kết quả được sắp xếp theo `Sobai` giảm dần.

SELECT IDChuyenmuc, Chuyenmuclon, Sobai
  FROM [Tech Hacks]
  WHERE IDChuyenmuc > 2
  AND Chuyenmuclon = 'Lap trinh'
  Order By Sobai DESC;

Kết quả trả về:


+-------------+-------------+-------+
|IDChuyenmuc  |Chuyenmuclon |Sobai  |
+-------------+-------------+-------+
|    4        |Lap trinh    |122    |
|    3        |Lap trinh    |111    |
+-------------+-------------+-------+

Kết hợp dữ liệu từ nhiều bảng (JOIN)

Để minh họa việc truy vấn từ nhiều bảng, chúng ta thêm bảng Muc:


+----+-----------+-------------+
| ID |Ten muc    |Trang thai   |
+----+-----------+-------------+
| 1  |Lap trinh  |Hien         |
| 2  |Mang xa hoi|An           |
| 3  |Web        |An           |
+----+-----------+-------------+

Yêu cầu là lấy `Muccon` và `Chuyenmuclon` từ bảng Tech Hacks, cùng với `Trangthai` từ bảng Muc. Các bản ghi phải thỏa mãn điều kiện `Chuyenmuclon` của Tech Hacks trùng với `Tenmuc` của Muc VÀ `Trangthai` của Muc phải là 'Hien'. Kết quả được sắp xếp theo `Muccon` tăng dần.

SELECT [Tech Hacks].Muccon,[Tech Hacks].Chuyenmuclon,Muc.Trangthai
  FROM [Tech Hacks]
  INNER JOIN Muc
  ON [Tech Hacks].Chuyenmuclon=Muc.Tenmuc AND Muc.Trangthai= 'Hien'
  ORDER BY [Tech Hacks].Muccon ASC;

Kết quả trả về:


+----------+------------+----------+
|Muccon    |Chuyenmuclon|Trangthai |
+----------+------------+----------+
|Chrome    |Web         |Hien      |
|JavaScript|Lap trinh   |Hien      |
|Python    |Lap trinh   |Hien      |
|SQL Server|Lap trinh   |Hien      |
+----------+------------+----------+

Sử dụng từ khóa TOP để giới hạn kết quả

Ví dụ này sử dụng `TOP(2)` để chỉ trả về 2 hàng đầu tiên từ bảng Tech Hacks mà `Chuyenmuclon` là 'Lap trinh', được sắp xếp theo `IDChuyenmuc` tăng dần. Các hàng khác, dù thỏa mãn điều kiện, cũng sẽ bị bỏ qua.

SELECT TOP(2) *
  FROM [Tech Hacks]
  WHERE Chuyenmuclon='Lap trinh'
  ORDER BY IDChuyenmuc ASC;

Kết quả:


+-------------+--------------+-------------+-------+
|IDChuyenmuc  |Muccon        |Chuyenmuclon |Sobai  |
+-------------+--------------+-------------+-------+
|    1        |SQL Server    |Lap trinh    |101    |
|    3        |Python        |Lap trinh    |111    |
+-------------+--------------+-------------+-------+

Sử dụng từ khóa TOP PERCENT

Tương tự như `TOP`, nhưng `TOP PERCENT` trả về một phần trăm nhất định của tổng số hàng. Ở đây, chúng ta lấy 10% số hàng đầu tiên từ bảng Tech Hacks với `Chuyenmuclon` là ‘Lap trinh’, sắp xếp theo `IDChuyenmuc` tăng dần.

SELECT TOP(10) PERCENT *
  FROM [Tech Hacks]
  WHERE Chuyenmuclon='Lap trinh'
  ORDER BY IDChuyenmuc ASC;

Kết quả:


+-------------+--------------+-------------+-------+
|IDChuyenmuc  |Muccon        |Chuyenmuclon |Sobai  |
+-------------+--------------+-------------+-------+
|    1        |SQL Server    |Lap trinh    |101    |
+-------------+--------------+-------------+-------+

Dùng DISTINCT với SELECT để loại bỏ trùng lặp

Từ khóa `DISTINCT` rất hữu ích khi bạn muốn lấy danh sách các giá trị duy nhất từ một cột. Ví dụ sau sẽ trả về danh sách các chức danh công việc (`JobTitle`) duy nhất từ bảng `HumanResources.Employee` trong cơ sở dữ liệu `AdventureWorks2012`.

USE AdventureWorks2012;
GO
SELECT DISTINCT JobTitle
FROM HumanResources.Employee
ORDER BY JobTitle;
GO

Tạo bảng mới với SELECT INTO

Lệnh `SELECT INTO` cho phép bạn tạo một bảng mới và điền dữ liệu vào đó dựa trên kết quả của một truy vấn SELECT. Đây là một cách nhanh chóng để sao chép dữ liệu hoặc tạo bảng tạm thời.

Ví dụ đầu tiên tạo một bảng tạm thời có tên `#Bicycles` trong `tempdb`.

USE tempdb;
GO
IF OBJECT_ID (N'#Bicycles',N'U') IS NOT NULL
DROP TABLE #Bicycles;
GO
SELECT * 
INTO #Bicycles
FROM AdventureWorks2012.Production.Product
WHERE ProductNumber LIKE 'BK%';
GO

Ví dụ thứ hai tạo một bảng cố định có tên `NewProducts` trong cơ sở dữ liệu `AdventureWorks2012`.

USE AdventureWorks2012;
GO
IF OBJECT_ID('dbo.NewProducts', 'U') IS NOT NULL
    DROP TABLE dbo.NewProducts;
GO
ALTER DATABASE AdventureWorks2012 SET RECOVERY BULK_LOGGED;
GO

SELECT * INTO dbo.NewProducts
FROM Production.Product
WHERE ListPrice > $25 
AND ListPrice < $100;
GO
ALTER DATABASE AdventureWorks2012 SET RECOVERY FULL;
GO

Những câu hỏi thường gặp về lệnh SELECT trong SQL Server

SELECT * và SELECT column_name: Khác biệt và ý nghĩa hiệu năng

Sự khác biệt cơ bản nằm ở lượng dữ liệu được truy xuất và tác động đến hiệu suất. `SELECT *` sẽ lấy tất cả các cột của bảng, bao gồm cả những cột bạn không cần. Điều này có thể gây ra gánh nặng không cần thiết cho mạng và bộ nhớ, làm chậm ứng dụng, đặc biệt với các bảng lớn. Ngược lại, `SELECT column_name` chỉ định chính xác các cột bạn muốn, tối ưu hóa việc truyền tải dữ liệu và cải thiện đáng kể hiệu suất truy vấn.

Có thể dùng mệnh đề ORDER BY trong lệnh SELECT không?

Chắc chắn rồi. Mệnh đề `ORDER BY` là một phần không thể thiếu của câu lệnh SELECT, được dùng để sắp xếp các hàng trong tập kết quả theo một hoặc nhiều cột được chỉ định. Bạn có thể sắp xếp theo thứ tự tăng dần (`ASC`) hoặc giảm dần (`DESC`).

Mục đích của mệnh đề GROUP BY trong lệnh SELECT là gì?

Mệnh đề `GROUP BY` được sử dụng để nhóm các hàng có cùng giá trị trong một hoặc nhiều cột thành các nhóm tóm tắt. Nó thường đi kèm với các hàm tổng hợp như `COUNT`, `SUM`, `AVG`, `MAX`, và `MIN` để thực hiện các phép tính trên mỗi nhóm. Ví dụ, `GROUP BY Course` sẽ nhóm tất cả các sinh viên theo khóa học của họ, cho phép bạn tính số lượng sinh viên hoặc điểm trung bình cho từng khóa học.