你问得很好,因为你被告知的不是真的。多个 DML 语句,无论是在单个存储过程中还是分散在多个 DML 语句中,只有在通过显式 BEGIN TRAN 和 COMMIT / ROLLBACK 显式分组时才会作为一个组相互影响。你应该把逻辑放在最有意义的地方(意思是最容易理解、可读、可维护的地方)。
此外,如果一个 proc 无论如何都要调用多个 proc,那么将它们放在一个 proc 和多个 proc 中在概念上的真正区别是什么?当一个 proc 调用多个 proc 且每个 proc 都有 DML 语句时,这个人认为将多个 DML 语句放入单个 proc 时,存储过程的任何理论属性是否也会使事情变得更糟?最后,您仍然有一个入口点负责多个 DML 调用。
例如:
INSERT ....
DELETE ....
是两个单独的事务,因为每个查询本身都是一个事务(除非在显式事务中分组)。那么:
BEGIN TRAN
INSERT ....
DELETE ....
COMMIT TRAN
是单笔交易。在一个事务中拥有多个 DML 语句可能由于并发(即阻塞/死锁)而存在性能问题,但如果操作要求两个 DML 语句在概念上都是一个原子工作单元,那么它们需要在显式事务中组合在一起。
现在,鉴于上面的第一个示例有两个 DML 语句并且没有显式事务,假设它们在一个存储过程中,这与以下没有什么不同:
CREATE PROC MasterProc
AS
EXEC SubProc1;
EXEC SubProc2;
GO
CREATE PROC SubProc1
AS
INSERT ...
GO
CREATE PROC SubProc2
AS
DELETE ...
GO
并且,给定第二个示例,其中两个 DML 语句都分组在显式事务中,假设它们在存储过程中,这与(使用上述相同的两个 SubProc* 过程)没有什么不同:
CREATE PROC MasterProc
AS
BEGIN TRAN;
EXEC SubProc1;
EXEC SubProc2;
COMMIT TRAN;
GO
编辑:
一些额外的信息是,显然单个存储过程中的多个 DML 语句对性能更差的原因是(据说)SQL Server 无法获得组合的“正确”(即好的?)执行计划,但是如果 DML 语句是分开的,那么 SQL Server 可以为每个单独的存储过程生成“适当的”(即好的?)执行计划。
嗯,执行计划实际上是在查询级别。当然,在 SQL Server 2000 之前,只能重新编译整个过程,但从 SQL Server 2005 开始,可以进行语句级重新编译。但无论哪种方式,每个查询都是自己优化的,因此将它们分开似乎并没有带来任何好处。事实上,鉴于局部变量(包括表变量)不能在存储过程之间共享,如果您必须将每个变量作为输入参数传递并且将其中的一个或多个传回。该代码将难以维护和调试。
编辑 2:
以下是一些有关执行计划缓存和重新编译信息的资源:
还请注意,这些文档确实提到了在对象(即存储过程等)级别缓存的执行计划。但是,这并不意味着 DML 语句作为一个整体进行了优化,并且如果分离出来会有所不同。将 DML 语句分离为单独的存储过程的唯一真正好处是,您可以(从 SQL Server 2008 开始)通过DBCC FREEPROCCACHE ({ plan_handle | sql_handle }) 手动对它们单独发出重新编译请求。但是,如果这只是偶尔需要的东西,那么重新编译整个存储过程并没有什么坏处,与笨重的代码分离相比,这可能成本更低。而且,如果它是经常需要的东西,那么 DML 语句本身可以添加一个OPTION (RECOMPILE),这不会影响该存储过程中的其他查询。
编辑 3:(希望是最终版 :)
我们不要忘记,这仍然是我们正在做的计算机科学,几乎所有东西都是可测试的。用一个表和几个 proc 设置一个场景(如上面显示的示例)并运行调用子存储过程的“主”proc 以及多合一 proc 应该是相当容易的。您可以在运行时查看 SSMS 中的“实际执行计划”,也可以从计划缓存中提取计划(如上面的链接资源所示)。所以从技术上讲,没有人需要依赖这个建议使用单独存储过程的神秘人,甚至没有人相信这个想法充其量是过时的,最坏的情况是纯粹的神话; SQL Server 会准确地告诉你它在任何给定情况下会做什么:)。