SQL Server'继续下一步'等效

时间:2009-09-11 14:02:51

标签: sql sql-server vb.net tsql

我正在VB.net中开展一个项目,该项目接收包含T-SQL的大型文本文件,并针对本地SQL数据库执行它们,但我遇到了错误处理方面的问题。

我正在使用以下技术:

  • VB.net
  • Framework 3.5
  • SQL Express 2005

我试图执行的SQL大多是直接的,但我的应用程序完全不知道架构或其中包含的数据。例如:

UPDATE mytable SET mycol2='data' WHERE mycol1=1
INSERT INTO mytable (mycol1, mycol2) VALUES (1,'data')
UPDATE mytable SET mycol2='data' WHERE mycol1=2
INSERT INTO mytable (mycol1, mycol2) VALUES (1,'data')
UPDATE mytable SET mycol2='data' WHERE mycol1=3

以上是我正在执行的事情的一个示例,但这些文件每个将包含大约10,000到20,000个语句。

我的问题是,当使用sqlCommand.ExecuteNonQuery()时,我收到异常,因为第二个INSERT语句将触及表上的主键约束。

我需要知道发生了这个错误并将其记录下来,但还要处理任何后续语句。我已经尝试在TRY / CATCH块中包装这些语句,但我无法找到处理错误的方法,然后继续处理其他语句。

查询分析器似乎以这种方式运行,但在使用sqlCommand.ExecuteNonQuery()时则不行。

那么有没有T-SQL相当于'Resume Next'或其他一些方法我可以做到这一点而不会在我身上引入大量的字符串处理?

非常感谢任何帮助。

8 个答案:

答案 0 :(得分:2)

SQL Server确实有Try / Catch语法。参见:

http://msdn.microsoft.com/en-us/library/ms175976.aspx

要在文件中使用它,你要么必须重写文件本身以使用try / catch语法包装每一行,否则你的代码必须以编程方式修改文件内容以包装每一行。

没有T-SQL相当于“On Error Resume Next”,感谢Cthulhu。

答案 1 :(得分:1)

存在降低进程速度的风险(通过使数千次访问SQL服务器而不是一次),您可以通过将文件拆分为多个单独的查询(INSERT或UPDATE)来处理此问题。然后,您可以捕获每个错误,并记录或处理它,因为您的业务逻辑需要。

答案 2 :(得分:1)

实际上,您的批处理已执行到最后,因为密钥违规不会中断批处理执行。如果从Management Studio运行相同的SQL文件,您将看到结果是所有有效语句都已执行,并且消息面板包含每个键违例的错误。 ADO.NEt的SqlClient行为方式大致相同,但在批处理结束时(当SqlCommand.ExecuteNonQuery返回时),它会解析返回的消息并引发异常。例外是一个SqlException但它的Errors集合包含一个SqlError,用于发生的每个键冲突。

不幸的是,没有银弹。理想情况下,SQL文件不应导致错误。您可以选择迭代异常的SqlErrors,并根据个别情况决定错误是否严重,或者您可以忽略它,因为您知道SQL文件存在数据质量问题。有些错误可能很严重,不容忽视。请参阅Database Engine Error Severities

另一种选择是明确告诉SqlClient不要抛出。如果将连接的FireInfoMessageEventOnUserErrors属性设置为true,则会引发SqlConnection.InfoMessage事件而不是异常。

答案 3 :(得分:0)

我不知道下一步支持简历的方法,但一种方法是使用本地表变量来防止错误,例如。

Declare @Table table(id int, value varchar(100))
UPDATE mytable SET mycol2='data' WHERE mycol1=1
--Insert values for later usage
INSERT INTO @Table (id, value) VALUES (1,'data')
--Insert only if data does not already exist. 
INSERT INTO mytable (mycol1, mycol2) 
Select id, Value from @Table t left join 
mytable t2 on t.id = t2.MyCol1
where t2.MyCol is null and t.id = 1

修改

好吧,我不知道我本身就提出这个问题,但如果你在所有步骤结束时设置一个退出条件并保持一个退出条件,你可以通过在while循环中包装try catch来实现一种简历跟踪你最后执行的步骤。

Declare @Table table(id int, value varchar(100))

Declare @Step int
set @Step = 0
While (1=1)
Begin

    Begin Try

        if @Step < 1 
        Begin                 
               Insert into @Table  (id, value) values  ('s', 1)
                   Set @Step = @Step + 1
        End
        if @Step < 2
        Begin
            Insert into @Table values ( 1, 's')
                    Set @Step = @Step + 1
        End
        Break
    End Try 
    Begin Catch
      Set @Step = @Step + 1
    End Catch 

End 
Select * from @Table

答案 4 :(得分:0)

不幸的是,我认为有一种方法可以强制SqlCommand在返回错误后继续处理。

如果您不确定是否有任何命令会导致错误(并且会有一些性能成本),您应该将文本文件中的命令拆分为单独的SqlCommands ...这样您就可以尝试使用/ catch阻止并找到有问题的陈述。

...当然,这取决于文本文件中的T-SQL命令,每个命令都在一个单独的行上(或以其他方式描述)。

答案 5 :(得分:0)

我使用的一种技术是使用try / catch,并在catch中使用异常信息引发事件。然后,调用者可以连接一个事件处理程序,以便根据信息做任何事情(记录它,收集它以便在UI中报告,等等)。

您还可以包含将CancelableEventArgs对象作为第二个事件参数传递的技术(在许多.NET框架区域中使用,例如Windows.Forms事件和XSD验证),以及事件处理程序可以设置为指示的布尔字段毕竟处理应该中止。

我要求你做的另一件事是准备你的INSERT和UPDATE,然后用不同的argments多次调用它们。

答案 6 :(得分:0)

begin try
 --your critical commands
end try
begin catch
  -- is necessary write somethink like this
  select ''
end cath

答案 7 :(得分:0)

您需要检查PK值是否已经存在。另外,一个大交易总是比许多小交易要快。而且行和页面不需要那样长时间锁定。

-- load a temp/import table
create table @importables (mycol1 int, mycol2 varchar(50))
insert @importables (mycol1, mycol2) values (1, 'data for 1')
insert @importables (mycol1, mycol2) values (2, 'data for 2')
insert @importables (mycol1, mycol2) values (3, 'data for 3')

-- update the base table rows that are already there
update mt set MyCol2 = i.MyCol2
from @importables i (nolock)
    inner join MyTable mt on mt.MyCol1 = i.MyCol1
where mt.MyCol2 <> i.MyCol2 -- no need to fire triggers and logs if nothing changed

-- insert new rows AFTER the update, so we don't update the rows we just inserted.
insert MyTable (mycol1, mycol2)
select mycol1, mycol2
from @importables i (nolock)
    left join MyTable mt (nolock) on mt.MyCol1 = i.MyCol1
where mt.MyCol1 is null;

您可以通过打开SqlConnection,创建#temp表,使用SqlBulkCopy对该临时表进行批量插入并从那里进行增量操作(而不是我的@)来进一步改进此功能在此示例中为importables)。只要您使用相同的SqlConnection,该连接上的后续查询就可以访问#temp表,直到您删除它或断开连接为止。