今天給大家分享一下我自己和身邊人摔過的跤,來看看這幾個“易錯知識點”,你中過招沒?
1. NULL值:它不是“0”或“空字符串”,是“未知”
這大概是所有SQL初學(xué)者的第一個噩夢。NULL不等于零,也不等于空字符串,它表示“未知”。所以,用 去判斷NULL,基本是白費力氣。=
-- 錯誤示范:這樣查不出任何結(jié)果
SELECT * FROM Employees
WHERE ManagerId = NULL;
-- 正確姿勢:使用 IS NULL 或 IS NOT NULL
SELECT * FROM Employees
WHERE ManagerId IS NULL;
-- 另一個常見坑:任何與NULL的運算,結(jié)果還是NULL
SELECT 10 + NULL; -- 結(jié)果是 NULL
SELECT 'Hello ' + NULL; -- 結(jié)果還是 NULL
想象一下,你要算團隊平均獎金,如果有人獎金是NULL,你沒處理,整個平均值可能就廢了。
-- 假設(shè)Bonus列有NULL值
SELECT AVG(Bonus) FROM Employees; -- 結(jié)果可能不準(zhǔn),因為NULL不參與運算
SELECT AVG(ISNULL(Bonus, 0)) FROM Employees; -- 先用ISNULL把NULL轉(zhuǎn)成0再算
2. 在WHERE子句里對字段做計算
這個坑性能損耗極大。當(dāng)你對表字段使用函數(shù)或者運算時,SQL很可能就無法使用索引了,導(dǎo)致全表掃描。
-- 錯誤示范:假設(shè)CreateTime字段有索引
SELECT * FROM Orders
WHERE YEAR(CreateTime) = 2023;
-- 正確姿勢:讓計算發(fā)生在等式的另一邊,保持字段“干凈”
SELECT * FROM Orders
WHERE CreateTime >= '2023-01-01' AND CreateTime < '2024-01-01';
上面兩句結(jié)果一樣,但下面那句能命中索引,查詢速度天差地別。記住,要把字段當(dāng)“祖宗”一樣供著,盡量別對它動手動腳。
3. JOIN 和 WHERE 的過濾順序
很多人以為LEFT JOIN(左連接)時,條件寫在ON里和寫在WHERE里是一樣的。大錯特錯!
-- 場景:找出所有員工,以及他們的部門名(即使沒有部門也顯示)
-- 假設(shè)我們只想看‘技術(shù)部’的員工,但其他部門的員工也要顯示為NULL
-- 錯誤寫法:這樣寫,LEFT JOIN 實際上變成了 INNER JOIN
SELECT e.Name, d.DepartmentName
FROM Employees e
LEFT JOIN Departments d ON e.DepartmentId = d.Id
WHERE d.DepartmentName = '技術(shù)部'; -- WHERE子句會過濾掉部門為NULL的行!
-- 正確寫法:把針對右表的過濾條件放在ON里
SELECT e.Name, d.DepartmentName
FROM Employees e
LEFT JOIN Departments d ON e.DepartmentId = d.Id AND d.DepartmentName = '技術(shù)部';
簡單記:ON是連接前過濾,WHERE是連接后過濾。對于LEFT JOIN,放在ON里能保住左表的所有記錄,放在WHERE里就可能“誤殺”。
4. COUNT(*) 和 COUNT(字段名) 的區(qū)別
這倆兄弟看著像,脾氣可不一樣。
-- 統(tǒng)計表中有多少條記錄
SELECT COUNT(*) FROM Employees; -- 計算所有行的數(shù)量,不管列里是不是NULL
-- 統(tǒng)計某個字段非空值的數(shù)量
SELECT COUNT(ManagerId) FROM Employees; -- 只計算 ManagerId 不是 NULL 的行
所以,如果你的字段允許為NULL, COUNT(字段名)的結(jié)果很可能就小于 COUNT(*)。 用的時候得心里有數(shù)。
5. 字符串比較的大小寫和空格
SQL的默認設(shè)置下,字符串比較是不區(qū)分大小寫的,但這依賴于你的數(shù)據(jù)庫排序規(guī)則(Collation)。有時候你覺得它該區(qū)分,它卻不區(qū)分,很讓人困惑。
-- 在默認不區(qū)分大小寫的數(shù)據(jù)庫里,這個查詢會返回 'apple', 'APPLE', 'Apple' 所有行
SELECT * FROM Fruits WHERE Name = 'apple';
-- 如果你真想精確匹配大小寫,可以這樣做:
SELECT * FROM Fruits WHERE Name = 'apple' COLLATE SQL_Latin1_General_CP1_CS_AS; -- CS表示Case Sensitive(區(qū)分大小寫)
另外,結(jié)尾的空格也經(jīng)常被忽略。
-- ‘Hello’ 和 ‘Hello ’(后面有個空格)在比較時很可能被認為是相等的。
最后說一句
這些知識點單拎出來看都不難,但混在復(fù)雜的業(yè)務(wù)邏輯里,一不小心就會成為隱蔽的bug。 我自己的經(jīng)驗是,多寫多錯,多錯多記,下次下筆之前,心里先打個問號:“這里有沒有我熟悉的坑?”
扎實的基礎(chǔ),往往就體現(xiàn)在對這些細節(jié)的處理上。 共勉!
該文章在 2025/11/6 15:14:40 編輯過