需根據另一張表的資料更新本表欄位時,SQL Server 提供 UPDATE ... FROM ... JOIN 語法,比子查詢更直觀、效能更佳。本文詳細說明多種寫法、適用場景與注意事項。
-- 表 A1 (目標)
EmpID Name Bonus
1 Tom NULL
2 Jerry NULL
-- 表 A2 (來源)
ID BonusAmt
1 1100.00
2 2500.00
-- 目標:A1.Bonus = A2.BonusAmt WHERE A1.EmpID = A2.ID
UPDATE a1
SET a1.Bonus = a2.BonusAmt
FROM A1 a1
INNER JOIN A2 a2 ON a1.EmpID = a2.ID;
UPDATE 目標別名 SET ... FROM 目標別名 JOIN 來源別名 ON ...SET 使用來源表別名欄位。UPDATE A1
SET Bonus = A2.BonusAmt
FROM A1, A2
WHERE A1.EmpID = A2.ID;
UPDATE A1
SET Bonus = (SELECT BonusAmt FROM A2 WHERE A2.ID = A1.EmpID)
WHERE EXISTS (SELECT 1 FROM A2 WHERE A2.ID = A1.EmpID);
WITH Src AS (
SELECT ID, BonusAmt * 1.1 AS AdjustedBonus -- 來源端先計算調整
FROM A2
WHERE BonusAmt > 1000
)
UPDATE a1
SET a1.Bonus = Src.AdjustedBonus
FROM A1 a1
INNER JOIN Src ON a1.EmpID = Src.ID;
| 議題 | 說明 |
|---|---|
| 目標表別名 | UPDATE a1 SET ... FROM A1 a1 ... 中的 a1 必須與 FROM 子句中的目標表別名一致,否則會更新錯誤表或報錯。 |
| 多重匹配 | 若來源表對目標表有多筆匹配,SQL Server 會隨機取一筆更新 (不報錯),資料不一致風險極高。務必保證聯結鍵唯一 或用 ROW_NUMBER() 取第一筆。 |
| NULL 處理 | SET col = src.col 若來源為 NULL 會將目標設為 NULL。需保留原值請用 SET col = ISNULL(src.col, col) 或 CASE WHEN src.col IS NOT NULL THEN src.col ELSE col END。 |
| 觸發器 | 會啟動目標表的 AFTER UPDATE 觸發器,INSERTED/DELETED 反映更新後/前狀態。 |
| 交易與鎖 | 大量更新建議分批 (如每 5000 列) 配合 WAITFOR DELAY,避免長時間鎖定造成阻塞。 |
-- 將 Orders 表中已出貨訂單的 Customer 表 LastOrderDate 更新為最新出貨日期
WITH LatestShip AS (
SELECT CustomerID, MAX(ShipDate) AS LastShipDate
FROM Orders
WHERE ShipDate IS NOT NULL
GROUP BY CustomerID
)
UPDATE c
SET c.LastOrderDate = ls.LastShipDate,
c.TotalOrders = c.TotalOrders + 1
FROM Customers c
INNER JOIN LatestShip ls ON c.CustomerID = ls.CustomerID
WHERE c.IsActive = 1;
| 寫法 | 執行時間 | 邏輯讀取 | 備註 |
|---|---|---|---|
| UPDATE ... FROM ... INNER JOIN | 1.2 秒 | 45,000 | 最佳 |
| UPDATE ... FROM table, table WHERE | 1.3 秒 | 46,000 | 次佳 |
| 子查詢 | 8.7 秒 | 320,000 | 差 |
掌握 UPDATE ... FROM ... JOIN 寫法,能撰寫高效、可讀、安全的多表更新邏輯,是 T-SQL 開發必備技能。