長期深耕 SQL Server 的開發者轉向 MySQL 時,INSERT、UPDATE、DELETE 的語法細節差異往往是「隱形坑」。本文從遷移視角出發,對比兩大數據庫在批量插入、連接更新、UPSERT、截斷表、日期處理等高頻場景的異同,提供可直接落地的最佳實踐模板。
| 操作 | SQL Server (T-SQL) | MySQL | 關鍵差異點 |
|---|---|---|---|
| 語句結束符 | 可選 ; (批次分隔用 GO) | 強制 ; 分隔每條語句 | MySQL 多語句必須分號,否則語法錯誤 |
| 自增列顯式插入 | SET IDENTITY_INSERT ON 後可插 | 直接允許插入指定值 | MySQL 遷移數據極其方便,無需開關 |
| 複製表結構+數據 | SELECT * INTO new_t FROM old_t | CREATE TABLE new_t SELECT * FROM old_t | MySQL 無 INTO,用 CREATE ... SELECT |
| 僅複製結構 | SELECT * INTO new_t FROM old_t WHERE 1=2 | CREATE TABLE new_t SELECT * FROM old_t WHERE 1=2 | 同理,WHERE 1=2 過濾所有行 |
| 批量插入語法 | INSERT ... VALUES (...), (...) (2008+) | 原生支援值列表批量 | MySQL 效能極高,萬條級秒級入庫 |
| 連接更新 | UPDATE t1 SET c1=t2.c1 FROM t1 JOIN t2 ON ... | UPDATE t1 JOIN t2 ON ... SET t1.c1=t2.c1 | MySQL JOIN 在 SET 前,語序不同 |
| UPSERT (存在更新/不存在插入) | MERGE / IF EXISTS UPDATE ELSE INSERT | INSERT ... ON DUPLICATE KEY UPDATE / REPLACE INTO | MySQL 專有高效語法,單語句原子完成 |
| 清空表 | TRUNCATE TABLE | TRUNCATE TABLE | 一致:重置自增,不可回滾,不觸發觸發器 |
INSERT INTO `order` (order_no, user_id, amount, status, create_time)
VALUES ('ORD202401001', 1001, 299.00, 1, NOW());
INSERT INTO `order` (order_no, user_id, amount, status, create_time) VALUES
('ORD202401002', 1001, 199.00, 1, NOW()),
('ORD202401003', 1002, 399.00, 1, NOW()),
('ORD202401004', 1003, 99.00, 1, NOW());
-- 萬條數據單次執行通常 < 1 秒,極大減少網絡往返
-- 結構+數據
CREATE TABLE `order_archive` SELECT * FROM `order`;
-- 僅結構
CREATE TABLE `order_archive` SELECT * FROM `order` WHERE 1=2;
-- 指定列複製
CREATE TABLE `order_summary` SELECT order_no, user_id, amount FROM `order` WHERE status=2;
UPDATE `order` SET status = 2, pay_time = NOW() WHERE order_no = 'ORD202401001';
-- 將用戶等級同步到訂單冗余字段
UPDATE `order` o
JOIN `user` u ON o.user_id = u.id
SET o.user_level = u.level
WHERE o.status = 1;
INSERT ... ON DUPLICATE KEY UPDATE(核心神器)-- 表有唯一鍵 uk_order_no (order_no)
INSERT INTO `order` (order_no, user_id, amount, status, update_time) VALUES
('ORD202401001', 1001, 299.00, 2, NOW()),
('ORD202401005', 1004, 159.00, 1, NOW())
ON DUPLICATE KEY UPDATE
amount = VALUES(amount),
status = VALUES(status),
update_time = VALUES(update_time);
-- 存在則更新指定列,不存在則插入。原子性、高效、無競爭。
REPLACE INTO 簡化版(全列覆蓋)REPLACE INTO `order` (order_no, user_id, amount, status, create_time, update_time) VALUES
('ORD202401001', 1001, 329.00, 2, '2024-01-01 10:00:00', NOW());
-- 本質:DELETE 舊行 + INSERT 新行。影響行數:插入=1,更新=2(删+增)。
-- ⚠️ 缺點:未指定列重置為默認值,自增 ID 可能改變,外鍵關係斷裂風險。
DELETE FROM `order` WHERE status = 0 AND create_time < DATE_SUB(NOW(), INTERVAL 30 DAY);
TRUNCATE TABLE `order_temp`; -- 瞬時完成,不可回滾,重置 AUTO_INCREMENT
現象: 向 DATETIME/TIMESTAMP 列插入非法日期字符串(如 '2024-13-01'、'abc'),MySQL 不報錯,靜默存入 '0000-00-00 00:00:00'(零值日期)。
後果: 應用層 ORM/JDBC 讀取時拋異常(如 java.sql.SQLException: Value '0000-00-00' can not be represented as java.sql.Timestamp),導致業務崩潰。
根因: MySQL 預設非嚴格模式下對非法日期「寬容處理」。
解決方案(三選一):
my.cnf 設 sql_mode=STRICT_TRANS_TABLES,...,非法日期直接報錯 ERROR 1292 (22007): Incorrect datetime value。jdbc:mysql://host:3306/db?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull
# zeroDateTimeBehavior 選項:
# convertToNull → 讀取零值日期返回 null(推薦)
# round → 讀取零值日期返回 0001-01-01 00:00:00
# exception → 拋異常(默認行為)
MySQL 增刪改語法簡潔高效,批量 VALUES、ON DUPLICATE KEY UPDATE、自增列顯式插入 是遷移後的「效率倍增器」;強制分號、連接更新語序、日期零值陷阱 是必須內化的「遷移護身符」。建立 sql_mode=STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION 基線,配合 zeroDateTimeBehavior=convertToNull 連接參數,可消除 90% 遷移期數據質量隱患。