Danh Sách Các Câu Truy Vấn Tiêu Biểu
Dưới đây là bộ sưu tập các kịch bản xử lý dữ liệu phổ biến thường xuất hiện trong các buổi phỏng vấn kỹ thuật, kèm theo ví dụ minh họa cách viết truy vấn hiệu quả bằng MySQL.
1. Lọc sản phẩm đạt chuẩn bền vững
Yêu cầu tìm ra các mã sản phẩm có trạng thái tái chế cao và hàm lượng thấp.
SELECT
product_id AS pid
FROM
Products
WHERE
low_fats = 'Y'
AND recyclable = 'Y';
2. Tìm kiếm khách hàng thuộc dạng giới thiệu
Lấy tên khách hàng mà người giới thiệu của họ không phải là ID 2 hoặc giá trị này trống.
SELECT
name AS customer_name
FROM
Customer
WHERE
referee_id <> 2 OR referee_id IS NULL;
3. Thống kê quốc gia quy mô lớn
Trả về thông tin các quốc gia có diện tích trên 3 triệu km² hoặc dân số trên 25 triệu.
SELECT
name AS country_name,
population AS pop_count,
area AS land_area
FROM
World
WHERE
area >= 3000000 OR population >= 25000000;
4. Bài toán đọc bài viết (Duyệt bài)
Xác định danh sách tác giả đã tự xem lại bài viết của mình, sắp xếp theo thứ tự tăng dần.
SELECT DISTINCT
author_id AS id
FROM
Views
WHERE
author_id = viewer_id
ORDER BY
id ASC;
5. Kiểm tra độ dài nội dung tweet
Sử dụng hàm đếm ký tự để xác định bài đăng vượt quá 15 đơn vị. Lưu ý phân biệt giữa byte và ký tự.
SELECT
tweet_id AS tid
FROM
Tweets
WHERE
LENGTH(content) > 15;
6. Ghép thông tin nhân viên và mã định danh
Sử dụng LEFT JOIN để lấy tên nhân viên kèm mã định danh duy nhất, trả về null nếu thiếu.
SELECT
e_uni.unique_id,
emp.name AS employee_name
FROM
Employees AS emp
LEFT JOIN
EmployeeUNI AS e_uni
ON
emp.id = e_uni.id;
7. Phân tích doanh số bán hàng cơ bản
Kết hợp bảng bán hàng và bảng sản phẩm để hiển thị giá và năm giao dịch.
SELECT
prod_name,
sale_year,
unit_price
FROM
Sales AS s
LEFT JOIN
Product AS p
ON
s.product_id = p.product_id;
8. Khách hàng đến nhưng không mua gì
Tìm các khách hàng có lần ghé thăm nhưng không có hóa đơn liên kết tương ứng.
SELECT
v.customer_id,
COUNT(v.customer_id) AS count_of_no_trans
FROM
Visits AS v
LEFT JOIN
Transactions AS t
ON
v.visit_id = t.visit_id
WHERE
t.transaction_id IS NULL
GROUP BY
v.customer_id;
9. So sánh nhiệt độ ngày hôm trước
Chọn các ngày có nhiệt độ cao hơn ngày liền kề trước đó bằng cách sử dụng self-join.
SELECT
w_high.id
FROM
Weather AS w_high
CROSS JOIN
Weather AS w_low
WHERE
DATEDIFF(w_high.recordDate, w_low.recordDate) = 1
AND w_high.temperature > w_low.temperature;
10. Thời gian chạy trung bình của tiến trình
Tính thời gian chênh lệch giữa sự kiện bắt đầu và kết thúc cho từng máy chủ.
SELECT
a.machine_id,
ROUND(AVG(b.timestamp - a.timestamp), 3) AS processing_time
FROM
Activity AS a
JOIN
Activity AS b
ON
a.machine_id = b.machine_id
AND a.process_id = b.process_id
AND a.activity_type = 'start'
AND b.activity_type = 'end'
GROUP BY
a.machine_id;
11. Bộ phận nhân viên nhận thưởng dưới ngưỡng
Liệt kê nhân viên có thưởng thấp hơn 1000 hoặc chưa được ghi nhận mức thưởng.
SELECT
e.name AS emp_name,
b.bonus_amount
FROM
Employee AS e
LEFT JOIN
Bonus AS b
ON
e.empId = b.empId
WHERE
b.bonus_amount < 1000 OR b.bonus_amount IS NULL;
12. Đếm số lần tham gia thi môn học
Tạo danh sách đầy đủ tất cả sinh viên và môn học, sau đó đếm số lần dự thi thực tế.
SELECT
stu.student_id,
stu.student_name,
sub.subject_name,
COUNT(e.subject_name) AS attended_exams
FROM
(SELECT * FROM Students, Subjects) AS stu
LEFT JOIN
Examinations AS e
ON
stu.student_id = e.student_id
GROUP BY
stu.student_id, stu.student_name, sub.subject_name
ORDER BY
stu.student_id, sub.subject_name;
13. Quản lý cấp cao với nhiều cấp dưới
Tìm các quản lý có ít nhất 5 nhân viên trực tiếp báo cáo.
SELECT
mgr.name AS manager_name
FROM
Employee AS mgr
JOIN
(SELECT managerId, COUNT(*) AS cnt
FROM Employee
GROUP BY managerId
HAVING COUNT(*) >= 5) AS sub
ON
mgr.id = sub.managerId;
14. Tính tỷ lệ xác nhận tài khoản
Sử dụng CASE để tính tỷ lệ thành công của các yêu cầu xác nhận từ phần đăng ký.
SELECT
s.user_id,
ROUND(IFNULL(SUM(CASE WHEN c.action = 'timeout' THEN 0 ELSE 1 END) / COUNT(c.action), 0), 2) AS confirmation_rate
FROM
Signups AS s
LEFT JOIN
Confirmations AS c
ON
s.user_id = c.user_id
GROUP BY
s.user_id;
15. Phim điện ảnh thú vị (Không boring và số lẻ)
Sắp xếp film theo điểm đánh giá giảm dần, loại bỏ các phim chán và giữ lại ID lẻ.
SELECT
*
FROM
cinema
WHERE
description <> 'boring'
AND MOD(id, 2) != 0
ORDER BY
rating DESC;
16. Tính giá trung bình bán hàng (Xử lý Null)
Tính giá trung bình có trọng số dựa trên số lượng mua, xử lý các trường hợp chia cho 0.
SELECT
p.product_id,
IFNULL(ROUND(SUM(p.price * u.units) / SUM(u.units), 2), 0) AS average_price
FROM
Prices AS p
LEFT JOIN
UnitsSold AS u
ON
p.product_id = u.product_id
AND u.purchase_date BETWEEN p.start_date AND p.end_date
GROUP BY
p.product_id;
17. Đánh giá kinh nghiệm nhân viên theo dự án
Tính trung bình số năm làm việc của nhóm nhân viên trong mỗi dự án riêng biệt.
SELECT
p.project_id,
ROUND(AVG(e.experience_years), 2) AS average_years
FROM
Project AS p
LEFT JOIN
Employee AS e
ON
p.employee_id = e.employee_id
GROUP BY
p.project_id;
18. Tỷ lệ đăng ký thi đấu
Sử dụng subquery để tính phần trăm người đăng ký vào tổng số người dùng trong hệ thống.
SELECT
r.contest_id,
ROUND(COUNT(r.user_id) / (SELECT COUNT(*) FROM Users) * 100, 2) AS percentage
FROM
Users AS u
LEFT JOIN
Register AS r
ON
u.user_id = r.user_id
GROUP BY
r.contest_id
HAVING
r.contest_id IS NOT NULL
ORDER BY
percentage DESC, r.contest_id ASC;
19. Chất lượng và tỷ lệ lỗi truy vấn
Phân tích chất lượng theo tỷ lệ đánh giá và vị trí, đồng thời tính phần trăm truy vấn kém.
SELECT
query_name,
ROUND(AVG(rating / position), 2) AS quality,
ROUND(SUM(CASE WHEN rating < 3 THEN 1 ELSE 0 END) / COUNT(*) * 100, 2) AS poor_query_percentage
FROM
Queries
GROUP BY
query_name;
20. Tổng quan giao dịch theo tháng (Substring)
Trích xuất tháng từ ngày tháng giao dịch để phân loại dữ liệu theo kỳ.
SELECT
SUBSTRING(trans_date, 1, 7) AS month,
country,
COUNT(state) AS trans_count,
SUM(CASE WHEN state = 'approved' THEN 1 ELSE 0 END) AS approved_count,
SUM(amount) AS total_amount,
SUM(CASE WHEN state = 'approved' THEN amount ELSE 0 END) AS approved_total_amount
FROM
Transactions
GROUP BY
SUBSTRING(trans_date, 1, 7), country;
21. Dịch vụ giao hàng tức thì
Xác định tỷ lệ giao hàng khớp với sở thích khách hàng đúng ngay thời điểm đặt.
SELECT
ROUND(SUM(order_date = customer_pref_delivery_date) / COUNT(*) * 100, 2) AS immediate_percentage
FROM
Delivery
WHERE
(customer_id, order_date) IN (
SELECT customer_id, MIN(order_date)
FROM Delivery
GROUP BY customer_id
);
22. Phân tích người dùng chơi game liên tục
Tính tỷ lệ người dùng chơi game trong 3 ngày liên tiếp dựa trên chuỗi ngày tháng.
SELECT
IFNULL(ROUND(COUNT(DISTINCT Result.player_id) / COUNT(DISTINCT Activity.player_id), 2), 0) AS fraction
FROM
(
SELECT Activity.player_id
FROM (
SELECT player_id, DATE_ADD(MIN(event_date), INTERVAL 1 DAY) AS next_day
FROM Activity
GROUP BY player_id
) AS Expected, Activity
WHERE Activity.event_date = Expected.next_day
AND Activity.player_id = Expected.player_id
) AS Result;
23. Số lượng môn học giáo viên giảng dạy
Đếm số lượng môn học khác nhau do một giáo viên phụ trách.
SELECT
teacher_id,
COUNT(DISTINCT subject_id) AS subject_count
FROM
Teacher
GROUP BY
teacher_id;
24. Số người dùng hoạt động gần đây
Đếm số lượt đăng nhập độc nhất trong khoảng thời gian cụ thể (Last 30 days).
SELECT
activity_date AS day,
COUNT(DISTINCT user_id) AS active_users
FROM
Activity
WHERE
activity_date BETWEEN '2019-06-28' AND '2019-07-27'
GROUP BY
activity_date;
25. Phân tích phạm vi thời gian bán hàng
Lọc sản phẩm chỉ được bán hoàn toàn trong một khoảng thời gian tối thiểu nhất định.
SELECT
p.product_id,
product_name
FROM
Sales AS s
JOIN
Product AS p
ON
p.product_id = s.product_id
GROUP BY
s.product_id
HAVING
MAX(sale_date) <= '2019-03-31'
AND MIN(sale_date) >= '2019-01-01';
26. Lớp học có nhiều học sinh
Xác định các lớp học có ít nhất 5 học viên đang tham gia.
SELECT
class
FROM
Courses
GROUP BY
class
HAVING
COUNT(student) >= 5;
27. Đếm số lượng người theo dõi
Tóm tắt số lượng follower cho mỗi người dùng trên nền tảng mạng xã hội giả định.
SELECT
user_id,
COUNT(follower_id) AS followers_count
FROM
Followers
GROUP BY
user_id
ORDER BY
user_id;
28. Con số duy nhất lớn nhất
Tìm giá trị số lớn nhất chỉ xuất hiện đúng một lần trong cơ sở dữ liệu.
SELECT
IF(MAX(num) IS NULL, NULL, MAX(num)) AS num
FROM
(SELECT num, COUNT(num) AS cnt
FROM MyNumbers
GROUP BY num
HAVING COUNT(num) = 1) AS a;
29. Khách hàng mua hết mọi sản phẩm
Xác định khách hàng có hóa đơn bao gồm tất cả các mã sản phẩm hiện có trong kho.
SELECT
customer_id
FROM
Product AS p
LEFT JOIN
Customer AS c
ON
p.product_key = c.product_key
GROUP BY
customer_id
HAVING
COUNT(DISTINCT c.product_key) = (SELECT COUNT(DISTINCT product_key) FROM Product);
30. Thông tin thống kê phòng ban lãnh đạo
Đếm số nhân viên báo cáo và tuổi trung bình của cấp dưới đối với từng trưởng bộ phận.
SELECT
employee_id,
name AS manager_name,
reports_count,
average_age
FROM
Employees AS e
JOIN
(SELECT reports_to, COUNT(reports_to) AS reports_count, ROUND(AVG(age), 0) AS average_age
FROM Employees
GROUP BY reports_to) AS a
ON
e.employee_id = a.reports_to
ORDER BY
employee_id;
31. Xác định phòng ban chính thức của nhân viên
Sử dụng UNION để gộp các điều kiện: Phòng ưu tiên flag Y và Nhân viên chỉ ở một phòng duy nhất.
SELECT
employee_id,
department_id
FROM
Employee
WHERE
primary_flag = 'Y'
UNION
SELECT
a.employee_id,
a.department_id
FROM
(SELECT employee_id, department_id, COUNT(primary_flag)
FROM Employee
GROUP BY employee_id
HAVING COUNT(primary_flag) = 1) AS a;
32. Kiểm tra hình tam giác hợp lệ
Áp dụng điều kiện bất đẳng thức tam giác để xác định xem ba cạnh tạo nên hình tam giác hay không.
SELECT
x, y, z,
CASE WHEN x >= y AND x + y > z AND x - y < z
THEN 'Yes'
ELSE 'No' END AS triangle_status
FROM
Triangle;
33. Tìm số lặp lại liên tiếp ba lần
So sánh các dòng kế tiếp nhau (ID n, n+1, n+2) để tìm giá trị trùng lặp liên tiếp.
SELECT DISTINCT
l1.num AS ConsecutiveNums
FROM
Logs AS l1, Logs AS l2, Logs AS l3
WHERE
l1.id = l2.id - 1
AND l2.id = l3.id - 1
AND l1.num = l2.num
AND l2.num = l3.num;
34. Cập nhật giá sản phẩm mới nhất
Tìm giá mới nhất của sản phẩm trước hoặc bằng một ngày cụ định, mặc định là 10 nếu chưa thay đổi.
SELECT
p1.product_id,
IFNULL(p2.new_price, 10) AS price
FROM
(SELECT DISTINCT product_id FROM Products) AS p1
LEFT JOIN
(SELECT product_id, new_price
FROM Products
WHERE (product_id, change_date) IN
(SELECT product_id, MAX(change_date)
FROM Products
WHERE change_date <= '2019-08-16'
GROUP BY product_id)) AS p2
ON
p1.product_id = p2.product_id;
35. Chặn xe buýt khi đạt trọng tải tối đa
Sử dụng hàm cửa sổ (Window Function) để tính tổng trọng lượng tích lũy và chặn tại ngưỡng giới hạn.
SELECT person_name
FROM Queue
WHERE turn = (
SELECT MAX(turn)
FROM (
SELECT turn, SUM(weight) OVER (ORDER BY turn) AS cumulative_sum
FROM Queue
) AS subquery
WHERE cumulative_sum <= 1000
);
36. Phân loại thu nhập tài khoản
Chia nhóm tài khoản theo khung thu nhập (Thấp, Cao, Trung bình) và đếm số lượng.
SELECT 'Low Salary' AS category, SUM(CASE WHEN income < 20000 THEN 1 ELSE 0 END) AS accounts_count
FROM Accounts
UNION ALL
SELECT 'High Salary' AS category, SUM(CASE WHEN income > 50000 THEN 1 ELSE 0 END) AS accounts_count
FROM Accounts
UNION ALL
SELECT 'Average Salary' AS category, SUM(CASE WHEN income >= 20000 AND income <= 50000 THEN 1 ELSE 0 END) AS accounts_count
FROM Accounts;
37. Loại trừ nhân viên bị đuổi do lương thấp và mất cấp trên
Loại bỏ những nhân viên có lương thấp và người quản lý của họ không còn tồn tại trong hệ thống.
SELECT
employee_id
FROM
Employees
WHERE
salary < 30000
AND manager_id NOT IN (SELECT employee_id FROM Employees)
ORDER BY
employee_id;
38. Thống kê hoạt động xem phim (Rank & Avg)
Hợp nhất hai báo cáo: Người xem nhiều nhất và Phim điểm cao nhất trong tháng 2.
SELECT name AS results
FROM (
SELECT m1.user_id, name, COUNT(DISTINCT m1.movie_id) AS cnt
FROM MovieRating m1
LEFT JOIN Movies m2 ON m1.movie_id = m2.movie_id
LEFT JOIN Users u ON m1.user_id = u.user_id
GROUP BY m1.user_id
ORDER BY cnt DESC, name ASC
LIMIT 1
) AS b
UNION ALL
SELECT title AS results
FROM (
SELECT m1.movie_id, title, AVG(rating) AS point
FROM MovieRating m1
LEFT JOIN Movies m2 ON m1.movie_id = m2.movie_id
WHERE created_at LIKE '2020-02%'
GROUP BY m1.movie_id, title
ORDER BY point DESC, title ASC
LIMIT 1
) AS a;
39. Xác định người có nhiều bạn bè nhất
Tổng hợp requester_id và accepter_id thành một cột, sau đó đếm tần suất xuất hiện cao nhất.
SELECT id, COUNT(id) AS num
FROM (
SELECT requester_id AS id FROM RequestAccepted
UNION ALL
SELECT accepter_id AS id FROM RequestAccepted
) AS combined
GROUP BY id
ORDER BY num DESC
LIMIT 1;
40. Top 3 nhân viên lương cao nhất từng bộ phận
Sử dụng DENSE_RANK qua phân vùng để xếp hạng lương trong từng department.
SELECT Department, Employee, salary
FROM (
SELECT d.name AS Department, e.name AS Employee, salary,
DENSE_RANK() OVER(PARTITION BY d.id ORDER BY salary DESC) AS rank_val
FROM Employee e
LEFT JOIN Department d ON e.departmentId = d.id
) AS ranked_data
WHERE rank_val <= 3;
41. Chuẩn hóa định dạng tên người dùng
Viết hoa chữ cái đầu và viết thường các chữ còn lại trong tên người dùng.
SELECT user_id, CONCAT(UPPER(SUBSTRING(name, 1, 1)), LOWER(SUBSTRING(name, 2))) AS formatted_name
FROM Users
ORDER BY user_id;
42. Tìm bệnh nhân mắc bệnh đặc trưng
Sử dụng REGEXP hoặc LIKE để lọc các tiền tố hoặc hậu tố của mã bệnh trong điều kiện bệnh án.
SELECT * FROM Patients
WHERE conditions LIKE 'DIAB1%' OR conditions LIKE '% DIAB1%';
43. Tìm mức lương thứ hai cao nhất
Sử dụng SKIP ROWS hoặc OFFSET (LIMIT 1,1) để lấy giá trị lớn thứ hai, trả về NULL nếu không có.
SELECT IFNULL(
(SELECT DISTINCT salary FROM Employee ORDER BY salary DESC LIMIT 1, 1),
NULL
) AS SecondHighestSalary;
44. Nhóm sản phẩm bán ra theo ngày
Sử dụng GROUP_CONCAT để gom danh sách sản phẩm bán được thành chuỗi văn bản ngăn cách dấu phẩy.
SELECT sell_date, COUNT(DISTINCT product) AS num_sold, GROUP_CONCAT(DISTINCT product ORDER BY product SEPARATOR ',') AS products_list
FROM Activities
GROUP BY sell_date
ORDER BY sell_date ASC;
45. Sản phẩm bán chạy trong tháng mục tiêu
Tìm các sản phẩm có tổng đơn hàng tháng 2/2020 đạt ngưỡng quy định tối thiểu.
SELECT product_name, SUM(unit) AS total_unit
FROM Products p
LEFT JOIN Orders o ON p.product_id = o.product_id
WHERE order_date LIKE '2020-02%'
GROUP BY product_name
HAVING SUM(unit) >= 100;
46. Xác thực định dạng email
Sử dụng Regex pattern để kiểm tra cấu trúc email hợp lệ với tên miền cố định.
SELECT user_id, name, mail
FROM Users
WHERE mail REGEXP '^[a-zA-Z][a-zA-Z0-9_.-]*\\@leetcode\\.com$';
47. Xóa bản sao email dư thừa
Xóa các dòng có địa chỉ email trùng lặp, chỉ giữ lại dòng có ID nhỏ nhất.
DELETE FROM Person
WHERE id NOT IN (
SELECT id
FROM (SELECT MIN(id) AS id, email
FROM Person
GROUP BY email) AS unique_emails
);