業務中頻繁需要「判斷某記錄是否存在」,常見寫法性能差異巨大。本文對比三種主流方案,給出最佳實踐結論。
SELECT COUNT(*) FROM tablename WHERE col = 'value';
缺點:即使找到第一條匹配行,MySQL 仍繼續掃描全表/索引統計總數,I/O 與 CPU 浪費嚴重。
SELECT 1 FROM tablename WHERE col = 'value' LIMIT 1;
優點:
LIMIT 1 讓優化器在找到第一條匹配行後立即停止1,避免傳輸業務列數據SELECT * FROM tablename WHERE col = 'value' LIMIT 1;
-- 應用層判斷 resultSet.next()
缺點:傳輸完整行數據(含大字段),網絡與內存開銷大,純屬浪費。
$stmt = $pdo->prepare("SELECT 1 FROM users WHERE email = ? LIMIT 1");
$stmt->execute([$email]);
$exists = $stmt->fetchColumn(); // false 或 '1'
try (PreparedStatement ps = conn.prepareStatement(
"SELECT 1 FROM users WHERE email = ? LIMIT 1")) {
ps.setString(1, email);
try (ResultSet rs = ps.executeQuery()) {
boolean exists = rs.next();
}
}
var exists bool
err := db.QueryRow("SELECT 1 FROM users WHERE email = ? LIMIT 1", email).Scan(&exists)
// exists = true/false,ErrNoRows 表示不存在
若在大查詢中作為條件,用 EXISTS 更語義化且性能相當:
SELECT * FROM orders o
WHERE EXISTS (
SELECT 1 FROM users u WHERE u.id = o.user_id AND u.status = 1
);
MySQL 優化器會將 EXISTS (SELECT 1 ...) 轉化為 Semi-Join,同樣在首次匹配後停止。
| 寫法 | 掃描行數 | 網絡傳輸 | 語義清晰度 | 推薦指數 |
|---|---|---|---|---|
| COUNT(*) | 全表/全索引 | 1 行數值 | 低 | ⭐ |
| SELECT * LIMIT 1 | 首匹配即停 | 整行數據 | 中 | ⭐⭐ |
| SELECT 1 LIMIT 1 | 首匹配即停 | 1 字節 | 高 | ⭐⭐⭐⭐⭐ |
| EXISTS 子查詢 | 首匹配即停 | 無(作為條件) | 高 | ⭐⭐⭐⭐ |
核心原則:只取需要的、只掃必要的。
SELECT 1 ... LIMIT 1是判斷存在性的標準最優解。