2017-08-02 44 views
0

My SQL Server test procedure我想知道的SQL Server过程的错误输出是打印或VBA知道,这样我可以检查我的过程完全执行或不

我的VBA代码的测试是这样

Public mycon As New ADODB.Connection 
Const st As String = "Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=True;Data Source=WEL-COME-PC\SQLEXPRESS;Use Procedure for Prepare=1;Auto Translate=True;Packet Size=4096;Workstation ID=WEL-COME-PC;Use Encryption for Data=False;Tag with column collation when possible=False;Initial Catalog=testdb" 

Sub conopen() 

If mycon.State = 0 Then 
    mycon.Open st 
End If 

End Sub 

Sub conclose() 

If mycon.State = 1 Then 

mycon.Close 
End If 
End Sub 

Sub proceduretotest() 

query = "exec usp_datepractice '7/30/2017','7/29/2017','576.test'" 

Call conopen 
MsgBox "connected to sql server" 

mycon.Execute query 

Call conclose 


End Sub 

这是我测试的SQL Server程序:

create table datepractice 
(
    TodaysDate Date, 
    yesterdays date, 
) 

create procedure usp_datepractice 
    (@datetodays date, 
     @dateyesterday date, 
     @datetest varchar 
    ) 
as 
begin 
    insert into datepractice (TodaysDate, yesterdays) 
    values (@datetodays, @dateyesterday) 

    insert into datepractice (TodaysDate, yesterdays) 
    values (@datetodays, @dateyesterday) 

    insert into datepractice (TodaysDate, yesterdays) 
    values (@datetest, '09/09/2017') 

    update datepractice 
    set yesterdays = '7/26/2017' 
    where yesterdays = '7/29/2017' 
end 

exec usp_datepractice '7/30/2017','7/29/2017','576.test' 

我特意把'576.test'存储过程的SQL Server中抛出一个错误,现在,W如果我通过VBA执行程序,程序部分运行,并且在影响某些行后,它会抛出我故意输入的错误。

现在我想打印或知道VBA中的错误,因为它不显示在VBA和过程继续运行。

我想知道的SQL Server过程错误输出

消息241,级别16,状态1,过程usp_datepractice,7号线
转换从字符串转换日期和/或时间时失败

要从VBA打印或报告,以便我可以检查我的程序是否完全执行。

+1

尝试检查连接对象的“错误”集合。 https://support.microsoft.com/en-us/help/167957/info-extracting-error-information-from-ado-in-vb –

+0

@TimWilliams是否会工作? – user8406687

+0

这是给你测试... –

回答

2

我从一开始就很抱歉,这个回复将会很长很详细,但是我最近看到了一些类似的问题,而且似乎还没有收到完整的答案。其中一个原因是,AFAIK不是一个简单的解决方案。这基本上是因为SQL Server不使用与.NET或Office相同的异常抛出。因此,毫无疑问,即使您知道发生了错误,您的VBA代码中也不会出现异常,您可以捕获它。

那么我们该怎么做呢?我的解决方案相对比较复杂,但一旦建立,它就可以用于任何需要调用的存储过程。

首先在SQL Server中创建一个错误表。我使用这个:

CREATE TABLE Errors 
(
    ErrorNumber int NOT NULL, 
    ErrorMessage nvarchar(4000) NOT NULL, 
    ErrorSeverity int NOT NULL, 
    ErrorState int NOT NULL, 
    ErrorLine int NOT NULL, 
    ErrorProcedure nvarchar(128) NOT NULL, 
    Msg varchar(200) NULL, 
    ErrorID int NOT NULL, 
CONSTRAINT PK_Errors PRIMARY KEY CLUSTERED 
(
    ErrorID ASC 
) 
) 

接下来创建一个程序,所有其他过程遇到错误时可以调用。我的是:

CREATE PROCEDURE uspErrorReporter 

@msg varchar(200) 

AS 
declare @id int; 

SET @id = (SELECT (ISNULL(MAX(ErrorID),0)) + 1 FROM Errors) 


INSERT INTO Errors 
    SELECT 
     ERROR_NUMBER() AS ErrorNumber, 
     ERROR_MESSAGE() AS ErrorMessage, 
     ERROR_SEVERITY() AS ErrorSeverity, 
     ERROR_STATE() AS ErrorState, 
     ERROR_LINE() AS ErrorLine,   
     ERROR_PROCEDURE() AS ErrorProcedure, 
     @msg, 
     @id; 

SELECT [email protected] 

请注意,这里我正在返回负号。这是因为我的存储过程通常返回受影响的行数(通过SELECT @@ ROWCOUNT),这应该是正数,所以我想要一个错误代码为负数。

接下来我们创建一个存储过程来返回发生的任何错误细节。事情是这样的:

CREATE PROCEDURE uspErrorDetailsGet 

@id int 

AS 
    SELECT 
     ErrorNumber, 
     ErrorMessage, 
     ErrorSeverity, 
     ErrorState, 
     ErrorLine,   
     ErrorProcedure, 
     Msg 
    FROM Errors WHERE ErrorID = [email protected]; 

最后(SQL Server中),我们现在改变我们的存储过程调用此错误收集机构。我举以下矿井用于说明目的简单:

CREATE procedure uspDealerInsert 
@dealer varchar(15) 

as 

begin try 

    set nocount on 

    INSERT INTO Dealers VALUES(@dealer) 
    SELECT @@ROWCOUNT 

    set nocount off 

end try 

begin catch 
    declare @msg nvarchar(200) 
    SET @msg = ('Error during insertion of dealer...') 

    EXECUTE uspErrorReporter @msg 
end catch 

如果你想测试我的例子,你需要创建一个经销商表。这是因为它得到一样简单:

CREATE TABLE Dealers(
    Dealer varchar(15) NOT NULL, 
    CONSTRAINT PK_Dealers PRIMARY KEY CLUSTERED 
    (
    Dealer ASC 
    ) 
) 

现在我们已经准备好了一些VBA:

Sub Button1_Click() 

Dim conn As ADODB.Connection 
Dim cmd As ADODB.Command 
Dim connStr As String 
Dim param As ADODB.Parameter 
Dim result As ADODB.Recordset 
Dim retCode As Integer 

    connStr = "Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=dbName;Data Source=serverName" 

    Set conn = New ADODB.Connection 
    conn.ConnectionString = connStr 
    conn.Open 

    Set cmd = New ADODB.Command 
    Set param = cmd.CreateParameter 
    param.Name = "@dealer" 
    param.Type = adVarChar 
    param.Value = "Chris" 
    param.Size = 15 
    cmd.Parameters.Append param 

    With cmd 
     .ActiveConnection = conn 
     .CommandType = adCmdStoredProc 
     .CommandText = "uspDealerInsert" 
     Set result = .Execute 
    End With 

    If Not result.EOF Then 
     retCode = result.Fields(0).Value 
     If retCode < 0 Then 
      Dim msgcmd As ADODB.Command 
      Dim msgparam As ADODB.Parameter 
      Set msgcmd = New ADODB.Command 
      Set msgparam = msgcmd.CreateParameter 
      msgparam.Name = "@id" 
      msgparam.Type = adInteger 
      msgparam.Value = retCode 
      msgcmd.Parameters.Append msgparam 

      Dim msgresult As ADODB.Recordset 
      With msgcmd 
       .ActiveConnection = conn 
       .CommandType = adCmdStoredProc 
       .CommandText = "uspErrorDetailsGet" 
       Set msgresult = .Execute 
       If Not msgresult.EOF Then 
        MsgBox msgresult.Fields(1).Value 
       End If 
      End With 
      Set msgcmd = Nothing 
      Set msgresult = Nothing 
     End If 

    End If 

    conn.Close 
    Set result = Nothing 
    Set cmd = Nothing 
    Set conn = Nothing 

End Sub 

现在对于一些解释。 ADODB.Command执行返回一个RecordSet。它在这个RecordSet中,因此我们可以找到我们自己的返回代码。通常在我的过程中,我返回的是受行影响的单个值,因此记录集只有一行和一个字段。这就是为什么我们知道受影响的行应该显示为Fields(0).Value。但是现在我们从之前的SQL Server工作中受益匪浅。如果返回的数字是负数,我们知道它是错误表中条目的(负)ID。所以我们调用存储过程来加载一个记录集,其中包含这个错误的完整条目。在我的示例中,我仅将ErrorMessage(Fields(1))发送到MsgBox用于显示目的(因为我通常发现这足以识别问题),但是您也可以通过添加其他字段来轻松更改此信息。

显然与许多调用数据库中的一个复杂的工作簿中,你会想要把错误代码显示在一个单独的子程序,这样就可以在多个地方调用它。

如果你用我的例子一起玩,第一次运行该代码应该执行罚款。然而,在第二次尝试时,您将收到主密钥违规消息。

很显然,您需要更改连接字符串,并确保你的表和存储过程具有相应的权限。

+0

它是不是在庞大的程序会工作,以及 – user8406687

+0

我有一些非常复杂的和整个使用相同的技术。重要的是,每个过程要么返回@@ ROWCOUNT,要么返回errorid的负数。某些过程有多个影响多个记录的语句。在这种情况下,我每次累计@@ ROWCOUNT并返回总和。 –

0

添加SET NOCOUNT ON;作为存储过程的第一个语句。这将抑制由SQL Server返回的消息(行计数)DONE_IN_PROC,该ADO经典(不是ADO.NET)返回为应用程序没有列的空结果集。

如果DONE_IN_PROC被由于SET NOCOUNT OFF(缺省值)返回时,应用程序代码需要调用在处理后续语句之前由Connection.Execute返回的RecordSet对象上的NextRecordset方法。除非需要处理多个记录集,否则只需指定SET NOCOUNT ON;通常会更容易。

相关问题