DISTINCT trong SQL: Không chỉ là lọc trùng, đó là cả một nghệ thuật tối ưu!
1. Bản chất của DISTINCT
Về mặt định nghĩa, DISTINCT là một chỉ thị (modifier) đi sau SELECT, ra lệnh cho Database Engine loại bỏ tất cả các dòng trùng lặp trong tập kết quả (Result Set) và chỉ giữ lại duy nhất một dòng đại diện.
Cú pháp cơ bản:
SELECT DISTINCT column1, column2 FROM table_name;
Lưu ý quan trọng: DISTINCT tác động lên toàn bộ các cột bạn khai báo trong SELECT. Không có chuyện bạn viết SELECT DISTINCT col1, col2 mà nó chỉ lọc trùng cho mỗi col1 đâu nhé!
2. DISTINCT xử lý giá trị NULL như thế nào?
Trong SQL, NULL đại diện cho một giá trị chưa xác định. Một quy tắc "bất thành văn" là NULL không bằng chính nó (NULL <> NULL).
Tuy nhiên, với lệnh DISTINCT, Database lại đối xử với các giá trị NULL như là các giá trị bằng nhau. Nếu cột bạn chọn có 10 dòng NULL, DISTINCT sẽ gom tất cả lại và chỉ trả về duy nhất một dòng NULL.
3. "Dưới nắp ca-pô": Database làm gì khi bạn gọi DISTINCT?
Đây là phần mà các bạn Senior thường quan tâm. Để lọc được các dòng trùng lặp, Database không thể "nhìn cái là thấy ngay". Nó thường sử dụng một trong hai chiến lược sau:
- Sort-Unique (Sắp xếp): Database sẽ đem toàn bộ dữ liệu đi sắp xếp (Sort). Khi các dòng giống nhau đã nằm cạnh nhau, nó chỉ cần duyệt qua một lượt và loại bỏ những dòng lặp. Độ phức tạp thường là .
- Hash-Unique (Băm): Database tạo ra một bảng băm (Hash Table) trong bộ nhớ. Với mỗi dòng dữ liệu, nó tính toán mã băm. Nếu mã băm đã tồn tại trong bảng, dòng đó bị bỏ qua. Nếu chưa, nó được thêm vào. Cách này thường nhanh hơn () nhưng tốn RAM hơn.
4. Cuộc chiến không hồi kết: DISTINCT vs. GROUP BY
Nhiều bạn thắc mắc: SELECT DISTINCT name và SELECT name FROM table GROUP BY name có gì khác nhau?
- Về kết quả: Chúng cho ra kết quả y hệt nhau.
- Về mục đích: *
DISTINCTdùng để loại bỏ trùng lặp đơn thuần. GROUP BYdùng để gom nhóm nhằm thực hiện các phép tính toán (Aggregate) nhưSUM,COUNT.- Về hiệu năng: Trong hầu hết các hệ quản trị hiện đại (MySQL, Postgres), bộ tối ưu hóa (Optimizer) sẽ tự động chuyển đổi chúng về cùng một phương thức thực thi. Tuy nhiên, hãy dùng DISTINCT khi bạn chỉ muốn lọc trùng để câu query trông rõ nghĩa (Clean Code) hơn.
5. Những "Cạm bẫy" hiệu năng cần tránh
Sử dụng DISTINCT bừa bãi là cách nhanh nhất để làm "bay màu" hiệu năng server của bạn:
- DISTINCT trên quá nhiều cột: Càng nhiều cột, Database càng tốn nhiều công sức để so sánh và sắp xếp.
- SELECT DISTINCT: Đây là một "Anti-pattern" kinh điển. Việc bắt Database so sánh toàn bộ các cột trong bảng (bao gồm cả những cột chứa dữ liệu lớn như TEXT, BLOB) sẽ khiến query chạy cực chậm.
- Quên tận dụng Index: Nếu cột bạn dùng DISTINCT đã được đánh Index, Database sẽ chỉ cần duyệt qua Index đó (rất nhanh) thay vì quét toàn bộ bảng (Table Scan).
6. Khi nào thực sự cần dùng DISTINCT?
Đôi khi, việc bạn phải dùng đến DISTINCT là dấu hiệu của một thiết kế Database chưa chuẩn hoặc một câu JOIN bị sai (gây ra tình trạng "nhân bản" dữ liệu - Cartesian Product).
- Nên dùng: Khi bạn thực sự cần danh sách các giá trị duy nhất từ một tập hợp (ví dụ: lấy danh sách các Role hiện có trong hệ thống).
- Nên xem lại: Nếu bạn thấy mình dùng
DISTINCTcho mọi câu query chỉ để "cho chắc ăn", hãy kiểm tra lại mệnh đềJOINvà cấu trúc bảng của mình.
Lời kết
DISTINCT là một công cụ mạnh mẽ nhưng cần được sử dụng một cách có ý thức. Hiểu được cách nó vận hành từ việc xử lý NULL cho đến các thuật toán Hash/Sort bên dưới sẽ giúp bạn viết được những câu query không chỉ đúng mà còn tối ưu.
Hy vọng bài viết chuyên sâu này giúp anh em vững tay chèo hơn trong biển dữ liệu mênh mông. Ở bài tới, chúng ta sẽ nói về SQL Subqueries - những câu query "trong lòng" query nhé!
Anh em đã từng gặp case nào dùng DISTINCT làm server "treo" luôn chưa? Hay có mẹo nào tối ưu lọc trùng hay hơn không? Chia sẻ ngay bên dưới để mình cùng thảo luận nhé!
All rights reserved