我有一个存储过程,我在其中发送用户定义的类型(即表)。我已简化以使其易于阅读。
CREATE TYPE [dbo].[ProjectTableType] AS TABLE(
[DbId] [uniqueidentifier] NOT NULL,
[DbParentId] [uniqueidentifier] NULL,
[Description] [text] NULL
)
CREATE PROCEDURE [dbo].[udsp_ProjectDUI] (@cmd varchar(10),
@tblProjects ProjectTableType READONLY) AS BEGIN
DECLARE @myNewPKTable TABLE (myNewPK uniqueidentifier)
IF(LOWER(@cmd) = 'insert')
BEGIN
INSERT INTO
dbo.Project
(
DbId,
DbParentId,
Description)
OUTPUT INSERTED.DbId INTO @myNewPKTable
SELECT NEWID(),
DbParentId,
Description
FROM @tblProjects;
SELECT * FROM dbo.Project WHERE dbid IN (SELECT myNewPK FROM @myNewPKTable);
END
这是供其他应用程序使用的DLL使用的,因此我们不必负责验证。我想模仿大容量插入,如果其中一行不能插入而其他行很好,那么正确的行仍会插入。有没有办法做到这一点?我也想对UPDATE执行此操作,如果其中一个失败,则存储过程将继续尝试更新其他过程。
我能想到的唯一选择是一次只执行一次(或者多次调用存储过程的代码中的循环,或者一次存储过程中的循环),但是我想知道性能会受到什么影响是为了解决这个问题,还是有更好的解决方案。
答案 0 :(得分:2)
不确定要继续发生哪些错误,但是除非遇到各种意外错误,否则我将尽力避免演变为RBAR。
检查明确的违规行为
我认为主要是PK变更,您可以通过在插入(和更新)之前检查是否存在来避免。如果还有其他业务逻辑故障情况,也可以在此处检查。
insert into dbo.Project
(
DbId,
DbParentId,
Description
)
output insert.DbId
into @myNewPKTable (DbId)
select
DbId = newid(),
DbParentId = s.DbParentId,
Description = s.Description
from @tblProjects s -- source
-- LOJ null check makes sure we don't violate PK
-- NOTE: I'm pretending this is an alternate key of the table.
left outer join dbo.Project t -- target
on s.dbParentId = t.dbParentId
where t.dbParentId is null
如果可能的话,我会尽量坚持进行批处理更新,并使用连接谓词消除您期望看到的大多数错误的可能性。由于担心“可能”导致系统关闭失败而更改为RBAR处理可能会浪费时间。然后,如果您遇到了一个非常讨厌的错误,您将无法恢复,并合法地使该批次失败。
RBAR
或者,如果您绝对需要成功或失败的逐行粒度,则可以尝试/捕获每个语句并使catch块不执行任何操作(或记录某些内容)。
declare
@DBParentId int,
@Description nvarchar(1000),
@Ident int
declare c cursor local fast_forward for
select
DbParentId = s.DbParentId,
Description = s.Description
from @tblProjects
open c
fetch next from c into @DBParentId, @Description
while @@fetch_status = 0
begin
begin try
insert into dbo.Project
(
DbId,
DbParentId,
Description
)
output insert.DbId
into @myNewPKTable (DbId)
select
newid(),
@DBParentId,
@Description
end try
begin catch
-- log something if you want
print error_message()
end catch
fetch next from c into @DBParentId, @Description
end
混合 您可能能够变得聪明并能使事情变得混杂。一种选择可能是使面向Web的插入过程实际上插入轻量级,最小化键控/约束表(如队列)中。然后,每隔大约一分钟,使一次代理作业通过记录的呼叫运行,并批量对其进行操作。根本上不会改变这里的任何模式,但是它使处理异步,因此调用者不必等待,并且通过将请求分批处理,可以节省搭载SQL最佳性能的处理能力。设置为基础的操作。
另一种选择可能是执行最多的基于集合的处理(使用检查来防止违反业务规则或约束)。如果有任何失败,则可以对剩余的行进行RBAR处理。如果一切成功,那么RBAR流程将永远不会实现。
结论 有几种解决方法。除非您有充分的理由需要逐行粒度,否则我将尝试尽可能多地使用基于集合的操作。
仅通过正确构建insert/update
语句即可避免大多数错误。
如果需要,可以将try/catch
与“空” catch块一起使用,这样失败就不会停止整个处理
根据您特定情况的随机性,您可能需要或需要将这两种方法混合使用