Chia sẻ của tác giả Yoon Sun Mi từ Datarian – một nền tảng chuyên về dữ liệu, cung cấp các dịch vụ như phân tích dữ liệu, tư vấn dữ liệu, đào tạo SQL, seminar hàng tháng, v.v.
Bạn đã bao giờ phân tích một truy vấn SQL vô cùng khó đọc chưa? Việc này khó như thể giải mật mã vậy! Bạn có thể rơi vào tình huống này không chỉ trong trường hợp đọc truy vấn SQL do người khác viết, mà còn có khả năng “vướng” phải những truy vấn SQL do chính bản thân viết trong quá khứ. Sẽ có những khi cho dù đọc bao nhiêu bạn cũng không thể hiểu được một truy vấn nào đó thực hiện chức năng gì. Tuy nhiên, không ai có thể phàn nàn về những truy vấn SQL tôi viết. SQL là ngôn ngữ gần như không hề có quy tắc cú pháp nào quy định việc viết cách khoảng hay viết hoa, viết thường. Ví dụ, hai truy vấn dưới đây đều trả về cùng một kết quả.
SELECT day, total_bill
FROM tips
SELECT day, total_bill FROM tips
Nếu đã từng làm việc với những ngôn ngữ đặt ra quy tắc nghiêm ngặt về cú pháp như Python, bạn sẽ có thể cảm thấy được giải phóng trong sự tự do của SQL. Tuy nhiên, khi đọc hiểu truy vấn SQL của người khác, bạn sẽ dễ dàng phải thốt lên “Ôi có cả loại ngôn ngữ này nữa sao…” Vậy nên trong bài viết này, tôi sẽ chia sẻ năm thói quen đơn giản giúp bạn viết những truy vấn SQL dễ đọc, mặc dù SQL không đặt ra những quy tắc cú pháp cụ thể nào.
1. Viết hoa câu lệnh
Đầu tiên là thói quen viết hoa các câu lệnh như SELECT
, FROM
, GROUP BY
, HAVING
, AS
hay AND
. Bạn thấy đoạn truy vấn nào dễ đọc hơn trong hai truy vấn dưới đây? Chắc chắn là truy vấn thứ nhất.
SELECT tip, total_bill
FROM tips
select tip, total_bill
from tips
Một số editor (trình soạn thảo mã nguồn) hỗ trợ SQL bằng cách làm nổi bật (highlight) câu lệnh và tên bảng dữ liệu (table) hoặc cột (column). Do đó khi sử dụng editor riêng, cho dù bạn không viết hoa câu lệnh thì truy vấn vẫn dễ đọc.

Trong đoạn mã trên, editor đã làm nổi bật SELECT
và FROM
. Tuy nhiên, mỗi editor có các quy tắc đánh dấu khác nhau và mỗi cơ sở dữ liệu cũng sử dụng các tên hàm khác nhau, do đó có nhiều trường hợp editor cũng không thể đánh dấu và phân biệt các thành phần một cách hợp lý. Đặc biệt, những hàm liên quan đến ngày tháng như DATE_ADD()
có sự khác biệt rất lớn giữa các cơ sở dữ liệu. Chính vì vậy, bạn nên duy trì thói quen viết hoa câu lệnh hay tên hàm để chủ động phân biệt các thành phần trong truy vấn.
2. Thường xuyên ngắt dòng
Thứ hai là thói quen thường xuyên ngắt dòng. Trong hai truy vấn dưới đây, chắc hẳn truy vấn thuận mắt và dễ đọc hơn là đoạn đầu tiên.
SELECT day
, time
, SUM(total_bill)
FROM tips
GROUP BY day, time
SELECT day, time, SUM(total_bill) FROM tips GROUP BY day, time
Cá nhân tôi không viết nhiều vế như SELECT
, FROM
và GROUP BY
trong cùng một dòng. Bạn sẽ cảm thấy tiện hơn khi viết tất cả liền mạch trên cùng một dòng, tuy nhiên sau này bạn sẽ cần đọc lại tất cả để nắm bắt ý định của truy vấn. Trong trường hợp truy vấn thứ hai ở trên, bạn cần phải đọc đến ký tự cuối cùng mới có thể hiểu được “À, hóa ra đây là truy vấn để tính tổng”. Ngược lại, trong truy vấn thứ nhất, bạn hoàn toàn có thể nắm bắt ý định và cấu trúc của truy vấn cho dù chỉ đọc những câu lệnh ở đầu mỗi dòng.
3. Thường xuyên ngắt dòng hơn nữa
Thói quen thứ ba là, ngắt dòng nhiều hơn nữa! Hãy tiếp tục so sánh hai truy vấn sau.
SELECT day
, time
, SUM(total_bill)
FROM tips
WHERE sex = 'Female'
AND smoker = 'Yes'
GROUP BY day
, time
SELECT day, time, SUM(total_bill)
FROM tips
WHERE sex = 'Female' AND smoker = 'Yes'
GROUP BY day, time
Giống như ví dụ ở trên, giá trị của việc ngắt dòng sẽ tỏa sáng trong trường hợp bạn cần vô hiệu hóa một dòng hay một đoạn code bằng chú thích. Truy vấn hiện tại đang sử dụng hai cột day
và time
làm tiêu chí tính tổng, nhưng hãy giả sử sau này bạn chỉ muốn giữ lại riêng cột day
. Lúc này, bạn có thể nhanh chóng loại bỏ cột time
khỏi logic bằng cách chú thích một dòng code.
SELECT day
-- , time
, SUM(total_bill)
FROM tips
WHERE sex = 'Female'
AND smoker = 'Yes'
GROUP BY day
-- , time
Đôi khi bạn sẽ gặp trường hợp cần thêm lại một logic đã xóa đi trước đó. Lúc này, thói quen ngắt dòng nhiều hơn nữa sẽ càng tỏa sáng. Đó là vì tiêu chuẩn ngắt dòng thường là câu lệnh, giúp bạn dễ dàng nắm bắt vấn đề và giải quyết nhanh hơn. Bạn nên ngắt dòng cả khi sử dụng các toán tử AND
hay OR
trong câu lệnh WHERE
như ví dụ sau.
SELECT day
, time
, SUM(total_bill)
FROM tips
WHERE 1 = 1
-- AND sex = 'Female'
AND smoker = 'Yes'
GROUP BY day
, time
Đôi khi bạn sẽ thấy những người thành thạo SQL thêm điều kiện vô nghĩa 1 = 1
ở đầu câu lệnh WHERE
, sau đó xuống dòng rồi sử dụng AND
để bắt đầu điều kiện thật. Tôi cảm thấy đây là một cách ứng dụng khá thú vị nên đã đề cập ở đây để bạn tham khảo thêm.
4. Viết chú thích
Thói quen nên có thứ tư là thêm những chú thích (comment) ngắn để giải thích ý định của hàm, điều kiện hoặc biến. Đặc biệt, trong trường hợp có nhiều truy vấn con (subquery / inner query) khiến truy vấn chính trở nên rất dài, việc ghi lại ý định của từng đoạn truy vấn sẽ giúp bạn nhanh chóng hiểu được chúng khi đọc lại trong tương lai.
SELECT AVG(sales) avg_sales
FROM (
-- tính tổng doanh thu theo ngày trong tuần
SELECT day -- day là ngày trong tuần, không tính cuối tuần
, SUM(total_bill) sales
FROM tips
WHERE sex = 'Female' -- trường hợp người thanh toán là phụ nữ
GROUP BY day
) daily_sales
Ngoài ra, bạn cũng nên viết chú thích cho các điều kiện CASE
, IF
hoặc các điều kiện lọc WHERE
. Khi viết chú thích cho điều kiện, một câu trần thuật đơn giản “Điều kiện này nghĩa là …” (What) gần như không có ý nghĩa gì. Tuy nhiên, việc chỉ phán đoán dựa vào tên cột cũng có thể gây ra hiểu lầm, do đó bạn nên giải thích thêm một chút trong những trường hợp cần thiết. Những chú thích giải thích chi tiết tại sao lại cần điều kiện này (Why), điều kiện này hoạt động như thế nào (How) chắc chắn sẽ rất hữu ích trong việc đọc hiểu truy vấn sau này. Những lúc như vậy, bạn có thể tự dành cho chính mình một lời khen vì đã nuôi dưỡng thói quen viết chú thích!
5. Thường xuyên sử dụng Alias
Việc viết chú thích cũng quan trọng, tuy nhiên rốt cuộc thì vấn đề căn bản nhất vẫn là viết mã tốt. Liệu bạn có đang sử dụng alias một cách đại khái như x
, y
, t
, df
, a
cho các truy vấn con hay tên cột không?
Giả sử bạn sử dụng alias t1
cho truy vấn con trong lệnh FROM
, sau đó bạn thấy một biểu thức t1.x + t2.y
ở truy vấn cha (outer query). Lúc này, để hiểu được biểu thức có ý nghĩa là gì, bạn cần phải tìm đọc lại cả truy vấn con định nghĩa các alias đó.
Tới đây, bạn có thể nghĩ rằng “Không có nhiều khi cần sử dụng lại truy vấn nhưng lại có rất nhiều alias, vậy mà alias nào cũng phải đặt tên sao?” Vâng, nếu không làm như vậy thì bạn không thể tránh khỏi đau khổ khi cần đọc lại những truy vấn cũ của mình.
Có một sự thật thú vị rằng đây không chỉ là trăn trở của riêng tôi và một số người. Khi bạn tìm kiếm “Cách đặt tên biến” trên Google, bạn sẽ thấy rất nhiều nhà phát triển chia sẻ những mẹo đặt tên biến hiệu quả. Nhưng thật may mắn là việc đặt tên biến này sẽ trở nên dễ dàng hơn theo thời gian khi bạn luyện tập thật nhiều.
Điều quan trọng nhất là những quy tắc được thống nhất
Tôi đã đề cập đến nhiều quy tắc, tuy nhiên điều quan trọng nhất lại chính là những quy tắc được thống nhất giữa những người làm việc cùng nhau. Cho dù có là quy tắc hay đến đâu, nếu vi phạm những quy ước mà nhóm đang tuân thủ thì ít nhất bạn cũng phải mất công thuyết phục các thành viên khác đồng ý thay đổi quy ước. Do đó, trước tiên hãy tuân thủ chế độ chung. Nếu bạn hoàn toàn không quan tâm đến những quy tắc viết mã vốn có của nhóm thì cho dù áp dụng phương pháp hay đến đâu, bạn chắc chắn vẫn sẽ lẻ loi một mình như chiếc sừng tê giác.
Trên tất cả, một vấn đề quan trọng khác là bạn cần viết và tổ chức mã nguồn trong khi tự nhắc mình rằng, mã nguồn này không chỉ có tôi mà sẽ có cả những người khác đọc. Trải nghiệm bức bối của bạn khi đọc các truy vấn SQL của người tiền nhiệm một ngày nào đó có thể lặp lại với người kế nhiệm. Người ta thường không biết rằng một đoạn mã SQL được viết một cách hẳn hoi ngay lúc này, rất có thể sẽ trở thành phao cứu sinh của chính họ trong tương lai.
The original article: SQL 가독성을 높이는 다섯 가지 사소한 습관
The translated article above belongs to the author Yoon Sun Mi (윤선미) and yozmIT (요즘IT). Metacoders commits not to use this content for any commercial purpose.