Hướng Dẫn Giải Quyết Các Bài Toán SQL Phỏng Vấn Thực Chiến

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
);

Thẻ: mysql sql Interview Preparation Database Design data analysis

Đăng vào ngày 2 tháng 7 lúc 07:53