SqlCommand.ExecuteNonQuery() 执行 DELETE 语句返回受影响行数,须用参数化查询(如 "DELETE FROM Users WHERE Id = @id"),连接需处于 Open 状态,参数名须与 SQL 占位符严格一致,避免拼接用户输入以防 SQL 注入。

用 SqlCommand.ExecuteNonQuery() 执行 DELETE 语句
DELETE 操作不返回结果集,必须用 ExecuteNonQuery(),而不是 ExecuteReader() 或 ExecuteScalar()。后者会抛出异常或返回无意义值。
常见错误现象:InvalidOperationException: "该命令已有一个打开的 DataReader"(误用 ExecuteReader),或返回值始终为 -1(误用 ExecuteScalar)。
- 确保连接处于
Open状态,否则抛出InvalidOperationException: "Connection must be Open" - SQL 字符串中不要拼接用户输入,必须用参数化查询,例如:
"DELETE FROM Users WHERE Id = @id" -
ExecuteNonQuery()返回受影响的行数(int),可用于判断是否真删到了数据:if (rowsAffected == 0) { /* 未匹配到记录 */ }
带参数的 DELETE 必须用 SqlParameter 而非字符串拼接
直接拼接字符串不仅有 SQL 注入风险,还会因单引号、空值、特殊字符导致语法错误或误删。
正确做法是统一用 SqlParameter 绑定值,尤其注意 null 值处理:
- 数据库字段允许 NULL?传
DBNull.Value,不是null或空字符串 - 参数名必须和 SQL 中的占位符完全一致(包括 @ 符号),大小写敏感(取决于数据库,SQL Server 不敏感,但养成一致习惯)
- 避免重复添加同名参数,否则抛出
ArgumentException: "An SqlParameter with ParameterName '@id' is already contained..."
示例:
cmd.CommandText = "DELETE FROM Orders WHERE Status = @status AND CreatedAt cmd.Parameters.Add(new SqlParameter("@status", "Pending")); <br>cmd.Parameters.Add(new SqlParameter("@before", DateTime.UtcNow.AddDays(-7)));
批量删除慎用 IN 子句 + 参数列表
SQL Server 对参数个数有限制(通常 2100 个),直接为每个 ID 写一个 @id1, @id2… 容易超限;而用字符串拼接 IN (1,2,3) 又绕过参数化,失去安全性。
可行方案:
- 分批执行:每批 ≤ 1000 个 ID,循环调用
ExecuteNonQuery() - 用临时表 +
JOIN删除(适合上万条):先用SqlBulkCopy把 ID 列批量写入临时表,再DELETE FROM T WHERE id IN (SELECT id FROM #temp) - 使用表值参数(TVP):需提前在数据库创建对应
TYPE,C# 用DataTable构造并作为SqlParameter传入(类型设为SqlDbType.Structured)
事务中执行 DELETE 要显式控制提交与回滚
单独一条 DELETE 默认自动提交,但涉及多表关联删除、前置校验或后续插入时,必须包裹在事务里,否则部分失败会导致数据不一致。
容易被忽略的点:
-
SqlTransaction对象必须传给所有参与的SqlCommand(通过cmd.Transaction = tran) - 未捕获异常时忘记
tran.Rollback(),连接关闭后事务自动回滚——但日志难追溯,应主动处理 - 长时间运行的 DELETE(如百万级)可能触发锁升级或阻塞,建议加
WITH (ROWLOCK)提示(SQL Server),并避免在高峰时段执行
事务内 DELETE 后若需读取刚删的数据做审计,不能依赖 SELECT,因为已不可见;应提前用 OUTPUT DELETED.* 捕获被删行(SQL Server 支持)。











