Transact-SQL数据的更新 ---语法详解与实战案例(SQL Server 2019)
⚠️ 重要提醒:数据更新操作不可逆!务必在执行前:
- 使用
BEGIN TRANSACTION和ROLLBACK测试- 备份重要数据
- 在生产环境使用
WHERE子句时务必双重检查- 避免无条件的
DELETE或UPDATE
一、插入数据 ------ INSERT
1.1 插入单行数据
✅ 基本语法:
sql
INSERT INTO table_name (column1, column2, ...)
VALUES (value1, value2, ...);
- 列名顺序可自定义,但必须与值顺序对应
- 若插入所有列,可省略列名(不推荐,可维护性差)
- 字符串用单引号,日期推荐用
'YYYY-MM-DD'格式
📌 案例代码:
sql
-- 创建学生表用于演示
IF OBJECT_ID('Students', 'U') IS NOT NULL DROP TABLE Students;
GO
CREATE TABLE Students (
StudentID INT PRIMARY KEY IDENTITY(1,1), -- 自增主键
Name NVARCHAR(50) NOT NULL,
Gender CHAR(1) CHECK (Gender IN ('M', 'F')),
BirthDate DATE,
ClassName NVARCHAR(50),
EnrollmentDate DATE DEFAULT GETDATE(),
Email NVARCHAR(100)
);
GO
-- 插入单行数据(指定列名)
INSERT INTO Students (Name, Gender, BirthDate, ClassName, Email)
VALUES ('张三', 'M', '2005-03-15', '高三(1)班', 'zhangsan@school.com');
-- 插入单行数据(省略可为空或有默认值的列)
INSERT INTO Students (Name, Gender, ClassName)
VALUES ('李四', 'F', '高三(2)班');
-- 插入单行数据(省略列名 - 必须按表结构顺序,且包含所有非空列)
-- 不推荐!表结构变更后容易出错
INSERT INTO Students
VALUES ('王五', 'M', '2005-07-22', '高三(1)班', GETDATE(), 'wangwu@school.com');
1.2 插入多行数据
✅ 基本语法:
sql
-- 方法1:VALUES 多组值(SQL Server 2008+)
INSERT INTO table_name (column1, column2, ...)
VALUES
(value1_1, value1_2, ...),
(value2_1, value2_2, ...),
(value3_1, value3_2, ...);
-- 方法2:INSERT INTO ... SELECT ... FROM ...(从其他表或查询结果插入)
INSERT INTO table_name (column1, column2, ...)
SELECT column1, column2, ...
FROM source_table
WHERE condition;
📌 案例代码:
sql
-- 方法1:使用多组VALUES插入多行
INSERT INTO Students (Name, Gender, BirthDate, ClassName, Email)
VALUES
('赵六', 'F', '2005-11-08', '高二(1)班', 'zhaoliu@school.com'),
('钱七', 'M', '2005-09-30', '高二(2)班', 'qianqi@school.com'),
('孙八', 'F', '2005-12-12', '高二(1)班', NULL); -- Email可为NULL
-- 方法2:从查询结果插入(创建临时表并插入)
IF OBJECT_ID('NewStudents', 'U') IS NOT NULL DROP TABLE NewStudents;
GO
CREATE TABLE NewStudents (
TempID INT IDENTITY(1,1),
FullName NVARCHAR(50),
Sex CHAR(1),
DOB DATE,
Class NVARCHAR(50)
);
GO
INSERT INTO NewStudents (FullName, Sex, DOB, Class)
VALUES
('周九', 'M', '2006-01-15', '高一(1)班'),
('吴十', 'F', '2006-02-20', '高一(2)班');
-- 从NewStudents表插入到Students表
INSERT INTO Students (Name, Gender, BirthDate, ClassName)
SELECT FullName, Sex, DOB, Class
FROM NewStudents;
-- 方法3:插入计算值或系统函数
INSERT INTO Students (Name, Gender, ClassName, EnrollmentDate)
VALUES
('郑十一', 'M', '高三(3)班', DATEADD(DAY, -30, GETDATE())), -- 30天前入学
('王十二', 'F', '高三(3)班', GETDATE()); -- 今天入学
二、修改数据 ------ UPDATE
2.1 修改单行数据
✅ 基本语法:
sql
UPDATE table_name
SET column1 = value1, column2 = value2, ...
WHERE condition; -- 必须有唯一条件,否则会修改多行!
⚠️ 危险操作 :没有
WHERE子句会更新表中所有行!
📌 案例代码:
sql
-- 查看当前数据
SELECT * FROM Students WHERE Name = '张三';
-- 修改单行数据(通过主键)
UPDATE Students
SET Email = 'zhangsan_new@school.com',
ClassName = '高三(2)班' -- 转班
WHERE StudentID = 1; -- 假设张三的ID是1
-- 修改单行数据(通过唯一条件)
UPDATE Students
SET Email = 'lisi_updated@school.com'
WHERE Name = '李四' AND ClassName = '高三(2)班'; -- 防止重名
-- 修改单行数据(使用表达式)
UPDATE Students
SET BirthDate = DATEADD(YEAR, -1, BirthDate) -- 减1岁(出生日期+1年)
WHERE Name = '王五';
-- 验证修改结果
SELECT StudentID, Name, Email, ClassName, BirthDate
FROM Students
WHERE StudentID IN (1, 2, 3);
2.2 修改多行数据
✅ 基本语法:
sql
UPDATE table_name
SET column1 = value1, column2 = value2, ...
WHERE condition; -- 条件匹配多行
📌 案例代码:
sql
-- 修改多行:给所有高二学生添加邮箱后缀
UPDATE Students
SET Email = Name + '@gaoer.school.com'
WHERE ClassName LIKE '高二%';
-- 修改多行:给所有没有邮箱的学生设置默认邮箱
UPDATE Students
SET Email = LOWER(Name) + '.default@school.com'
WHERE Email IS NULL;
-- 修改多行:使用CASE表达式根据不同条件设置不同值
UPDATE Students
SET ClassName = CASE
WHEN ClassName = '高三(1)班' THEN '毕业班A'
WHEN ClassName = '高三(2)班' THEN '毕业班B'
WHEN ClassName LIKE '高二%' THEN '预备毕业班'
ELSE ClassName
END,
EnrollmentDate = CASE
WHEN ClassName LIKE '高三%' THEN DATEADD(YEAR, -1, GETDATE())
ELSE EnrollmentDate
END
WHERE ClassName LIKE '高三%' OR ClassName LIKE '高二%';
-- 修改多行:增加所有学生的年龄(通过修改出生日期)
UPDATE Students
SET BirthDate = DATEADD(YEAR, -1, BirthDate)
WHERE StudentID <= 10; -- 仅修改前10名学生
-- 查看修改结果
SELECT StudentID, Name, ClassName, Email, BirthDate
FROM Students
ORDER BY StudentID;
三、删除数据 ------ DELETE
3.1 删除部分数据
✅ 基本语法:
sql
DELETE FROM table_name
WHERE condition; -- 必须有!否则删除所有数据
⚠️ 危险操作 :没有
WHERE子句会删除表中所有数据!
📌 案例代码:
sql
-- 删除单行数据
DELETE FROM Students
WHERE StudentID = 5; -- 删除ID为5的学生
-- 删除多行数据(按条件)
DELETE FROM Students
WHERE ClassName = '预备毕业班'; -- 删除所有预备毕业班学生
-- 删除多行数据(按范围)
DELETE FROM Students
WHERE BirthDate > '2006-01-01'; -- 删除2006年后出生的学生
-- 删除多行数据(使用IN)
DELETE FROM Students
WHERE StudentID IN (6, 7, 8); -- 删除指定ID的学生
-- 删除多行数据(使用子查询)
DELETE FROM Students
WHERE StudentID IN (
SELECT StudentID FROM Students WHERE Email LIKE '%default%'
); -- 删除所有默认邮箱的学生
-- 验证删除结果
SELECT * FROM Students ORDER BY StudentID;
3.2 删除数据表中所有的数据
✅ 两种方法:
sql
-- 方法1:DELETE FROM(可带WHERE,无WHERE则删全部)
DELETE FROM table_name;
-- 方法2:TRUNCATE TABLE(更快,不能回滚,重置自增ID)
TRUNCATE TABLE table_name;
🆚 DELETE vs TRUNCATE 对比:
特性 DELETE TRUNCATE 速度 慢(逐行删除) 快(释放数据页) 事务 可回滚 不可回滚(在事务中除外) 触发器 触发 不触发 WHERE子句 支持 不支持 自增ID 不重置 重置为初始值 权限 需要 DELETE 权限 需要 ALTER 权限
📌 案例代码:
sql
-- 方法1:DELETE FROM 删除所有数据(保留自增ID连续性)
-- 假设当前最大StudentID是12,删除后新插入ID从13开始
DELETE FROM Students;
-- 重新插入测试数据
INSERT INTO Students (Name, Gender, ClassName) VALUES ('测试学生', 'M', '测试班');
SELECT * FROM Students; -- StudentID 应该是13
-- 方法2:TRUNCATE TABLE 删除所有数据(重置自增ID)
TRUNCATE TABLE Students;
-- 重新插入测试数据
INSERT INTO Students (Name, Gender, ClassName) VALUES ('新测试学生', 'F', '新测试班');
SELECT * FROM Students; -- StudentID 重置为1
-- ⚠️ 生产环境慎用!建议先备份
-- 备份方法1:SELECT INTO 备份到新表
SELECT * INTO Students_Backup_20250913 FROM Students;
-- 备份方法2:使用事务测试
BEGIN TRANSACTION;
DELETE FROM Students WHERE StudentID < 10;
-- ROLLBACK; -- 如果结果不对,执行回滚
-- COMMIT; -- 如果结果正确,执行提交
四、综合性实战案例
🎯 案例25:学生管理系统 ------ 数据批量初始化与更新
sql
USE SchoolManagement;
GO
-- 创建学生表、班级表、成绩表
IF OBJECT_ID('Students', 'U') IS NOT NULL DROP TABLE Students;
IF OBJECT_ID('Classes', 'U') IS NOT NULL DROP TABLE Classes;
IF OBJECT_ID('Grades', 'U') IS NOT NULL DROP TABLE Grades;
GO
CREATE TABLE Classes (
ClassID INT PRIMARY KEY IDENTITY(1,1),
ClassName NVARCHAR(50) UNIQUE NOT NULL,
GradeLevel INT, -- 年级:1=高一, 2=高二, 3=高三
TeacherInCharge NVARCHAR(50)
);
GO
CREATE TABLE Students (
StudentID INT PRIMARY KEY IDENTITY(1,1),
Name NVARCHAR(50) NOT NULL,
Gender CHAR(1) CHECK (Gender IN ('M', 'F')),
BirthDate DATE,
ClassID INT REFERENCES Classes(ClassID),
EnrollmentDate DATE DEFAULT GETDATE(),
Status NVARCHAR(20) DEFAULT 'Active' -- Active, Graduated, Dropped
);
GO
CREATE TABLE Grades (
GradeID INT IDENTITY(1,1) PRIMARY KEY,
StudentID INT REFERENCES Students(StudentID),
Subject NVARCHAR(50),
Score DECIMAL(5,2),
ExamDate DATE DEFAULT GETDATE(),
Semester NVARCHAR(20) -- '2024-2025第一学期'
);
GO
-- ========== 数据初始化 ==========
PRINT '🔄 正在初始化班级数据...';
INSERT INTO Classes (ClassName, GradeLevel, TeacherInCharge)
VALUES
('高一(1)班', 1, '张老师'),
('高一(2)班', 1, '李老师'),
('高二(1)班', 2, '王老师'),
('高二(2)班', 2, '赵老师'),
('高三(1)班', 3, '钱老师'),
('高三(2)班', 3, '孙老师');
PRINT '🔄 正在初始化学生数据...';
-- 使用循环插入100名学生(实际项目可用程序生成)
DECLARE @i INT = 1;
WHILE @i <= 100
BEGIN
INSERT INTO Students (Name, Gender, BirthDate, ClassID)
VALUES (
'学生' + RIGHT('00' + CAST(@i AS NVARCHAR(3)), 3),
CASE WHEN @i % 2 = 0 THEN 'F' ELSE 'M' END,
DATEADD(YEAR, -15 - (@i % 3), GETDATE()), -- 年龄15-17岁
1 + (@i % 6) -- 分配到6个班
);
SET @i = @i + 1;
END
PRINT '🔄 正在初始化成绩数据...';
-- 为每个学生插入3门课程成绩
DECLARE @StudentID INT;
DECLARE StudentCursor CURSOR FOR SELECT StudentID FROM Students;
OPEN StudentCursor;
FETCH NEXT FROM StudentCursor INTO @StudentID;
WHILE @@FETCH_STATUS = 0
BEGIN
INSERT INTO Grades (StudentID, Subject, Score, Semester)
VALUES
(@StudentID, '语文', 70 + RAND() * 30, '2024-2025第一学期'),
(@StudentID, '数学', 65 + RAND() * 35, '2024-2025第一学期'),
(@StudentID, '英语', 75 + RAND() * 25, '2024-2025第一学期');
FETCH NEXT FROM StudentCursor INTO @StudentID;
END
CLOSE StudentCursor;
DEALLOCATE StudentCursor;
PRINT '✅ 数据初始化完成!';
PRINT '📊 当前数据统计:';
SELECT
(SELECT COUNT(*) FROM Classes) AS 班级数,
(SELECT COUNT(*) FROM Students) AS 学生数,
(SELECT COUNT(*) FROM Grades) AS 成绩记录数;
GO
-- ========== 数据更新操作 ==========
PRINT '🔄 更新操作:将高一学生年龄统一调整为16岁';
-- 计算目标出生日期
DECLARE @TargetBirthDate DATE = DATEADD(YEAR, -16, GETDATE());
UPDATE s
SET BirthDate = @TargetBirthDate
FROM Students s
JOIN Classes c ON s.ClassID = c.ClassID
WHERE c.GradeLevel = 1;
PRINT '🔄 更新操作:为所有学生添加邮箱';
UPDATE Students
SET Status = 'Active',
-- 邮箱格式:name+id@school.edu
Email = LOWER(Name) + '_' + CAST(StudentID AS NVARCHAR(10)) + '@school.edu'
WHERE Email IS NULL;
PRINT '🔄 更新操作:调整不及格学生成绩(+5分,最高60分)';
UPDATE Grades
SET Score = CASE
WHEN Score < 55 THEN Score + 5
WHEN Score < 60 THEN 60
ELSE Score
END
WHERE Score < 60;
PRINT '✅ 更新操作完成!';
GO
-- ========== 数据删除操作 ==========
PRINT '🔄 删除操作:删除测试用的最后10名学生';
DELETE FROM Grades
WHERE StudentID IN (
SELECT TOP 10 StudentID FROM Students ORDER BY StudentID DESC
);
DELETE FROM Students
WHERE StudentID IN (
SELECT TOP 10 StudentID FROM Students ORDER BY StudentID DESC
);
PRINT '🔄 删除操作:清空成绩表(使用TRUNCATE)';
-- 先备份成绩数据
SELECT * INTO Grades_Backup_20250913 FROM Grades;
TRUNCATE TABLE Grades;
PRINT '✅ 删除操作完成!';
PRINT '📊 最终数据统计:';
SELECT
(SELECT COUNT(*) FROM Students) AS 学生数,
(SELECT COUNT(*) FROM Grades) AS 成绩记录数,
(SELECT COUNT(*) FROM Grades_Backup_20250913) AS 备份成绩数;
GO
🎯 案例26:库存管理系统 ------ 事务处理与安全更新
sql
USE InventorySystem;
GO
-- 创建产品表和库存变动日志表
IF OBJECT_ID('Products', 'U') IS NOT NULL DROP TABLE Products;
IF OBJECT_ID('InventoryLog', 'U') IS NOT NULL DROP TABLE InventoryLog;
GO
CREATE TABLE Products (
ProductID INT PRIMARY KEY IDENTITY(1,1),
ProductName NVARCHAR(100) NOT NULL,
Category NVARCHAR(50),
UnitPrice DECIMAL(10,2),
StockQuantity INT DEFAULT 0, -- 库存数量
LastUpdated DATETIME DEFAULT GETDATE()
);
GO
CREATE TABLE InventoryLog (
LogID INT IDENTITY(1,1) PRIMARY KEY,
ProductID INT,
ChangeType NVARCHAR(20), -- '进货', '销售', '调整'
QuantityChange INT,
OldQuantity INT,
NewQuantity INT,
ChangeDate DATETIME DEFAULT GETDATE(),
Operator NVARCHAR(50),
Remarks NVARCHAR(200)
);
GO
-- ========== 初始化产品数据 ==========
PRINT '🔄 初始化产品数据...';
INSERT INTO Products (ProductName, Category, UnitPrice, StockQuantity)
VALUES
('笔记本电脑', '电子产品', 5000.00, 100),
('无线鼠标', '电子产品', 150.00, 500),
('机械键盘', '电子产品', 800.00, 200),
('办公椅', '家具', 1200.00, 50),
('台灯', '家居用品', 200.00, 300);
SELECT * FROM Products;
GO
-- ========== 安全更新操作(使用事务) ==========
PRINT '🔄 安全操作:销售5台笔记本电脑(使用事务)';
BEGIN TRY
BEGIN TRANSACTION;
DECLARE @ProductID INT = 1; -- 笔记本电脑
DECLARE @SaleQuantity INT = 5;
DECLARE @CurrentStock INT;
DECLARE @NewStock INT;
-- 获取当前库存
SELECT @CurrentStock = StockQuantity FROM Products WHERE ProductID = @ProductID;
-- 检查库存是否足够
IF @CurrentStock < @SaleQuantity
BEGIN
RAISERROR('库存不足!当前库存:%d,需求数量:%d', 16, 1, @CurrentStock, @SaleQuantity);
END
-- 计算新库存
SET @NewStock = @CurrentStock - @SaleQuantity;
-- 更新产品库存
UPDATE Products
SET StockQuantity = @NewStock,
LastUpdated = GETDATE()
WHERE ProductID = @ProductID;
-- 记录库存变动日志
INSERT INTO InventoryLog (ProductID, ChangeType, QuantityChange, OldQuantity, NewQuantity, Operator, Remarks)
VALUES (@ProductID, '销售', -@SaleQuantity, @CurrentStock, @NewStock, '销售员A', '客户订单#20250913001');
COMMIT TRANSACTION;
PRINT '✅ 销售成功!剩余库存:' + CAST(@NewStock AS NVARCHAR(10));
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
PRINT '❌ 操作失败:' + ERROR_MESSAGE();
END CATCH
GO
-- ========== 批量进货操作 ==========
PRINT '🔄 批量进货操作:为所有电子产品增加库存';
BEGIN TRY
BEGIN TRANSACTION;
-- 创建临时表存储进货数据
DECLARE @IncomingStock TABLE (
ProductID INT,
IncomingQuantity INT,
PurchasePrice DECIMAL(10,2)
);
-- 插入进货数据
INSERT INTO @IncomingStock VALUES
(1, 50, 4500.00), -- 笔记本电脑
(2, 200, 120.00), -- 无线鼠标
(3, 100, 650.00); -- 机械键盘
-- 更新库存并记录日志
UPDATE p
SET StockQuantity = p.StockQuantity + i.IncomingQuantity,
LastUpdated = GETDATE()
OUTPUT
inserted.ProductID,
'进货' AS ChangeType,
i.IncomingQuantity AS QuantityChange,
deleted.StockQuantity AS OldQuantity,
inserted.StockQuantity AS NewQuantity,
GETDATE() AS ChangeDate,
'采购员B' AS Operator,
'批量进货' AS Remarks
INTO InventoryLog (ProductID, ChangeType, QuantityChange, OldQuantity, NewQuantity, ChangeDate, Operator, Remarks)
FROM Products p
INNER JOIN @IncomingStock i ON p.ProductID = i.ProductID;
COMMIT TRANSACTION;
PRINT '✅ 批量进货完成!';
-- 显示更新后的库存
SELECT ProductName, StockQuantity FROM Products WHERE Category = '电子产品';
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
PRINT '❌ 批量进货失败:' + ERROR_MESSAGE();
END CATCH
GO
-- ========== 价格调整操作 ==========
PRINT '🔄 价格调整:电子产品降价10%,家具涨价5%';
BEGIN TRANSACTION;
-- 记录价格调整前的数据
SELECT ProductID, ProductName, UnitPrice AS OldPrice
INTO #PriceAdjustmentTemp
FROM Products;
-- 执行价格调整
UPDATE Products
SET UnitPrice = CASE
WHEN Category = '电子产品' THEN UnitPrice * 0.9
WHEN Category = '家具' THEN UnitPrice * 1.05
ELSE UnitPrice
END,
LastUpdated = GETDATE();
-- 显示调整结果
SELECT
p.ProductName,
t.OldPrice,
p.UnitPrice AS NewPrice,
CASE
WHEN p.UnitPrice > t.OldPrice THEN '涨价'
WHEN p.UnitPrice < t.OldPrice THEN '降价'
ELSE '不变'
END AS AdjustmentType,
ROUND(((p.UnitPrice - t.OldPrice) / t.OldPrice) * 100, 2) AS PercentChange
FROM Products p
JOIN #PriceAdjustmentTemp t ON p.ProductID = t.ProductID
WHERE p.UnitPrice <> t.OldPrice;
COMMIT TRANSACTION;
PRINT '✅ 价格调整完成!';
GO
-- ========== 数据清理操作 ==========
PRINT '🔄 数据清理:删除0库存且1年未更新的产品';
-- 先查看将要删除的数据
SELECT ProductID, ProductName, StockQuantity, LastUpdated
FROM Products
WHERE StockQuantity = 0 AND LastUpdated < DATEADD(YEAR, -1, GETDATE());
-- 执行删除(实际项目中建议先备份)
DELETE FROM Products
WHERE StockQuantity = 0 AND LastUpdated < DATEADD(YEAR, -1, GETDATE());
PRINT '✅ 数据清理完成!';
GO
🎯 案例27:数据迁移与转换 ------ 综合INSERT、UPDATE、DELETE
sql
USE DataMigration;
GO
-- 创建旧系统表和新系统表
IF OBJECT_ID('OldCustomers', 'U') IS NOT NULL DROP TABLE OldCustomers;
IF OBJECT_ID('NewCustomers', 'U') IS NOT NULL DROP TABLE NewCustomers;
IF OBJECT_ID('CustomerMapping', 'U') IS NOT NULL DROP TABLE CustomerMapping;
GO
-- 旧系统客户表(结构不规范)
CREATE TABLE OldCustomers (
CustID VARCHAR(10), -- 混合字母数字
CustName VARCHAR(100),
ContactInfo VARCHAR(200), -- 电话和邮箱混合
RegDate VARCHAR(20), -- 字符串格式日期
Status CHAR(1) -- 'A'=活跃, 'I'=无效
);
GO
-- 新系统客户表(结构规范)
CREATE TABLE NewCustomers (
CustomerID INT PRIMARY KEY IDENTITY(1001,1),
CustomerCode NVARCHAR(20) UNIQUE NOT NULL,
CustomerName NVARCHAR(100) NOT NULL,
Phone NVARCHAR(20),
Email NVARCHAR(100),
RegistrationDate DATE,
CustomerStatus NVARCHAR(20) DEFAULT 'Active',
CreatedDate DATETIME DEFAULT GETDATE()
);
GO
-- 客户映射表(用于关联新旧ID)
CREATE TABLE CustomerMapping (
OldCustID VARCHAR(10) PRIMARY KEY,
NewCustomerID INT REFERENCES NewCustomers(CustomerID),
MigrationDate DATETIME DEFAULT GETDATE()
);
GO
-- ========== 插入旧系统测试数据 ==========
PRINT '🔄 插入旧系统测试数据...';
INSERT INTO OldCustomers VALUES
('C001', '张三公司', '电话:13800138001;邮箱:zhangsan@company.com', '2023-01-15', 'A'),
('C002', '李四商店', '13900139002;zhaosi@store.com', '2023-02-20', 'A'),
('C003', '王五企业', 'email:wangwu@enterprise.com;phone:13700137003', '2022-11-05', 'I'),
('C004', '赵六小店', '13600136004', '2024-03-10', 'A'),
('C005', '钱七工作室', 'qianqi@studio.com', '2023-08-25', 'A');
GO
-- ========== 数据迁移过程 ==========
PRINT '🔄 开始数据迁移...';
BEGIN TRY
BEGIN TRANSACTION;
-- 步骤1:从旧表插入到新表(数据清洗和转换)
INSERT INTO NewCustomers (CustomerCode, CustomerName, Phone, Email, RegistrationDate, CustomerStatus)
SELECT
'NEW_' + CustID AS CustomerCode,
CustName AS CustomerName,
-- 提取电话号码(简单模式,实际需更复杂逻辑)
CASE
WHEN ContactInfo LIKE '%电话:%' THEN
SUBSTRING(ContactInfo, CHARINDEX('电话:', ContactInfo) + 3, 11)
WHEN ContactInfo LIKE '%phone:%' THEN
SUBSTRING(ContactInfo, CHARINDEX('phone:', ContactInfo) + 6, 11)
WHEN ContactInfo LIKE '[0-9][0-9][0-9]%' AND LEN(ContactInfo) >= 11 THEN
LEFT(ContactInfo, 11)
ELSE NULL
END AS Phone,
-- 提取邮箱
CASE
WHEN ContactInfo LIKE '%邮箱:%' THEN
SUBSTRING(ContactInfo, CHARINDEX('邮箱:', ContactInfo) + 3, 100)
WHEN ContactInfo LIKE '%email:%' THEN
SUBSTRING(ContactInfo, CHARINDEX('email:', ContactInfo) + 6, 100)
WHEN ContactInfo LIKE '%@%' THEN ContactInfo
ELSE NULL
END AS Email,
-- 转换日期
CASE
WHEN ISDATE(RegDate) = 1 THEN CAST(RegDate AS DATE)
ELSE GETDATE()
END AS RegistrationDate,
-- 转换状态
CASE Status
WHEN 'A' THEN 'Active'
WHEN 'I' THEN 'Inactive'
ELSE 'Unknown'
END AS CustomerStatus
FROM OldCustomers;
PRINT '✅ 数据插入完成,共迁移 ' + CAST(@@ROWCOUNT AS NVARCHAR(10)) + ' 条记录';
-- 步骤2:创建映射关系
INSERT INTO CustomerMapping (OldCustID, NewCustomerID)
SELECT
oc.CustID,
nc.CustomerID
FROM OldCustomers oc
JOIN NewCustomers nc ON nc.CustomerCode = 'NEW_' + oc.CustID;
PRINT '✅ 映射关系创建完成';
-- 步骤3:更新旧系统数据(标记已迁移)
UPDATE OldCustomers
SET Status = 'M' -- Migrated
WHERE CustID IN (SELECT OldCustID FROM CustomerMapping);
PRINT '✅ 旧系统数据更新完成';
COMMIT TRANSACTION;
PRINT '✅ 数据迁移全部完成!';
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
PRINT '❌ 数据迁移失败:' + ERROR_MESSAGE();
END CATCH
GO
-- ========== 验证迁移结果 ==========
PRINT '📊 迁移结果验证:';
SELECT '旧系统' AS Source, COUNT(*) AS RecordCount FROM OldCustomers
UNION ALL
SELECT '新系统', COUNT(*) FROM NewCustomers
UNION ALL
SELECT '映射表', COUNT(*) FROM CustomerMapping;
SELECT
oc.CustID AS 旧ID,
oc.CustName AS 旧名称,
oc.ContactInfo AS 旧联系方式,
nc.CustomerCode AS 新编码,
nc.CustomerName AS 新名称,
nc.Phone AS 新电话,
nc.Email AS 新邮箱,
nc.CustomerStatus AS 新状态
FROM OldCustomers oc
JOIN CustomerMapping cm ON oc.CustID = cm.OldCustID
JOIN NewCustomers nc ON cm.NewCustomerID = nc.CustomerID
ORDER BY oc.CustID;
GO
-- ========== 清理无效数据 ==========
PRINT '🔄 清理操作:删除旧系统中无效且未迁移的数据';
DELETE FROM OldCustomers
WHERE Status = 'I' AND CustID NOT IN (SELECT OldCustID FROM CustomerMapping);
PRINT '✅ 清理完成,删除 ' + CAST(@@ROWCOUNT AS NVARCHAR(10)) + ' 条记录';
GO
✅ 本章核心更新操作速查表
| 操作类型 | 语法 | 关键注意事项 |
|---|---|---|
| 插入单行 | INSERT INTO table (cols) VALUES (vals) |
指定列名,避免依赖表结构顺序 |
| 插入多行 | INSERT INTO table (cols) VALUES (v1), (v2), ... |
SQL Server 2008+支持多值插入 |
| 插入查询结果 | INSERT INTO table SELECT ... FROM ... |
列数和类型必须匹配 |
| 更新单行 | UPDATE table SET col=val WHERE pk=xxx |
使用主键或唯一条件 |
| 更新多行 | UPDATE table SET col=val WHERE condition |
务必测试WHERE条件 |
| 删除部分 | DELETE FROM table WHERE condition |
先用SELECT验证条件 |
| 删除全部 | DELETE FROM table 或 TRUNCATE TABLE table |
TRUNCATE更快但重置自增ID |
🛡️ 安全最佳实践
-
始终使用事务测试:
sqlBEGIN TRANSACTION; -- 你的UPDATE/DELETE语句 -- SELECT * FROM table WHERE ... 验证结果 -- ROLLBACK; -- 如果不对 -- COMMIT; -- 如果正确 -
备份重要数据:
sqlSELECT * INTO TableName_Backup_YYYYMMDD FROM TableName; -
使用WHERE子句前先验证:
sql-- 先执行SELECT确认要修改的行 SELECT * FROM Students WHERE ClassName = '高三(1)班'; -- 确认无误后再执行UPDATE UPDATE Students SET ... WHERE ClassName = '高三(1)班'; -
生产环境避免直接操作:
- 使用存储过程封装更新逻辑
- 添加权限控制
- 记录操作日志
-
使用TRY...CATCH处理异常:
sqlBEGIN TRY BEGIN TRANSACTION; -- 更新操作 COMMIT TRANSACTION; END TRY BEGIN CATCH ROLLBACK TRANSACTION; -- 记录错误日志 END CATCH
📘 学习建议:
- 在开发环境反复练习,熟悉各种场景
- 理解
DELETE和TRUNCATE的区别和适用场景 - 掌握事务的使用,这是数据安全的保障
- 学会数据备份和恢复策略
- 复杂更新先写SELECT验证,再改写为UPDATE
✅ 本章是数据库操作的核心技能,掌握后可安全高效地管理数据!