統計查詢常需排除已訂購用戶,直覺寫法是 NOT IN (子查詢) 或將子查詢結果拼接成超長 IN (...) 列表。後者雖避開子查詢效能問題,卻可能觸發 max_allowed_packet 限制導致報錯。本文剖析風險成因與解決之道。
SELECT COUNT(1)
FROM day_resinlog_2012_06_12
WHERE mobile_number NOT IN (
SELECT DISTINCT mobile_number FROM tbl_cartoon_order
);
NOT IN 子查詢優化較弱,大資料量下極慢。# 應用程式先查出手機號列表
SELECT DISTINCT mobile_number FROM tbl_cartoon_order;
# 再拼接成超長 SQL
SELECT COUNT(1)
FROM day_resinlog_2012_06_12
WHERE mobile_number NOT IN ('13800000001','13800000002', ...);
max_allowed_packet(預設 4MB/16MB),直接報錯 Packet too large。mysqld、mysql client、mysqldump 等所有連線。INSERT、REPLACE、LOAD DATA、超長 IN 列表皆會觸發。[mysqld]
max_allowed_packet = 64M # 視實際最大 SQL 長度調整
# 臨時生效(無需重啟)
SET GLOBAL max_allowed_packet = 67108864;
注意:Client 端也需同步調大,否則連線仍受限。
SELECT COUNT(1)
FROM day_resinlog_2012_06_12 d
LEFT JOIN tbl_cartoon_order o ON d.mobile_number = o.mobile_number
WHERE o.mobile_number IS NULL;
NOT EXISTS/LEFT JOIN ... IS NULL 優化極佳。mobile_number 索引,百萬級秒出結果。JOIN 或 NOT IN (SELECT ... FROM temp)。LEFT JOIN ... IS NULL 或 NOT EXISTS。max_allowed_packet。Packet too large 錯誤。