Chào mọi người, hiện mình đang luyện SQL với leetcode. Dưới đây là quá trình làm của mình, nếu có câu lệnh nào hay hơn thì mọi người chia sẻ dưới cmt nhé.
Lưu ý: 1 số bài mình có tham khảo cách làm của người khác. Và mình sử dùng MySQL.

Select phần 1

584. Viết truy vấn SQL với referee_id khác 2

SELECT name FROM Customer 
WHERE NOT referee_id = 2 OR referee_id IS NULL;

1757. Viết truy vấn SQL gồm các product_id vừa low fat và recyclable

SELECT product_id FROM Products
WHERE low_fats = 'Y' AND recyclable = 'Y';

1683. Viết truy vấn SQL tìm ID các tweet mà content có số lượng kí tự > 15

SELECT tweet_id FROM Tweets
WHERE char_length(content) > 15;

627. Thay đổi cột sex trong bảng salary (m thành f, f thành m)
Ý tưởng: dùng IF

UPDATE Salary
SET sex = IF(sex = 'm', 'f', 'm');

1068. Viết truy vấn SQL báo cáo product_name, year và price cho từng sale_id
Ý tưởng: dùng INNER JOIN

SELECT product_name, year, price
FROM Sales s
JOIN Product p
ON s.product_id = p.product_id;

1148. Viết truy vấn SQL tìm các tác giả đã xem ít nhất 1 trong các bài viết của họ, sắp xếp tăng theo id
Ý tưởng: author_id = viewer_id

SELECT DISTINCT author_id AS id
FROM Views
WHERE author_id = viewer_id
ORDER BY id ASC;

1853. Viết truy vấn SQL chuyển đổi định dạng thời gian
Ý tưởng: dùng DATE_FORMAT

SELECT DATE_FORMAT(day, '%W, %M %e, %Y') AS day
FROM Days;

1821. Viết truy vấn những khách hàng có revenue > 0 trong năm 2021

SELECT customer_id
FROM Customers
WHERE year = 2021 AND revenue > 0;

2026. Viết truy vấn SQL báo cáo ID các vấn đề có số lượt thích trên tổng số lượt vote < 60%, sắp xếp theo problem_id tăng dần

SELECT problem_id
FROM Problems
WHERE likes / (likes + dislikes) < 0.6
ORDER BY problem_id;

2082. Viết truy vấn SQL đếm số lượng khách hàng có ít nhất 1 hóa đơn amount > 500

SELECT COUNT(DISTINCT customer_id) AS rich_count
FROM Store 
WHERE amount > 500;

2377. Viết truy vấn SQL sắp xếp lại bảng huy chương Olympic (theo thứ tự vàng, bạc, đồng, nếu cùng hc đồng thì sắp xếp theo bảng chữ cái)

SELECT * FROM Olympic
ORDER BY gold_medals, silver_medals, bronze_medals, country;

Quan hệ giữa các bảng (trùng 1683, 627, 1068 ở Select phần 1)

2356. Tính số môn học DUY NHẤT mỗi giáo viên dạy

SELECT teacher_id, COUNT(DISTINCT subject_id) AS cnt
FROM Teacher
GROUP BY teacher_id;

1378. HIển thị ID của người dùng, nếu không có thì trả về null
Ý tưởng: vì xuất hiện null nên dùng LEFT JOIN

SELECT uni.unique_id AS unique_id, es.name AS name
FROM Employees es
left JOIN Employeeuni uni ON es.id = uni.id;

1795. Sắp xếp lại bảng Products, nếu cửa hàng không có sản phẩm thì không thêm vào

SELECT product_id, 'store1' AS store, store1 AS price
FROM Products WHERE store1 IS NOT NULL
UNION
SELECT product_id, 'store2' AS store, store2 AS price
FROM Products WHERE store2 IS NOT NULL
UNION
SELECT product_id, 'store3' AS store, store3 AS price
FROM Products WHERE store3 IS NOT NULL;

1587. Báo cáo tên và số dư của người dùng nếu số dư lớn hơn 10000. Số dư là tổng số tiền các giao dịch liên quan đến tài khoản đó
Ý tưởng: gom các hàng có cùng account lại để tìm số dư

SELECT u.name, SUM(t.amount) AS balance
FROM Users u
JOIN Transactions t ON u.account = t.account
GROUP BY t.account
HAVING balance > 10000;

1581. Tìm các ID đã truy cập mà không thực hiện giao dịch, và đếm số lần thực hiện như vậy
Ý tưởng: chỉ cần lọc ra các visit_id tồn tại trong bảng Visits mà không có trong bảng Transactions

SELECT customer_id, COUNT(customer_id) AS count_no_trans
FROM Visits
WHERE visit_id NOT IN (SELECT visit_id FROM Transactions)
GROUP BY customer_id;

1179. Format lại bảng sao cho có từng phòng ban và doanh thu mỗi phòng ban mỗi tháng

SELECT id, 
SUM(IF (month = 'Jan', revenue, null)) AS Jan_Revenue,
SUM(IF (month = 'Feb', revenue, null)) AS Feb_Revenue,
SUM(IF (month = 'Mar', revenue, null)) AS Mar_Revenue,
SUM(IF (month = 'Apr', revenue, null)) AS Apr_Revenue,
SUM(IF (month = 'May', revenue, null)) AS May_Revenue,
SUM(IF (month = 'Jun', revenue, null)) AS Jun_Revenue,
SUM(IF (month = 'Jul', revenue, null)) AS Jul_Revenue,
SUM(IF (month = 'Aug', revenue, null)) AS Aug_Revenue,
SUM(IF (month = 'Sep', revenue, null)) AS Sep_Revenue,
SUM(IF (month = 'Oct', revenue, null)) AS Oct_Revenue,
SUM(IF (month = 'Nov', revenue, null)) AS Nov_Revenue,
SUM(IF (month = 'Dec', revenue, null)) AS Dec_Revenue
FROM Department
GROUP BY id;

1484. Liệt kê số sản phẩm khác nhau mỗi ngày bán được và tên của chúng, tên sản phẩm sắp xếp theo từ điển, bảng kết quả sắp xếp theo sell_date
Ý tưởng: công đoạn mình nghĩ khó nhất là tìm được hàm nối các giá trị trên cột thành 1 hàng, vì vậy mình đã hỏi ChatGPT và tìm được hàm GROUP_CONCAT

SELECT sell_date, COUNT(DISTINCT product) AS num_sold, 
GROUP_CONCAT(DISTINCT product ORDER BY product SEPARATOR ',') AS products
FROM Activities 
GROUP BY sell_date;

1890. Báo cáo lần đăng nhập mới nhất của người dùng trong năm 2020, không bao gồm người dùng không đăng nhập vào năm 2020

SELECT user_id, MAX(time_stamp) AS last_stamp
FROM Logins
WHERE YEAR(time_stamp) = 2020
GROUP BY user_id;

1251. Tìm giá bán trung bình mỗi sản phẩm, làm tròn đến 2 chữ số
Ý tưởng: hàm làm tròn ROUND

SELECT p.product_id, ROUND(SUM(p.price * u.units)/SUM(u.units), 2) AS average_price
FROM Prices p
JOIN Unitssold 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;

1789. Báo cáo nhân viên và bộ phận chính của họ

SELECT employee_id, department_id
FROM Employee
WHERE employee_id IN 
(
  SELECT employee_id
  FROM Employee
  GROUP BY employee_id
  HAVING count(*) = 1
) OR primary_flag = 'Y';

Các loại join khi truy vấn (phần free lặp lại)

1303. Tìm quy mô nhóm của từng nhân viên

SELECT employee_id, COUNT(employee_id) OVER (PARTITION BY team_id) AS team_size
FROM Employee;

1571. Báo cáo tổng thể tích hàng tồn kho trong mỗi kho

SELECT w.name AS warehouse_name, SUM(w.units * p.width * p.length * p.height) AS volumn
FROM Warehouse w JOIN Products p ON w.product_id = p.product_id
GROUP BY w.name;

2339. Báo cáo các trận đấu có thể có của giải đấu, cả lượt đi lẫn lượt về
Ý tưởng: dùng CROSS JOIN, loại các trận mà 2 đội cùng tên

SELECT t1.team_name AS home_team, t2.team_name AS away_team
FROM Teams t1
CROSS JOIN Teams t2
WHERE t1.team_name != t2.team_name;

1623. Liệt kê các trường hợp mà tên và ID của học sinh không được trùng nhau

SELECT a.student_name AS member_A, b.student_name AS member_B, c.student_name AS member_C
FROM SchoolA a, SchoolB b, SchoolC c
WHERE a.student_name != b.student_name 
  AND a.student_name != c.student_name
  AND b.student_name != c.student_name
  AND a.student_id != b.student_id
  AND a.student_id != c.student_id
  AND b.student_id != c.student_id;

1421. Tìm giá trị hàng hóa theo ID và năm

SELECT q.id, q.year, IFNULL(npv, 0) AS npv
FROM Queries q 
LEFT JOIN NPV n ON q.id = n.id AND q.year = n.year;

1069. Báo cáo tổng số lượng đã bán cho mỗi sản phẩm

SELECT product_id, SUM(quantity) AS total_quantity
FROM Sales;

Select phần 2 (trùng 2356, 1179)

1741. Tính thời gian mỗi nhân viên dành ở văn phòng theo ngày

SELECT event_day AS day, emp_id, SUM(out_time - in_time) AS total_time
FROM Employees
GROUP BY day, emp_id;

1693. Tìm số lượng lãnh đạo và đối tác của từng hãng theo ngày

SELECT date_id, make_name, COUNT(DISTINCT lead_id) AS unique_leads, 
COUNT(DISTINCT partner_id) AS unique_partners
FROM DailySales
GROUP BY date_id, make_name;

1350. Tìm danh sách sinh viên đăng kí vào các khoa không còn tồn tại

SELECT id, name
FROM Students
WHERE department_id NOT IN (SELECT id FROM Departments);

1777. Tìm giá từng mặt hàng của mỗi cửa hàng

SELECT product_id, 
SUM(IF (store = 'store1', price, null)) AS store1,
SUM(IF (store = 'store2', price, null)) AS store2,
SUM(IF (store = 'store3', price, null)) AS store3
FROM Products
GROUP BY product_id;

1565. Đếm số lượng đơn hàng và khách hàng có hóa đơn > $20 cho mỗi tháng

SELECT DATE_FORMAT(order_date, '%Y-%m') AS month, COUNT(DISTINCT order_id) AS order_count, COUNT(DISTINCT customer_id) AS customer_count
FROM Orders
GROUP BY month;

1173. Tính tỉ lệ đơn đặt hàng ngay lập tức, làm tròn đến 2 chữ số

SELECT ROUND(100 * AVG(IF (order_date = customer_pref_delivery_date)), 2) AS immediate_percentage
FROM Delivery;

Chữa bài tập 1 (trùng 1890)

595. Tìm các nước có diện tích ít nhất là 3000000 km2 hoặc dân số ít nhất là 25000000 người

SELECT name, population, area
FROM World
WHERE area >= 3000000 OR population >= 25000000;

620. Tìm các bộ phim có ID lẻ và được mô tả không boring, sắp xếp theo rating giảm dần

SELECT id, movie, description, rating 
FROM Cinema
WHERE id % 2 = 1 AND description != 'boring'
ORDER BY rating DESC;

610. Với 3 đoạn thẳng, xem chúng có thể tạo thành 1 tam giác hay không

SELECT *, IF(x + y > z AND y + z > x AND z + x > y, 'Yes', 'No') AS triangle
FROM Triangle;

1729. Trả về số người theo dõi của mỗi người, sắp xếp theo user_id tăng dần

SELECT user_id, COUNT(DISTINCT follower_id) AS followers_count
FROM Followers
GROUP BY user_id
ORDER BY user_id;

1050. Tìm tất cả các cặp diễn viên - đạo diễn đã hợp tác với nhau ít nhất 3 lần

SELECT actor_id, director_id
FROM ActorDirector
GROUP BY actor_id, director_id
HAVING COUNT(DISTINCT timestamp) >= 3;

Chữa bài tập 2 (trùng 1484)

511. Tìm lần đăng nhập đầu tiên của mỗi người chơi

SELECT player_id, MIN(event_date) AS first_login
FROM Activity
GROUP BY player_id;

175. Báo cáo tên và địa chỉ của mỗi người, nếu không có thì báo null
Ý tưởng: xuất hiện null nên dùng LEFT JOIN

SELECT p.firstName, p.lastName, a.city, a.state
FROM Person p
LEFT JOIN Address a ON p.personId = a.personId;

1082. Tìm sản phẩm bán chạy nhất theo tổng giá bán, nếu có nhiều hơn 1 thì in ra tất cả
Ý tưởng: dùng Window function, cụ thể là RANK

SELECT seller_id
FROM 
(
   SELECT seller_id, RANK() OVER (PARTITION BY seller_id ORDER BY SUM(price) DESC) AS rank
   FROM Sales
   GROUP BY seller_id;
) x WHERE rank = 1;

2072. In ra tên trường đại học nào có nhiều sinh viên xuất sắc hơn, nếu bằng nhau thì hòa

WITH Student AS (
   SELECT COUNT(*) AS NY FROM NewYork WHERE score >= 90
   UNION
   SELECT COUNT(*) AS CA FROM California WHERE score >= 90
)
SELECT CASE 
   WHEN NY > CA THEN 'New York University'
   WHEN CA > NY THEN 'California University'
   ELSE 'No Winner'
END AS winner
FROM Student;