2014-07-05 16 views
5

Ho un'applicazione basata su Excel che si basa su diversi database che mi collego utilizzando ADO (libreria Microsoft ActiveX Data Objects 6.1). I database risiedono su server regionali e c'è un sovraccarico nello stabilire la connessione iniziale, quindi memorizzo le connessioni in un oggetto Scripting.Dictionary per il riutilizzo.ADO con KDB +/qodbc.dll in VBA

Private moConnCacheDict As Scripting.Dictionary 

Quando utilizzo i driver del database kdb + qodbc.dll, ottengo un comportamento imprevisto. Posso collegare e riutilizzare più origini dati (Oracle, Sybase, Access) e un'istanza del database KDB come previsto. Tuttavia, se creo una seconda connessione al database KDB ed eseguo una query sul nuovo set di dati, nessun dato viene restituito nonostante il fatto che la query sia legittima.

Recordset.BOF = TRUE and Recordset.EOF = TRUE 

Sembra eseguito correttamente ei campi sono visibili. La connessione al server regionale precedente sembra persistere e posso recuperare con successo i dati che risiede sul server originale, nonostante il fatto che, se guardo,

Recordset.ActiveCommand.ActiveConnection.Properties.Item("Extended Properties") 

, è la nuova stringa di connessione.

La stringa di connessione KDB + utilizza la seguente sintassi:

DRIVER=kdb+;DBQ=XXXXX;UID=XXXXX;PWD=XXXXX; 

ho incluso le funzioni fondamentali VBA utilizzati come esempio:

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 

(Nota l'ADO.Recordset è sempre chiusa e impostare a nulla dal chiamante).

L'unico modo per risolvere il problema è chiudere tutte le connessioni del database (indipendentemente dal provider) e quindi riconnettersi al server regionale desiderato. Questo è terribilmente inefficiente in quanto devo riaprire tutte le connessioni esistenti. Si noti inoltre che non è sufficiente farlo semplicemente nella cartella di lavoro corrente. Questo deve essere fatto a livello di applicazione. Se QUALSIASI connessione ADO a QUALSIASI database è ancora aperta, posso creare una nuova connessione ADO KDB + ma continuerà a puntare all'istanza precedente.

Ho guardato le proprietà di errore dell'oggetto di connessione KDB + e ci sono due errori:

  1. Più-passaggio OLE DB operazione generato errori. Controllare ciascun valore di stato OLE DB, se disponibile. Nessun lavoro è stato fatto
  2. Il provider non supporta la proprietà.

questo sembra essere documentati in http://support.microsoft.com/kb/269495 ma sono in grado di individuare qualsiasi CLSID nel Registro di sistema in modo sono in grado di sperimentare con la modifica suggerita.

Se accendo ODBC registrazione vedo il seguente messaggio:

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) 

Questo sarebbe responsabile per l'errore in ogni caso?

Come sempre, qualsiasi aiuto e suggerimento sarebbe molto apprezzato.

+1

Mi sembra un errore nel driver KDB + OLE DB. Il mio suggerimento sarebbe quello di creare un * minimo *, un esempio riproducibile (basta aprire due connessioni KDB + e mostrare che il secondo non fa quello che dovrebbe essere) e inviarlo agli sviluppatori KDB + come segnalazione di bug. – Heinzi

risposta

0

Quello che stai vedendo è un bug nel driver, e dovresti cercare driver più recenti.

Non dovrei dare una risposta completa (invece di un commento) se non ho eseguito e testato il codice da solo, ma ti consiglio di enumerare la collezione di proprietà dell'oggetto di connessione e cercare il pool di connessioni .

L'impostazione del pool di connessioni su 0 (o su false, a seconda di cosa è possibile indovinare visualizzando il vartype del valore della proprietà) è una soluzione promettente. L'altra opzione consiste nell'utilizzare un recordset forward-only: che può funzionare o meno, ma vale la pena provarlo.

NB: C'è stato un progetto open source un paio di anni fa per scrivere un disco OLEDB corretto, ma sembra essersi sbiadito dalla vista.

+0

Non esiste alcuna proprietà denominata pool di connessioni sull'oggetto di connessione e l'inoltro non ha fatto nulla. Grazie comunque. – user2579685

+0

Enumerare la collezione di proprietà dell'oggetto Connection: Per i = 0 a oConn.Properties.Count-1 Debug.Print i & vbtab & oConn.Properties (i) .Name & "=" & oConn.Properties (i) .valore successivo –