Acelere esta operación de búsqueda/filtro-(base de datos VB6, TextFile, ADO, VFP 6.0)
text-files visual-foxpro (6)
Estoy tratando de descubrir cómo acelerar esta operación. Antes de importar un registro desde el archivo de texto, primero necesito ver si existe uno en la base de datos. Si existe, voy a realizar una operación de actualización en él. Si no existe voy a crear un nuevo registro.
Ejecutar el código que se ve debajo de esta operación toma alrededor de 3 horas.
Intenté usar el método de búsqueda de ADO y, de hecho, parece ser más lento que el método de filtro.
La base de datos es una base de datos Visual Foxpro 6. La tabla tiene un índice en el campo item_cd, pero la tabla no tiene ninguna clave principal establecida. Esto está fuera de mi control ya que no escribí el software y estoy tratando de evitar realizar cambios estructurales en la base de datos.
Hay 46652 filas en el archivo de texto y aproximadamente 650,000 registros / filas en el conjunto de registros ADO. Creo que adelgazar el conjunto de registros sería el mayor paso para solucionarlo, pero no he encontrado ninguna forma de hacerlo. Estoy tratando de evitar la creación de registros duplicados ya que no hay una clave principal, así que realmente necesito tener toda la tabla en mi conjunto de registros.
Debido a que estoy ejecutando esto en mi máquina local, parece que la operación está limitada por la potencia de la CPU. En realidad, esto podría usarse en toda la red, especialmente si puedo hacerlo más rápido.
Dim sFileToImport As String
sFileToImport = Me.lstFiles.Text
If sFileToImport = "" Then
MsgBox "You must select a file from the listbox to import."
Exit Sub
End If
If fConnectToDatabase = False Then Exit Sub
With gXRst
.CursorLocation = adUseClient
.CursorType = adOpenKeyset
.LockType = adLockReadOnly
.Open "SELECT item_cd FROM xmsalinv ORDER BY item_cd ASC", gXCon
End With
Call fStartProgress("Running speed test.")
Dim rstTxtFile As ADODB.Recordset
Set rstTxtFile = New ADODB.Recordset
Dim con As ADODB.Connection
Set con = New ADODB.Connection
Dim sConString As String, sSQL As String
Dim lRecCount As Long, l As Long
Dim s As String
sConString = "DRIVER={Microsoft Text Driver (*.txt; *.csv)};Dbq=" & gsImportFolderPath & ";Extensions=asc,csv,tab,txt;Persist Security Info=False;"
con.Open sConString
sSQL = "SELECT * FROM [" & sFileToImport & "]"
rstTxtFile.Open sSQL, con, adOpenKeyset, adLockPessimistic
If Not (rstTxtFile.EOF And rstTxtFile.BOF) = True Then
rstTxtFile.MoveFirst
lRecCount = rstTxtFile.RecordCount
Do Until rstTxtFile.EOF = True
''This code appears to actually be slower than the filter method I''m now using
''gXRst.MoveFirst
''gXRst.Find "item_cd = ''" & fPQ(Trim(rstTxtFile(0))) & "''"
gXRst.Filter = "item_cd = ''" & fPQ(Trim(rstTxtFile(0))) & "''"
If Not (gXRst.EOF And gXRst.BOF) = True Then
s = "Item Found - " & Trim(rstTxtFile(0)) ''item found
Else
s = "Item Not Found - " & Trim(rstTxtFile(0)) ''Item not found found
End If
l = l + 1
Call subProgress(l, lRecCount, s)
rstTxtFile.MoveNext
Loop
End If
Call fEndProgress("Finished running speed test.")
Cleanup:
rstTxtFile.Close
Set rstTxtFile = Nothing
gXRst.Close
En respuesta a la publicación de Bob Riemersma, el archivo de texto no está causando los problemas de velocidad. Cambié mi código para abrir un conjunto de registros con una consulta que busca un solo elemento. Este código ahora se ejecuta en 1 minuto y 2 segundos, en comparación con las tres o cuatro horas que estuve mirando hacia otro lado.
Dim sFileToImport As String
sFileToImport = Me.lstFiles.Text
If sFileToImport = "" Then
MsgBox "You must select a file from the listbox to import."
Exit Sub
End If
If fConnectToDatabase = False Then Exit Sub
Call fStartProgress("Running speed test.")
Dim rstTxtFile As ADODB.Recordset
Set rstTxtFile = New ADODB.Recordset
Dim con As ADODB.Connection
Set con = New ADODB.Connection
Dim sConString As String, sSQL As String
Dim lRecCount As Long, l As Long
Dim sngQty As Single, sItemCat As String
sConString = "DRIVER={Microsoft Text Driver (*.txt; *.csv)};Dbq=" & gsImportFolderPath & ";Extensions=asc,csv,tab,txt;Persist Security Info=False;"
con.Open sConString
sSQL = "SELECT * FROM [" & sFileToImport & "]"
rstTxtFile.Open sSQL, con, adOpenKeyset, adLockPessimistic
If Not (rstTxtFile.EOF And rstTxtFile.BOF) = True Then
rstTxtFile.MoveFirst
lRecCount = rstTxtFile.RecordCount
Do Until rstTxtFile.EOF = True
l = l + 1
sItemCat = fItemCat(Trim(rstTxtFile(0)))
If sItemCat <> "[item not found]" Then
sngQty = fItemQty(Trim(rstTxtFile(0)))
End If
Call subProgress(l, lRecCount, sngQty & " - " & sItemCat & " - " & rstTxtFile(0))
sngQty = 0
rstTxtFile.MoveNext
Loop
End If
Call fEndProgress("Finished running speed test.")
Cleanup:
rstTxtFile.Close
Set rstTxtFile = Nothing
Mis funciones:
Private Function fItemCat(sItem_cd As String) As String
''Returns blank if nothing found
If sItem_cd <> "" Then
With gXRstFind
.CursorLocation = adUseClient
.CursorType = adOpenKeyset
.LockType = adLockReadOnly
.Open "SELECT item_cd, ccategory FROM xmsalinv WHERE item_cd = ''" & fPQ(sItem_cd) & "''", gXCon
End With
If Not (gXRstFind.EOF And gXRstFind.BOF) = True Then
''An item can technically have a blank category although it never should have
If gXRstFind!ccategory = "" Then
fItemCat = "[blank]"
Else
fItemCat = gXRstFind!ccategory
End If
Else
fItemCat = "[item not found]"
End If
gXRstFind.Close
End If
End Function
Private Function fIsStockItem(sItem_cd As String, Optional bConsiderItemsInStockAsStockItems As Boolean = False) As Boolean
If sItem_cd <> "" Then
With gXRstFind
.CursorLocation = adUseClient
.CursorType = adOpenKeyset
.LockType = adLockReadOnly
.Open "SELECT item_cd, bal_qty, sug_qty FROM xmsalinv WHERE item_cd = ''" & fPQ(sItem_cd) & "''", gXCon
End With
If Not (gXRstFind.EOF And gXRstFind.BOF) = True Then
If gXRstFind!sug_qty > 0 Then
fIsStockItem = True
Else
If bConsiderItemsInStockAsStockItems = True Then
If gXRstFind!bal_qty > 0 Then
fIsStockItem = True
End If
End If
End If
End If
gXRstFind.Close
End If
End Function
Private Function fItemQty(sItem_cd As String) As Single
''Returns 0 if nothing found
If sItem_cd <> "" Then
With gXRstFind
.CursorLocation = adUseClient
.CursorType = adOpenKeyset
.LockType = adLockReadOnly
.Open "SELECT item_cd, bal_qty FROM xmsalinv WHERE item_cd = ''" & fPQ(sItem_cd) & "''", gXCon
End With
If Not (gXRstFind.EOF And gXRstFind.BOF) = True Then
fItemQty = CSng(gXRstFind!bal_qty)
End If
gXRstFind.Close
End If
End Function
Utilice un cursor firehose para los resultados de la consulta de VFP si no es así, y consulte su otra publicación aquí para obtener sugerencias sobre el conjunto de registros de archivo de texto.
Sin embargo, tal vez aún mejor, podrías intentar deshacerte de tu lento enfoque de "bucle y búsqueda".
Probablemente crearía un MDB Jet 4.0 temporal desde cero para cada archivo de texto que quiera buscar. Importe los datos de texto, indexe su campo clave. Use ADOX para definir una tabla vinculada en la base de datos de VFP. El uso de una consulta para hacer su coincidencia.
Cierre y deseche el MDB después.
Primero, intente crear un índice en memoria en item_cd
con gXRst!item_cd.Properties("OPTIMIZE").Value = True
que acelerará tanto Find
como Filter
.
Para obtener la máxima velocidad en la búsqueda, inicialice el índice de ayudante Collection
como esta
Set cIndex = New Collection
On Error Resume Next
Do While Not gXRst.EOF
cIndex.Add gXRst.Bookmark, "#" & gXRst!item_cd.Value
gXRst.MoveNext
Loop
On Error GoTo ErrorHandler
E insertada de Find
usa alguna función como esta
Public Function SearchCollection(Col As Object, Index As Variant) As Boolean
On Error Resume Next
IsObject Col(Index)
SearchCollection = (Err.Number = 0)
On Error GoTo 0
End Function
Una solución simple para acelerar la respuesta de Yours_Rs.find es usar primero la declaración Yours_Rs.move si es posible para usted. Lo que he hecho es usar la declaración MyRs.move antes de usar MyRs.find para acercarme a mi registro actual. Realmente me había funcionado ya que la respuesta de move statement es bastante enérgica.
Lo estaba usando para ubicar un registro de paciente. Por lo tanto, al mover el puntero a un registro cerca del registro real, la instrucción MyRs.find funciona con la velocidad de la luz.
Saludos,
MAS.
no responde su pregunta y este es un hilo muy antiguo, pero ¿por qué no importa su archivo de texto a una tabla temporal en su base de datos y luego hace una combinación? algo así como SELECT tt. * FROM texttemp tt left outer join xmsalinv xal en tt.field1 = xal.item_cd donde xal.item_cd es nulo
esto debería devolver el contenido de su archivo de texto importado que no tiene ninguna coincidencia item_cd en la base de datos, ya que se trata de un archivo de texto que complica la consulta y es por eso que me pregunto si no va a importar el contenido a un archivo temporal mesa.
ahora suponiendo que conozca el mapeo de los campos, probablemente también pueda usar esto para insertar asumiendo que su base de datos acepte insertar notación de selección que sería insertar en xmsalinv (campos) seleccionar (campos coincidentes) desde (como arriba ...) esto se mueve su estrangulamiento apunta al proceso de importación, que espero sea rápido.
las colecciones de ado parecen ser bastante estúpidas, por lo que no se benefician de ningún tipo de conocimiento sobre los datos y son un poco lentos.
ah siguiente artículo en "filtro vb6" google http://www.techrepublic.com/article/why-ados-find-method-is-the-devil/1045830
esta respuesta se basa en conocimientos básicos de sql y no está adaptada a foxpro
3 horas solo por unos cientos de miles de discos !!! Lo estás haciendo de la manera incorrecta. Simplemente: -aplique el archivo de texto en una tabla de VFP, luego inserte los que no existen en la tabla existente con un único SQL y actualice los que existen con otra actualización de SQL.
Eso es todo y debería tomar menos de un minuto (un minuto es incluso muy lento). Puede hacer todo esto usando el controlador VFPOLEDB y no importa que tenga una base de datos VFP6, VFPOLEDB tiene el motor VFP9 incorporado.