SQL Server数据删除 #

一、DELETE语句概述 #

1.1 基本语法 #

sql
DELETE [FROM] table_name
[WHERE condition];

-- 或使用FROM子句
DELETE table_name
FROM table_name alias
[JOIN other_table ON condition]
[WHERE condition];

1.2 示例表结构 #

sql
-- 创建示例表
CREATE TABLE users (
    id INT PRIMARY KEY,
    name NVARCHAR(50),
    email VARCHAR(100),
    status BIT DEFAULT 1
);

CREATE TABLE orders (
    id INT PRIMARY KEY,
    user_id INT,
    order_date DATE,
    amount DECIMAL(10,2),
    FOREIGN KEY (user_id) REFERENCES users(id)
);

INSERT INTO users VALUES
    (1, 'John', 'john@example.com', 1),
    (2, 'Jane', 'jane@example.com', 1),
    (3, 'Bob', 'bob@example.com', 0),
    (4, 'Alice', 'alice@example.com', 1),
    (5, 'Charlie', 'charlie@example.com', 0);

INSERT INTO orders VALUES
    (1, 1, '2024-01-01', 100.00),
    (2, 1, '2024-01-02', 200.00),
    (3, 2, '2024-01-03', 150.00);

二、基本删除 #

2.1 删除指定行 #

sql
-- 删除单行
DELETE FROM users WHERE id = 5;

-- 删除多行
DELETE FROM users WHERE status = 0;

-- 删除所有行(危险操作!)
DELETE FROM users;

2.2 条件删除 #

sql
-- 等于条件
DELETE FROM users WHERE id = 1;

-- 范围条件
DELETE FROM orders WHERE order_date < '2024-01-01';

-- IN条件
DELETE FROM users WHERE id IN (1, 2, 3);

-- LIKE条件
DELETE FROM users WHERE email LIKE '%@test.com';

-- NULL条件
DELETE FROM users WHERE email IS NULL;

2.3 复杂条件删除 #

sql
-- 多条件组合
DELETE FROM users 
WHERE status = 0 
  AND created_at < DATEADD(YEAR, -1, GETDATE());

-- 子查询条件
DELETE FROM users 
WHERE id IN (
    SELECT user_id FROM orders 
    WHERE status = 'cancelled'
);

-- EXISTS条件
DELETE FROM users u
WHERE NOT EXISTS (
    SELECT 1 FROM orders o WHERE o.user_id = u.id
);

三、TOP删除 #

3.1 TOP N删除 #

sql
-- 删除前N行
DELETE TOP (10) FROM users WHERE status = 0;

-- 删除前10%行
DELETE TOP (10) PERCENT FROM users;

3.2 使用ORDER BY删除 #

sql
-- 删除最早的10条记录
WITH cte AS (
    SELECT TOP 10 id
    FROM orders
    ORDER BY order_date ASC
)
DELETE FROM orders
WHERE id IN (SELECT id FROM cte);

-- 或直接使用CTE删除
WITH cte AS (
    SELECT TOP 10 *
    FROM orders
    ORDER BY order_date ASC
)
DELETE FROM cte;

四、多表删除 #

4.1 使用FROM子句 #

sql
-- 基于关联表删除
DELETE o
FROM orders o
INNER JOIN users u ON o.user_id = u.id
WHERE u.status = 0;

-- 删除没有订单的用户
DELETE u
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE o.id IS NULL;

4.2 使用子查询 #

sql
-- 使用子查询删除
DELETE FROM users
WHERE id IN (
    SELECT user_id FROM orders WHERE amount > 10000
);

-- 使用相关子查询
DELETE FROM users u
WHERE (
    SELECT COUNT(*) FROM orders o 
    WHERE o.user_id = u.id
) = 0;

4.3 使用CTE删除 #

sql
-- 使用CTE删除
WITH InactiveUsers AS (
    SELECT u.id
    FROM users u
    LEFT JOIN orders o ON u.id = o.user_id
    WHERE u.status = 0
    GROUP BY u.id
    HAVING COUNT(o.id) = 0
)
DELETE FROM users
WHERE id IN (SELECT id FROM InactiveUsers);

五、OUTPUT子句 #

5.1 返回删除数据 #

sql
-- 返回删除的数据
DELETE FROM users
OUTPUT deleted.id, deleted.name, deleted.email
WHERE id = 1;

-- 结果
-- id | name | email
-- 1  | John | john@example.com

5.2 输出到表 #

sql
-- 创建归档表
CREATE TABLE users_archive (
    id INT,
    name NVARCHAR(50),
    email VARCHAR(100),
    deleted_at DATETIME DEFAULT GETDATE()
);

-- 删除并归档
DELETE FROM users
OUTPUT deleted.id, deleted.name, deleted.email INTO users_archive
WHERE status = 0;

-- 查看归档数据
SELECT * FROM users_archive;

5.3 完整删除记录 #

sql
-- 创建审计表
CREATE TABLE delete_audit (
    table_name NVARCHAR(50),
    record_id INT,
    old_data NVARCHAR(MAX),
    deleted_by NVARCHAR(100) DEFAULT SUSER_NAME(),
    deleted_at DATETIME DEFAULT GETDATE()
);

-- 删除并记录完整数据
DELETE FROM users
OUTPUT 
    'users',
    deleted.id,
    (SELECT * FROM deleted FOR JSON PATH),
    DEFAULT,
    DEFAULT
INTO delete_audit
WHERE id = 1;

六、TRUNCATE TABLE #

6.1 基本用法 #

sql
-- 清空表
TRUNCATE TABLE users;

-- 安全写法
IF OBJECT_ID('temp_users', 'U') IS NOT NULL
    TRUNCATE TABLE temp_users;

6.2 TRUNCATE vs DELETE #

特性 TRUNCATE DELETE
速度 快(不记录日志) 慢(记录日志)
WHERE 不支持 支持
触发器 不触发 触发
IDENTITY 重置 不重置
回滚 可以(在事务中) 可以
外键引用 不允许 允许
权限 ALTER权限 DELETE权限
sql
-- TRUNCATE重置IDENTITY
CREATE TABLE test (
    id INT IDENTITY(1,1),
    name NVARCHAR(50)
);

INSERT INTO test VALUES ('A'), ('B');
SELECT * FROM test;  -- id: 1, 2

TRUNCATE TABLE test;
INSERT INTO test VALUES ('C');
SELECT * FROM test;  -- id: 1(重置了)

-- DELETE不重置IDENTITY
DELETE FROM test;
INSERT INTO test VALUES ('D');
SELECT * FROM test;  -- id: 2(未重置)

6.3 TRUNCATE限制 #

sql
-- 有外键引用时不能TRUNCATE
TRUNCATE TABLE users;  -- 错误:被orders表引用

-- 解决方案1:先删除外键约束
ALTER TABLE orders DROP CONSTRAINT fk_orders_users;
TRUNCATE TABLE users;
ALTER TABLE orders ADD CONSTRAINT fk_orders_users 
    FOREIGN KEY (user_id) REFERENCES users(id);

-- 解决方案2:使用DELETE
DELETE FROM users;

七、级联删除 #

7.1 外键级联删除 #

sql
-- 创建带级联删除的表
CREATE TABLE customers (
    id INT PRIMARY KEY,
    name NVARCHAR(50)
);

CREATE TABLE customer_orders (
    id INT PRIMARY KEY,
    customer_id INT,
    order_date DATE,
    FOREIGN KEY (customer_id) 
        REFERENCES customers(id)
        ON DELETE CASCADE  -- 级联删除
);

-- 删除客户时自动删除订单
DELETE FROM customers WHERE id = 1;
-- 同时删除customer_orders中相关记录

7.2 手动级联删除 #

sql
-- 手动删除关联数据
BEGIN TRANSACTION;

-- 先删除子表数据
DELETE FROM orders WHERE user_id = 1;

-- 再删除主表数据
DELETE FROM users WHERE id = 1;

COMMIT TRANSACTION;

7.3 使用触发器级联删除 #

sql
-- 创建删除触发器
CREATE TRIGGER tr_users_delete
ON users
INSTEAD OF DELETE
AS
BEGIN
    -- 先删除关联订单
    DELETE FROM orders 
    WHERE user_id IN (SELECT id FROM deleted);
    
    -- 再删除用户
    DELETE FROM users 
    WHERE id IN (SELECT id FROM deleted);
END

八、软删除 #

8.1 实现软删除 #

sql
-- 添加删除标记列
ALTER TABLE users ADD is_deleted BIT DEFAULT 0;
ALTER TABLE users ADD deleted_at DATETIME;

-- 软删除(更新标记)
UPDATE users 
SET 
    is_deleted = 1,
    deleted_at = GETDATE()
WHERE id = 1;

-- 查询时过滤已删除数据
SELECT * FROM users WHERE is_deleted = 0;

-- 恢复数据
UPDATE users 
SET is_deleted = 0, deleted_at = NULL 
WHERE id = 1;

8.2 使用视图简化 #

sql
-- 创建活动用户视图
CREATE VIEW vw_active_users AS
SELECT id, name, email, status
FROM users
WHERE is_deleted = 0;

-- 使用视图操作
SELECT * FROM vw_active_users;  -- 自动过滤已删除

-- 软删除
UPDATE users SET is_deleted = 1 WHERE id = 1;

8.3 使用触发器自动过滤 #

sql
-- 创建INSTEAD OF触发器
CREATE TRIGGER tr_users_soft_delete
ON users
INSTEAD OF DELETE
AS
BEGIN
    UPDATE users
    SET 
        is_deleted = 1,
        deleted_at = GETDATE()
    WHERE id IN (SELECT id FROM deleted);
END

-- 删除操作变成软删除
DELETE FROM users WHERE id = 1;

九、批量删除 #

9.1 分批删除 #

sql
-- 大表分批删除
DECLARE @batchSize INT = 1000;
DECLARE @rowsAffected INT = 1;

WHILE @rowsAffected > 0
BEGIN
    DELETE TOP (@batchSize) FROM orders
    WHERE order_date < '2023-01-01';
    
    SET @rowsAffected = @@ROWCOUNT;
    
    -- 延迟减少锁争用
    WAITFOR DELAY '00:00:00.1';
END

9.2 使用临时表 #

sql
-- 先选出要删除的ID
SELECT id INTO #to_delete
FROM orders
WHERE order_date < '2023-01-01';

-- 分批删除
DECLARE @batchSize INT = 1000;

WHILE EXISTS (SELECT 1 FROM #to_delete)
BEGIN
    DELETE TOP (@batchSize) FROM orders
    WHERE id IN (SELECT id FROM #to_delete);
    
    DELETE TOP (@batchSize) FROM #to_delete;
END

DROP TABLE #to_delete;

十、事务中的删除 #

10.1 使用事务 #

sql
-- 使用事务保证原子性
BEGIN TRANSACTION;

BEGIN TRY
    DELETE FROM orders WHERE user_id = 1;
    DELETE FROM users WHERE id = 1;
    
    COMMIT TRANSACTION;
    PRINT '删除成功';
END TRY
BEGIN CATCH
    ROLLBACK TRANSACTION;
    PRINT '删除失败: ' + ERROR_MESSAGE();
END CATCH

10.2 保存点 #

sql
-- 使用保存点部分回滚
BEGIN TRANSACTION;

DELETE FROM users WHERE id = 1;

SAVE TRANSACTION after_user_delete;

DELETE FROM orders WHERE user_id = 1;

-- 如果需要回滚到保存点
ROLLBACK TRANSACTION after_user_delete;

COMMIT TRANSACTION;

十一、删除验证 #

11.1 检查删除结果 #

sql
-- 使用@@ROWCOUNT
DELETE FROM users WHERE status = 0;

IF @@ROWCOUNT = 0
    PRINT '没有数据被删除';
ELSE
    PRINT '删除了 ' + CAST(@@ROWCOUNT AS VARCHAR) + ' 行';

11.2 验证数据完整性 #

sql
-- 删除后验证
DECLARE @userId INT = 1;

BEGIN TRANSACTION;

DELETE FROM orders WHERE user_id = @userId;
DELETE FROM users WHERE id = @userId;

-- 验证删除成功
IF NOT EXISTS (SELECT 1 FROM users WHERE id = @userId)
   AND NOT EXISTS (SELECT 1 FROM orders WHERE user_id = @userId)
BEGIN
    COMMIT TRANSACTION;
    PRINT '删除成功';
END
ELSE
BEGIN
    ROLLBACK TRANSACTION;
    PRINT '删除失败';
END

十二、删除优化 #

12.1 索引利用 #

sql
-- 确保WHERE条件使用索引
CREATE INDEX ix_users_status ON users(status);

-- 使用索引列删除
DELETE FROM users WHERE status = 0;

12.2 禁用触发器和约束 #

sql
-- 大批量删除时禁用触发器
DISABLE TRIGGER ALL ON users;

DELETE FROM users WHERE status = 0;

ENABLE TRIGGER ALL ON users;

-- 禁用约束检查
ALTER TABLE orders NOCHECK CONSTRAINT ALL;
DELETE FROM users WHERE status = 0;
ALTER TABLE orders CHECK CONSTRAINT ALL;

12.3 使用最小日志 #

sql
-- 在简单恢复模式下,TRUNCATE使用最小日志
ALTER DATABASE mydb SET RECOVERY SIMPLE;

TRUNCATE TABLE large_table;

ALTER DATABASE mydb SET RECOVERY FULL;

十三、安全删除 #

13.1 防止误删除 #

sql
-- 先查询再删除
BEGIN TRANSACTION;

-- 查看要删除的数据
SELECT * FROM users WHERE status = 0;

-- 确认数量
SELECT COUNT(*) AS '将要删除的行数' 
FROM users WHERE status = 0;

-- 确认后执行删除
DELETE FROM users WHERE status = 0;

-- 检查结果
SELECT * FROM users;

-- 确认无误后提交
COMMIT TRANSACTION;
-- 或回滚
-- ROLLBACK TRANSACTION;

13.2 权限控制 #

sql
-- 授予DELETE权限
GRANT DELETE ON users TO user1;

-- 拒绝DELETE权限
DENY DELETE ON users TO user1;

-- 撤销权限
REVOKE DELETE ON users FROM user1;

十四、常见问题 #

14.1 删除失败原因 #

sql
-- 1. 外键约束
DELETE FROM users WHERE id = 1;  -- 错误:被orders引用

-- 2. 触发器阻止
-- 触发器中ROLLBACK会阻止删除

-- 3. 锁超时
-- 其他事务持有锁

-- 4. 权限不足
-- 需要DELETE权限

14.2 处理删除冲突 #

sql
-- 使用WITH (ROWLOCK)提示
DELETE FROM users WITH (ROWLOCK) WHERE id = 1;

-- 使用事务隔离级别
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN TRANSACTION;
DELETE FROM users WHERE id = 1;
COMMIT TRANSACTION;

十五、总结 #

删除数据要点:

操作 说明
DELETE 条件删除,记录日志
TRUNCATE 清空表,快速
级联删除 外键CASCADE
软删除 标记删除
OUTPUT 返回删除数据

最佳实践:

  1. 删除前先查询确认
  2. 使用事务保护
  3. 大批量删除分批处理
  4. 考虑使用软删除
  5. 使用OUTPUT记录删除数据

下一步,让我们学习基础查询!

最后更新:2026-03-27