Ускорьте эту операцию поиска / фильтрации - (VB6, TextFile, ADO, VFP 6.0 База данных) - PullRequest
0 голосов
/ 26 февраля 2011

Я пытаюсь выяснить, как ускорить эту операцию. Перед тем, как импортировать запись из текстового файла, мне сначала нужно посмотреть, существует ли она в базе данных. Если он существует, я собираюсь выполнить операцию обновления на нем. Если он не существует, я собираюсь создать новую запись.

Выполнение кода, который вы видите под этой операцией, занимает где-то около 3 часов.

Я пытался использовать метод поиска ADO, и на самом деле он медленнее, чем метод фильтра.

База данных является базой данных Visual Foxpro 6. Таблица имеет индекс для поля item_cd, но для таблицы не установлен первичный ключ. Это вне моего контроля, так как я не писал программное обеспечение и стараюсь держаться подальше от внесения каких-либо структурных изменений в базу данных.

В текстовом файле 46652 строки и около 650 000 записей / строк в наборе записей ADO. Я думаю, что сокращение набора записей было бы самым большим шагом в исправлении этого, но я не придумал никакого способа сделать это. Я пытаюсь предотвратить создание повторяющихся записей, поскольку первичный ключ отсутствует, и поэтому мне действительно нужно иметь всю таблицу в моем наборе записей.

Поскольку я запускаю это на своей локальной машине, похоже, что операция ограничена мощностью процессора. В действительности это может быть использовано по всей сети, особенно если я смогу заставить его работать быстрее.

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

Ответы [ 6 ]

1 голос
/ 22 ноября 2011

не отвечает на ваш вопрос, и это довольно старая тема, но почему бы вам не импортировать ваш текстовый файл во временную таблицу в вашей базе данных, а затем выполнить соединение? что-то вроде SELECT tt. * FROM texttemp tt левое внешнее соединение xmsalinv xal на tt.field1 = xal.item_cd, где xal.item_cd равно нулю

это должно вернуть содержимое вашего импортированного текстового файла, в котором нет совпадений item_cd в базе данных, так как вы имеете дело с текстовым файлом, который усложняет запрос, поэтому мне интересно, что вы не импортировали содержимое к временному столу.

теперь, предполагая, что вы знаете отображение полей, вы, вероятно, также можете использовать это для вставки, предполагая, что ваш db принимает нотацию вставки select, которую он будет вставлять в xmsalinv (fields) select (соответствие полей) из (как указано выше ...) ) это переместит ваши удушающие точки в процесс импорта, который, я надеюсь, будет быстрым.

коллекции ado кажутся довольно глупыми, поэтому они не получают никаких знаний о данных и немного медленны.

ах следующий пункт в "vb6 filter" google http://www.techrepublic.com/article/why-ados-find-method-is-the-devil/1045830

Этот ответ основан на базовых знаниях SQL и не предназначен для foxpro

1 голос
/ 11 июля 2011

Простое решение для ускорения ответа Yours_Rs.find состоит в том, чтобы сначала использовать оператор Yours_Rs.move, если это возможно для вас. Что я сделал, так это использовал оператор MyRs.move перед тем, как использовать MyRs.find, чтобы приблизиться к моей реальной записи. Это действительно сработало для меня, так как реакция на ход заявления довольно оживленная.

Я использовал его, чтобы найти запись пациента. Таким образом, перемещение указателя на запись рядом с фактической записью заставило оператор MyRs.find работать со скоростью света.

С уважением,

MAS.

0 голосов
/ 24 апреля 2013

3 часа всего за несколько сотен тысяч записей !!! Вы делаете это неправильно. Просто: - добавить текстовый файл в таблицу VFP, - затем вставьте те, которые не существуют в существующей таблице с одним SQL -и обновить те, которые существуют с другим обновлением sql.

Это все и должно занять меньше минуты (минута очень медленная). Вы можете сделать все это, используя драйвер VFPOLEDB, и не имеет значения, что у вас есть база данных VFP6, VFPOLEDB имеет встроенный механизм VFP9.

0 голосов
/ 27 февраля 2011

Сначала можно попробовать создать индекс в памяти на item_cd с помощью gXRst!item_cd.Properties("OPTIMIZE").Value = True, что ускорит как Find, так и Filter.

Для максимальной скорости поиска инициализируйте индекс помощника Collectionвот так

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

И инстадат Find используйте такую ​​функцию, как эта

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
0 голосов
/ 26 февраля 2011

В ответ на сообщение Боба Римерсма текстовый файл не вызывает проблем со скоростью.Я изменил свой код, чтобы открыть набор записей с запросом, ищущим один элемент.Этот код теперь выполняется за 1 минуту и ​​2 секунды, в отличие от трех-четырех часов, которые я смотрел в другую сторону.

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

Мои функции:

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
0 голосов
/ 26 февраля 2011

Используйте курсор пожарного шланга для результатов запроса VFP, если вы этого не сделаете, и см. Ваш другой пост здесь с предложениями относительно текстового файла Recordset.

Возможно, еще лучше, хотя вы можете попробовать избавиться отмедленный "цикл и поиск".

Я бы, вероятно, создал бы временную MDB 4.0 Jet с нуля для каждого текстового файла, который вы хотите найти.Импортируйте текстовые данные, проиндексируйте ваше ключевое поле.Используйте ADOX для определения связанной таблицы в базе данных VFP.Используйте запрос для сопоставления.

Закройте и утилизируйте MDB позже.

...