Khắc phục lỗi MySQL 1242: Subquery returns more than 1 row

Trong quá trình làm việc với MySQL, lỗi [Err] 1242 - Subquery returns more than 1 row là một trong những thông báo phổ biến nhất khi thực hiện các truy vấn lồng nhau (subqueries). Lỗi này xảy ra khi một truy vấn con được kỳ vọng chỉ trả về một giá trị duy nhất (scalar value) để phục vụ một phép so sánh, nhưng thực tế nó lại trả về nhiều dòng dữ liệu.

Nguyên nhân gây lỗi

Thông thường, lỗi này xuất hiện khi bạn sử dụng các toán tử so sánh đơn lẻ như =, >, <, >=, <= hoặc != trước một truy vấn con. Nếu truy vấn con đó tìm thấy nhiều hơn một bản ghi khớp với điều kiện, hệ thống sẽ không biết phải lấy giá trị nào để so sánh và dẫn đến xung đột.

Ví dụ về một câu lệnh gây lỗi:

SELECT ten_sinh_vien 
FROM danh_sach_lop 
WHERE id_sinh_vien = (
    SELECT id_nguoi_dung 
    FROM dang_ky_khoa_hoc 
    WHERE ma_mon_hoc = 'CS101'
);

Nếu môn học 'CS101' có từ 2 sinh viên đăng ký trở lên, câu lệnh trên sẽ vấp phải lỗi 1242 ngay lập tức.

Các giải pháp xử lý hiệu quả

1. Sử dụng từ khóa IN

Đây là cách phổ biến nhất để thay thế toán tử =. Từ khóa IN cho phép so sánh một giá trị với một tập hợp các giá trị trả về từ truy vấn con.

-- Thay đổi dấu '=' thành 'IN'
SELECT ten_sinh_vien 
FROM danh_sach_lop 
WHERE id_sinh_vien IN (
    SELECT id_nguoi_dung 
    FROM dang_ky_khoa_hoc 
    WHERE ma_mon_hoc = 'CS101'
);

2. Sử dụng ANY hoặc SOME

Từ khóa ANY (hoặc đồng nghĩa là SOME) được dùng kết hợp với các toán tử so sánh. Điều kiện sẽ đúng nếu phép so sánh khớp với bất kỳ giá trị nào trong tập kết quả của truy vấn con.

SELECT ten_san_pham 
FROM kho_hang 
WHERE gia_ban > ANY (
    SELECT gia_niem_yet 
    FROM bang_gia_chi_tiet 
    WHERE danh_muc = 'DienTu'
);

3. Sử dụng từ khóa ALL

Ngược lại với ANY, từ khóa ALL yêu cầu điều kiện phải đúng với tất cả các giá trị trả về từ truy vấn con.

SELECT ma_nhan_vien 
FROM luong_thang 
WHERE muc_luong > ALL (
    SELECT muc_luong_co_ban 
    FROM quy_dinh_luong 
    WHERE khu_vuc = 'Vung1'
);

4. Giới hạn kết quả trả về bằng LIMIT

Nếu logic nghiệp vụ chỉ yêu cầu lấy một giá trị duy nhất (ví dụ: giá trị mới nhất hoặc cao nhất), bạn có thể thêm LIMIT 1 vào cuối truy vấn con để đảm bảo nó luôn trả về một dòng.

UPDATE ho_so_nhan_su 
SET phong_ban_moi = (
    SELECT id_phong_ban 
    FROM lich_su_cong_tac 
    WHERE id_nv = 105 
    ORDER BY ngay_dieu_dong DESC 
    LIMIT 1
) 
WHERE id_nv = 105;

5. Kiểm tra tính toàn vẹn của dữ liệu

Đôi khi lỗi này phát sinh do dữ liệu bị trùng lặp không mong muốn. Trong trường hợp này, bạn cần rà soát lại các ràng buộc (constraints) hoặc logic ghi dữ liệu để đảm bảo tính duy nhất. Việc sử dụng từ khóa DISTINCT trong truy vấn con cũng có thể giúp loại bỏ các giá trị trùng lặp nếu chúng có cùng giá trị.

CASE 
    WHEN ma_hoc_phan IN (
        SELECT DISTINCT ten_mon 
        FROM bang_diem 
        WHERE mssv = '20230001'
    ) 
    THEN 'Da_Hoc' 
    ELSE 'Chua_Hoc' 
END;

Tổng kết toán tử

  • IN: Tương đương với = ANY.
  • ANY/SOME: So sánh với bất kỳ giá trị nào trong danh sách.
  • ALL: So sánh với tất cả các giá trị trong danh sách.

Thẻ: mysql sql Database Query Optimization Error Handling

Đăng vào ngày 11 tháng 11 lúc 10:18