我有一个基于Excel的应用程序,该应用程序依赖于使用ADO(Microsoft ActiveX数据对象6.1库)连接到的多个不同数据库。数据库驻留在区域服务器上,建立初始连接会产生开销,因此我将连接缓存在Scripting.Dictionary对象中以供重用。
Private moConnCacheDict As Scripting.Dictionary
当我使用kdb + qodbc.dll数据库驱动程序时,出现异常情况。我可以按预期连接和重用多个数据源(Oracle,Sybase,Access)和一个KDB数据库实例。但是,如果我创建第二个KDB数据库连接并对新数据集执行查询,尽管该查询是合法的,但不会返回任何数据。
Recordset.BOF = TRUE and Recordset.EOF = TRUE
看起来执行得很好,并且该字段是可见的。与以前的区域服务器的连接似乎仍然存在,即使我查看以下事实,我也可以成功检索驻留在原始服务器上的数据:
Recordset.ActiveCommand.ActiveConnection.Properties.Item("Extended Properties")
,是新的连接字符串。
KDB +连接字符串使用以下语法:
DRIVER=kdb+;DBQ=XXXXX;UID=XXXXX;PWD=XXXXX;
我已经包含了核心VBA功能作为示例:
Private Function ExecuteQuery(sDBName As String, ByRef oRst As ADODB.Recordset, Optional bDeleteConnection As Boolean) As Boolean
Dim oDBConn As ADODB.Connection
Dim sSql As String
'delete connection
If bDeleteConnection Then Call DropConnection(sDBName)
'get cached or new connection
Call GetConnection(sDBName, oDBConn)
Select Case sDBName
Case "MAIN_US"
sSql = mSQL_MAIN
Case "MD_ASIA"
sSql = mSQL_MDASIA
End Select
Set oRst = New Recordset
oRst.Open sSql, oDBConn, adOpenKeyset, adLockPessimistic
If Not oDBConn.State = adStateOpen Then Err.Raise vbObjectError + 1024, "ExecuteQuery", sDBName & ": Recordset Closed. Unable to execute query ->" & sSql
ExecuteQuery = True
End Function
Private Function GetConnection(sDBName As String, ByRef oDBConn As ADODB.Connection) As Boolean
If moConnCacheDict Is Nothing Then Set moConnCacheDict = New Dictionary
If moConnCacheDict.Exists(sDBName) Then
'get existing connection
Set oDBConn = moConnCacheDict.Item(sDBName)
Else
'create connection
Set oDBConn = New Connection
With oDBConn
.Mode = adModeRead
Select Case sDBName
Case "MAIN_US"
.Mode = adModeReadWrite
.ConnectionString = mCONN_MAIN
Case "MD_ASIA"
.Mode = adModeRead
.ConnectionString = mCONN_MDASIA
End Select
.CursorLocation = adUseServer
.Open
End With
moConnCacheDict.Add sDBName, oDBConn
End If
GetConnection = True
End Function
Private Function DropConnection(Optional sDBName As String) As Boolean
Dim oDBConn As ADODB.Connection
Dim i As Integer
'delete object directly from cache
If Not moConnCacheDict Is Nothing Then
If sDBName = vbNullString Then
'close all connections
For i = 0 To moConnCacheDict.Count - 1
If Not IsEmpty(moConnCacheDict.Items(i)) Then
Set oDBConn = moConnCacheDict.Items(i)
If Not oDBConn Is Nothing Then
If oDBConn.State = adStateOpen Then oDBConn.Close
Set oDBConn = Nothing
Debug.Print Now, "Dropping Database Connection - " & moConnCacheDict.Keys(i)
End If
End If
Next i
Set moConnCacheDict = Nothing
Else
If moConnCacheDict.Exists(sDBName) Then
If Not IsEmpty(moConnCacheDict.Item(sDBName)) Then
Set oDBConn = moConnCacheDict.Item(sDBName)
If Not oDBConn Is Nothing Then
If oDBConn.State = adStateOpen Then oDBConn.Close
Set oDBConn = Nothing
Debug.Print Now, "Dropping Database Connection - " & "Dropping Database Connection - " & sDBName
End If
End If
moConnCacheDict.Remove (sDBName)
End If
End If
End If
DropConnection = True
End Function
(请注意,ADO.Recordset始终关闭,并且调用方将其设置为空)。
解决此问题的唯一方法是关闭所有数据库连接(无论提供程序如何),然后重新连接到所需的区域服务器。这是非常低效的,因为我必须重新打开所有现有的连接。还要注意,仅在当前工作簿中仅执行此操作还不够。这必须在应用程序级别完成。如果到ANY数据库的ANY ADO连接仍然打开,我可以创建一个新的KDB + ADO Connection,但它仍将指向先前的实例。
我看了KDB +连接对象的错误属性,有两个错误:
多步OLE DB操作生成错误。检查每个OLE DB状态值(如果有)。没有工作。
提供者不支持该属性。
这似乎记录在http://support.microsoft.com/kb/269495中,但是我无法在注册表中找到任何CLSID,因此无法尝试建议的更改。
如果我打开ODBC日志记录,则会看到以下消息:
EXCEL 8dc-22d0 EXIT SQLGetInfoW with return code -1 (SQL_ERROR)
HDBC 0x02131EA8
UWORD 151 <SQL_KEYSET_CURSOR_ATTRIBUTES2>
PTR 0x003C4FB0
SWORD 4
SWORD * 0x00000000
DIAG [S1096] [Microsoft][ODBC Driver Manager] Information type out of range (0)
反正这会导致错误吗?
一如既往,任何帮助和建议将不胜感激。
最佳答案
您看到的是驱动程序中的错误,您应该寻找更新的驱动程序。
如果我自己还没有运行和测试代码,我不应该给出完整的答案(而不是评论),但是我建议您枚举连接对象的属性集合并寻找连接池。
将连接池设置为0(或设置为false,具体取决于您通过查看属性值的vartype可以猜到的内容)是一种很有前途的解决方法。您的另一种选择是使用仅转发记录集:可能有效或无效,但值得尝试。
NB:几年前有一个开源项目来编写适当的OLEDB驱动器,但是这种说法似乎已经淡出人们的视线。