我从一开始就很抱歉,这个回复将会很长很详细,但是我最近看到了一些类似的问题,而且似乎还没有收到完整的答案。其中一个原因是,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用于显示目的(因为我通常发现这足以识别问题),但是您也可以通过添加其他字段来轻松更改此信息。
显然与许多调用数据库中的一个复杂的工作簿中,你会想要把错误代码显示在一个单独的子程序,这样就可以在多个地方调用它。
如果你用我的例子一起玩,第一次运行该代码应该执行罚款。然而,在第二次尝试时,您将收到主密钥违规消息。
很显然,您需要更改连接字符串,并确保你的表和存储过程具有相应的权限。
尝试检查连接对象的“错误”集合。 https://support.microsoft.com/en-us/help/167957/info-extracting-error-information-from-ado-in-vb –
@TimWilliams是否会工作? – user8406687
这是给你测试... –