SQL Server数据更新 #
一、UPDATE语句概述 #
1.1 基本语法 #
sql
UPDATE table_name
SET column1 = value1, column2 = value2, ...
[WHERE condition];
1.2 示例表结构 #
sql
-- 创建示例表
CREATE TABLE users (
id INT PRIMARY KEY,
name NVARCHAR(50),
email VARCHAR(100),
age INT,
status BIT DEFAULT 1,
salary DECIMAL(10,2),
updated_at DATETIME2
);
INSERT INTO users VALUES
(1, 'John', 'john@example.com', 25, 1, 5000.00, NULL),
(2, 'Jane', 'jane@example.com', 30, 1, 6000.00, NULL),
(3, 'Bob', 'bob@example.com', 28, 0, 4500.00, NULL),
(4, 'Alice', 'alice@example.com', 22, 1, 4000.00, NULL),
(5, 'Charlie', 'charlie@example.com', 35, 0, 7000.00, NULL);
CREATE TABLE departments (
id INT PRIMARY KEY,
name NVARCHAR(50),
budget DECIMAL(12,2)
);
INSERT INTO departments VALUES
(1, 'IT', 100000.00),
(2, 'HR', 50000.00);
二、基本更新 #
2.1 更新单列 #
sql
-- 更新单列
UPDATE users SET age = 26 WHERE id = 1;
-- 更新多行
UPDATE users SET status = 1 WHERE status = 0;
-- 更新所有行(危险操作!)
UPDATE users SET updated_at = GETDATE();
2.2 更新多列 #
sql
-- 更新多列
UPDATE users
SET
age = 26,
email = 'john_new@example.com',
updated_at = GETDATE()
WHERE id = 1;
-- 使用表达式更新
UPDATE users
SET
salary = salary * 1.1, -- 加薪10%
updated_at = GETDATE()
WHERE status = 1;
2.3 使用DEFAULT #
sql
-- 重置为默认值
UPDATE users SET status = DEFAULT WHERE id = 1;
-- 重置为NULL
UPDATE users SET updated_at = NULL WHERE id = 1;
三、条件更新 #
3.1 WHERE条件 #
sql
-- 等于条件
UPDATE users SET status = 0 WHERE id = 1;
-- 范围条件
UPDATE users SET salary = salary * 1.05
WHERE age BETWEEN 25 AND 30;
-- IN条件
UPDATE users SET status = 1
WHERE id IN (1, 2, 3);
-- LIKE条件
UPDATE users SET status = 0
WHERE email LIKE '%@test.com';
-- NULL条件
UPDATE users SET updated_at = GETDATE()
WHERE updated_at IS NULL;
3.2 复杂条件 #
sql
-- 多条件组合
UPDATE users
SET salary = salary * 1.1
WHERE status = 1
AND age > 25
AND salary < 5000;
-- 子查询条件
UPDATE users
SET status = 0
WHERE id IN (
SELECT user_id FROM orders
WHERE status = 'cancelled'
);
-- EXISTS条件
UPDATE users u
SET status = 0
WHERE EXISTS (
SELECT 1 FROM orders o
WHERE o.user_id = u.id
AND o.amount > 10000
);
四、TOP更新 #
4.1 TOP N更新 #
sql
-- 更新前N行
UPDATE TOP (10) users
SET status = 1
WHERE status = 0;
-- 更新前10%行
UPDATE TOP (10) PERCENT users
SET salary = salary * 1.05;
4.2 使用ORDER BY更新 #
sql
-- 更新工资最低的5人
WITH cte AS (
SELECT TOP 5 id
FROM users
ORDER BY salary ASC
)
UPDATE users
SET salary = salary * 1.1
WHERE id IN (SELECT id FROM cte);
-- 或者使用CTE直接更新
WITH cte AS (
SELECT TOP 5 *
FROM users
ORDER BY salary ASC
)
UPDATE cte
SET salary = salary * 1.1;
五、多表更新 #
5.1 UPDATE FROM JOIN #
sql
-- 使用FROM子句更新
UPDATE u
SET u.salary = u.salary * 1.1
FROM users u
INNER JOIN departments d ON u.department_id = d.id
WHERE d.name = 'IT';
-- 更新多表关联数据
UPDATE o
SET o.status = 'completed'
FROM orders o
INNER JOIN users u ON o.user_id = u.id
WHERE u.status = 1;
5.2 使用子查询更新 #
sql
-- 使用子查询值更新
UPDATE users
SET salary = (
SELECT AVG(salary) FROM users
)
WHERE id = 1;
-- 使用相关子查询
UPDATE users u
SET salary = (
SELECT AVG(salary)
FROM users
WHERE department_id = u.department_id
);
5.3 使用CTE更新 #
sql
-- 使用CTE更新
WITH UserStats AS (
SELECT
u.id,
COUNT(o.id) AS order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id
)
UPDATE u
SET u.order_count = us.order_count
FROM users u
INNER JOIN UserStats us ON u.id = us.id;
六、OUTPUT子句 #
6.1 返回更新数据 #
sql
-- 返回更新前后数据
UPDATE users
SET salary = salary * 1.1
OUTPUT
inserted.id,
deleted.salary AS old_salary,
inserted.salary AS new_salary
WHERE id = 1;
-- 结果
-- id | old_salary | new_salary
-- 1 | 5000.00 | 5500.00
6.2 输出到表 #
sql
-- 创建审计表
CREATE TABLE salary_audit (
user_id INT,
old_salary DECIMAL(10,2),
new_salary DECIMAL(10,2),
changed_at DATETIME DEFAULT GETDATE()
);
-- 更新并记录
UPDATE users
SET salary = salary * 1.1
OUTPUT
inserted.id,
deleted.salary,
inserted.salary
INTO salary_audit (user_id, old_salary, new_salary)
WHERE status = 1;
七、CASE表达式更新 #
7.1 条件更新 #
sql
-- 使用CASE条件更新
UPDATE users
SET status = CASE
WHEN age < 25 THEN 0
WHEN age >= 25 AND age < 35 THEN 1
ELSE 2
END;
-- 根据不同条件更新不同值
UPDATE products
SET price = CASE
WHEN category = 'Electronics' THEN price * 0.9
WHEN category = 'Clothing' THEN price * 0.8
ELSE price * 0.95
END;
7.2 多列条件更新 #
sql
-- 同时更新多列
UPDATE users
SET
status = CASE WHEN salary > 6000 THEN 1 ELSE 0 END,
salary = CASE WHEN salary < 5000 THEN salary * 1.2 ELSE salary END
WHERE id IN (1, 2, 3);
八、变量更新 #
8.1 使用变量 #
sql
-- 使用变量更新
DECLARE @newStatus BIT = 1;
DECLARE @raiseRate DECIMAL(3,2) = 0.1;
UPDATE users
SET
status = @newStatus,
salary = salary * (1 + @raiseRate)
WHERE id = 1;
8.2 更新并获取值 #
sql
-- 更新并获取值到变量
DECLARE @oldSalary DECIMAL(10,2);
DECLARE @newSalary DECIMAL(10,2);
UPDATE users
SET
salary = salary * 1.1,
@oldSalary = deleted.salary,
@newSalary = inserted.salary
OUTPUT deleted.salary, inserted.salary
WHERE id = 1;
PRINT '旧工资: ' + CAST(@oldSalary AS VARCHAR);
PRINT '新工资: ' + CAST(@newSalary AS VARCHAR);
九、事务中的更新 #
9.1 使用事务 #
sql
-- 使用事务保证原子性
BEGIN TRANSACTION;
BEGIN TRY
UPDATE users SET salary = salary * 1.1 WHERE id = 1;
UPDATE users SET salary = salary * 0.9 WHERE id = 2;
COMMIT TRANSACTION;
PRINT '更新成功';
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
PRINT '更新失败: ' + ERROR_MESSAGE();
END CATCH
9.2 乐观并发控制 #
sql
-- 使用版本号实现乐观锁
DECLARE @version INT;
SELECT @version = version FROM users WHERE id = 1;
UPDATE users
SET
salary = salary * 1.1,
version = version + 1
WHERE id = 1 AND version = @version;
IF @@ROWCOUNT = 0
PRINT '更新失败,数据已被修改';
ELSE
PRINT '更新成功';
十、批量更新 #
10.1 使用临时表 #
sql
-- 创建临时表存储更新数据
CREATE TABLE #updates (
user_id INT,
new_salary DECIMAL(10,2)
);
INSERT INTO #updates VALUES
(1, 5500.00),
(2, 6500.00),
(3, 4800.00);
-- 批量更新
UPDATE u
SET u.salary = up.new_salary
FROM users u
INNER JOIN #updates up ON u.id = up.user_id;
DROP TABLE #updates;
10.2 使用MERGE #
sql
-- 使用MERGE进行更新
MERGE INTO users AS target
USING (
SELECT id, salary FROM users_staging
) AS source
ON target.id = source.id
WHEN MATCHED THEN
UPDATE SET
target.salary = source.salary,
target.updated_at = GETDATE();
十一、更新优化 #
11.1 索引利用 #
sql
-- 确保WHERE条件使用索引
-- 创建索引
CREATE INDEX ix_users_status ON users(status);
-- 使用索引列更新
UPDATE users
SET salary = salary * 1.1
WHERE status = 1; -- 使用索引
11.2 分批更新 #
sql
-- 大表分批更新
DECLARE @batchSize INT = 1000;
DECLARE @rowsAffected INT = 1;
WHILE @rowsAffected > 0
BEGIN
UPDATE TOP (@batchSize) users
SET salary = salary * 1.1
WHERE status = 1;
SET @rowsAffected = @@ROWCOUNT;
-- 延迟减少锁争用
WAITFOR DELAY '00:00:00.1';
END
11.3 禁用触发器 #
sql
-- 大批量更新时禁用触发器
DISABLE TRIGGER ALL ON users;
-- 执行更新
UPDATE users SET salary = salary * 1.1;
-- 启用触发器
ENABLE TRIGGER ALL ON users;
十二、安全更新 #
12.1 防止全表更新 #
sql
-- 检查WHERE条件
-- 安全做法:先查询再更新
BEGIN TRANSACTION;
-- 先查看影响行数
SELECT COUNT(*) FROM users WHERE status = 1;
-- 确认后再更新
UPDATE users SET salary = salary * 1.1 WHERE status = 1;
-- 检查结果
SELECT * FROM users WHERE status = 1;
-- 确认无误后提交
COMMIT TRANSACTION;
12.2 使用安全设置 #
sql
-- 设置防止全表更新错误
-- SSMS: Tools → Options → Query Execution → SQL Server → ANSI
-- 勾选 "SET ANSI_NULL_DFLT_ON"
十三、更新验证 #
13.1 检查更新结果 #
sql
-- 使用@@ROWCOUNT
UPDATE users SET salary = salary * 1.1 WHERE status = 1;
IF @@ROWCOUNT = 0
PRINT '没有数据被更新';
ELSE
PRINT '更新了 ' + CAST(@@ROWCOUNT AS VARCHAR) + ' 行';
13.2 验证更新值 #
sql
-- 更新并验证
DECLARE @expectedSalary DECIMAL(10,2) = 5500.00;
UPDATE users SET salary = @expectedSalary WHERE id = 1;
IF EXISTS (
SELECT 1 FROM users
WHERE id = 1 AND salary = @expectedSalary
)
PRINT '更新验证成功';
ELSE
PRINT '更新验证失败';
十四、常见问题 #
14.1 更新失败原因 #
sql
-- 1. 违反约束
UPDATE users SET age = -1 WHERE id = 1; -- CHECK约束
-- 2. 外键约束
UPDATE orders SET user_id = 999 WHERE id = 1; -- 外键不存在
-- 3. 唯一约束
UPDATE users SET email = 'john@example.com' WHERE id = 2; -- 邮箱重复
-- 4. 锁超时
-- 其他事务持有锁
14.2 处理更新冲突 #
sql
-- 使用WITH (NOLOCK)读取(仅查询)
SELECT * FROM users WITH (NOLOCK);
-- 使用UPDLOCK锁定要更新的行
BEGIN TRANSACTION;
SELECT * FROM users WITH (UPDLOCK) WHERE id = 1;
UPDATE users SET salary = salary * 1.1 WHERE id = 1;
COMMIT TRANSACTION;
十五、总结 #
更新数据要点:
| 操作 | 说明 |
|---|---|
| 基本更新 | UPDATE SET WHERE |
| 多表更新 | UPDATE FROM JOIN |
| 条件更新 | CASE表达式 |
| 返回结果 | OUTPUT子句 |
| 批量更新 | 分批处理 |
| 安全更新 | 事务保护 |
最佳实践:
- 始终使用WHERE条件
- 先查询确认再更新
- 使用事务保证原子性
- 大批量更新分批处理
- 使用OUTPUT记录变更
下一步,让我们学习数据删除!
最后更新:2026-03-27