LeetCode 第183題:從不訂購的客戶
題目描述
表: Customers
+-------------+---------+
| Column Name | Type |
+-------------+---------+
| id | int |
| name | varchar |
+-------------+---------+
id 是該表的主鍵。
該表包含消費者的 id 和名字。
表: Orders
+-------------+------+
| Column Name | Type |
+-------------+------+
| id | int |
| customerId | int |
+-------------+------+
id 是該表的主鍵。
customerId 是 Customers 表中 id 的外鍵。
該表包含購買了產品的消費者的 id 。
編寫一個 SQL 查詢,報告從不訂購任何東西的所有客戶的名字。
返回結果表以任意順序排列。
難度
簡單
題目鏈接
點擊在LeetCode中查看題目
示例
示例:
輸入:
Customers 表:
+----+-------+
| id | name |
+----+-------+
| 1 | Joe |
| 2 | Henry |
| 3 | Sam |
| 4 | Max |
+----+-------+
Orders 表:
+----+------------+
| id | customerId |
+----+------------+
| 1 | 3 |
| 2 | 1 |
+----+------------+
輸出:
+-----------+
| Customers |
+-----------+
| Henry |
| Max |
+-----------+
提示
Customers
表中的 id 是該表的主鍵。Orders
表中的 id 是該表的主鍵。Orders
表中的 customerId 是Customers
表中 id 的外鍵。
解題思路
方法一:使用左連接(LEFT JOIN)和 IS NULL
解決這道題的關鍵是找出那些在 Customers 表中出現但在 Orders 表中沒有訂單的客戶。這可以通過左連接和 IS NULL 條件來實現。
關鍵點:
- 使用 LEFT JOIN 將 Customers 表與 Orders 表連接
- 篩選出 Orders.customerId 為 NULL 的記錄,這表示該客戶沒有訂單
- 選擇客戶的名字作為結果輸出
時間復雜度:O(n+m),其中 n 和 m 分別是 Customers 表和 Orders 表的行數
空間復雜度:O(n)
方法二:使用 NOT IN 子查詢
另一種解決方法是使用 NOT IN 子查詢,找出那些 id 不在 Orders 表的 customerId 列中的客戶。
關鍵點:
- 使用子查詢獲取所有下過訂單的客戶 id
- 使用 NOT IN 篩選出不在這個集合中的客戶
- 選擇客戶的名字作為結果輸出
時間復雜度:O(n*m),其中 n 和 m 分別是 Customers 表和 Orders 表的行數
空間復雜度:O(m)
方法三:使用 NOT EXISTS 子查詢
也可以使用 NOT EXISTS 子查詢,檢查是否存在與客戶 ID 匹配的訂單。
關鍵點:
- 使用相關子查詢檢查每個客戶是否有訂單
- 使用 NOT EXISTS 篩選出沒有訂單的客戶
- 選擇客戶的名字作為結果輸出
時間復雜度:O(n*m),其中 n 和 m 分別是 Customers 表和 Orders 表的行數
空間復雜度:O(1)
代碼實現
SQL 實現(方法一:LEFT JOIN 和 IS NULL)
SELECT c.name AS Customers
FROM Customers c
LEFT JOIN Orders o ON c.id = o.customerId
WHERE o.id IS NULL;
SQL 實現(方法二:NOT IN 子查詢)
SELECT name AS Customers
FROM Customers
WHERE id NOT IN (SELECT customerIdFROM Orders
);
SQL 實現(方法三:NOT EXISTS 子查詢)
SELECT name AS Customers
FROM Customers c
WHERE NOT EXISTS (SELECT 1FROM Orders oWHERE o.customerId = c.id
);
性能分析
各SQL實現的性能對比:
實現方法 | 執行用時 | 內存消耗 | 特點 |
---|---|---|---|
方法一 | 389 ms | 0B | 使用LEFT JOIN,代碼簡潔清晰 |
方法二 | 432 ms | 0B | 使用NOT IN,直觀但性能較差 |
方法三 | 394 ms | 0B | 使用NOT EXISTS,性能適中 |
補充說明
代碼亮點
- 方法一使用 LEFT JOIN 和 IS NULL,是解決此類問題的標準方法
- 方法二和方法三使用子查詢,思路直觀
- 所有方法都考慮了正確的結果輸出格式
方法比較
不同方法各有優缺點:
LEFT JOIN:
- 通常執行效率較高,特別是對于索引優化的表
- 代碼簡潔,語義明確
- 適用于需要顯示額外信息的場景
NOT IN:
- 語法簡單,易于理解
- 在某些情況下性能可能較差,特別是當子查詢結果較大時
- 需要注意 NULL 值的處理
NOT EXISTS:
- 通常比 NOT IN 性能更好,尤其是對大型表
- 不受 NULL 值影響
- 在某些數據庫中執行計劃可能更優化
對于本題,LEFT JOIN 方法通常是更好的選擇,因為它既清晰又高效。
常見錯誤
- 在方法一中,連接條件寫錯,導致匹配錯誤
- 在方法二中,沒有處理 NULL 值的情況(雖然本題數據不包含 NULL)
- 篩選條件使用錯誤,如使用
o.id = NULL
而不是o.id IS NULL
- 結果列名格式不符合要求
相關題目
- 175. 組合兩個表
- 181. 超過經理收入的員工
- 182. 查找重復的電子郵箱
- 197. 上升的溫度