Nếu Order
được lập chỉ mục, bạn có thể nhận được số bị thiếu đầu tiên với SQL mà không cần đọc bảng hoàn chỉnh bằng cách sử dụng loại trừ LEFT JOIN:
SELECT t1.`Order` + 1 AS firstMissingOrder
FROM tabla t1
LEFT JOIN tabla t2 ON t2.`Order` = t1.`Order` + 1
WHERE t2.`Order` IS NULL
AND t1.`Order` <> (SELECT MAX(`Order`) FROM tabla)
ORDER BY t1.`Order`
LIMIT 1
hoặc (có thể trực quan hơn)
SELECT t1.`Order` + 1 AS firstMissingOrder
FROM tabla t1
WHERE NOT EXISTS (
SELECT 1
FROM tabla t2
WHERE t2.`Order` = t1.`Order` + 1
)
AND t1.`Order` <> (SELECT MAX(`Order`) FROM tabla)
ORDER BY t1.`Order`
LIMIT 1
Truy vấn thứ hai sẽ được MySQL chuyển đổi thành truy vấn đầu tiên. Vì vậy, chúng thực tế ngang nhau.
Cập nhật
Strawberry đề cập đến một điểm hay:Số đầu tiên còn thiếu có thể là 1
, không được đề cập trong truy vấn của tôi. Nhưng tôi không thể tìm ra giải pháp, giải pháp này vừa - thanh lịch vừa nhanh chóng.
Chúng ta có thể đi theo hướng ngược lại và tìm kiếm số đầu tiên sau một khoảng cách. Nhưng sẽ cần tham gia lại bảng để tìm số hiện có cuối cùng trước khoảng cách đó.
SELECT IFNULL(MAX(t3.`Order`) + 1, 1) AS firstMissingOrder
FROM tabla t1
LEFT JOIN tabla t2 ON t2.`Order` = t1.`Order` - 1
LEFT JOIN tabla t3 ON t3.`Order` < t1.`Order`
WHERE t1.`Order` <> 1
AND t2.`Order` IS NULL
GROUP BY t1.`Order`
ORDER BY t1.`Order`
LIMIT 1
MySQL (trong trường hợp của tôi là MariaDB 10.0.19) không thể tối ưu hóa truy vấn đó đúng cách. Mất khoảng một giây trên bảng hàng 1M được lập chỉ mục (PK), ngay cả khi số đầu tiên bị thiếu là 9. Tôi mong máy chủ ngừng tìm kiếm sau t1.Order=10
, nhưng nó không kết nối để làm điều đó.
Một cách khác, nhanh nhưng trông xấu (IMHO), là sử dụng truy vấn ban đầu trong một lựa chọn con chỉ khi Order=1
tồn tại. Nếu không, hãy trả về 1
.
SELECT CASE
WHEN NOT EXISTS (SELECT 1 FROM tabla WHERE `Order` = 1) THEN 1
ELSE (
SELECT t1.`Order` + 1 AS firstMissingOrder
FROM tabla t1
LEFT JOIN tabla t2 ON t2.`Order` = t1.`Order` + 1
WHERE t2.`Order` IS NULL
AND t1.`Order` <> (SELECT MAX(`Order`) FROM tabla)
ORDER BY t1.`Order`
LIMIT 1
)
END AS firstMissingOrder
Hoặc sử dụng UNION
SELECT 1 AS firstMissingOrder FROM (SELECT 1) dummy WHERE NOT EXISTS (SELECT 1 FROM tabla WHERE `Order` = 1)
UNION ALL
SELECT firstMissingOrder FROM (
SELECT t1.`Order` + 1 AS firstMissingOrder
FROM tabla t1
LEFT JOIN tabla t2 ON t2.`Order` = t1.`Order` + 1
WHERE t2.`Order` IS NULL
AND t1.`Order` <> (SELECT MAX(`Order`) FROM tabla)
ORDER BY t1.`Order`
LIMIT 1
) sub
LIMIT 1