I have the following, but it is not putting the count of records from the query in the MsgBox. Only the RR and the TT.
On Error Resume Next
Dim recordCount2
Set con = CreateObject("ADODB.Connection")
Set objRecordset = CreateObject("ADODB.Recordset")
con.ConnectionString = "Provider=SQLOLEDB;Integrated Security=SSPI;Persist Security Info=False;Data Source=servername\logon;Initial Catalog=database_name"
con.Open
strQry = "SELECT * FROM smd..table_name (nolock) WHERE CAST(LastRunDate AS DATE) = CAST(GETDATE() AS DATE) AND TableNameKey in ('value1', 'value2')"
Set data = con.Open(strQry)
objRecordset.Open strQuery, adoConnection, adOpenDynamic, adLockOptimistic
recordCount2 = objRecordset.Count
MsgBox "TT " & recordCount2 & "RR"
objRS.Close: Set objRS = Nothing
con.Close: Set con = Nothing
My guess is there's an error that's occurring which is being hidden by On error resume next, and recordCount2 = objRecordset.Count is not actually succeeding. Why do you have On error resume next anyway? Delete that line or comment it out, and your problem should become obvious.
Using On Error Resume Next isn't a "magic bullet". Especially like this quote from #ansgar-wiechers
"Contrary to popular belief it doesn't magically make errors go away."
On Error Resume Next is very useful but needs to be used in the correct context. While it is active any statement that raises an error is handled silently, the statement that raised the error is skipped and the inbuilt Err object is populated with the error details for error trapping.
As others have suggested the first thing you should do when debugging these types of problems is comment out On Error Resume Next then the issues I'm about to highlight you might have found yourself.
In the example above there are a couple of lines that are likely raising errors and being skipped, these are;
Set data = con.Open(strQry)
This statement appears to want to execute the query in strQry but con.Open() is the wrong method for this, the ADODB.Connection is already open it doesn't need opening again. You likely meant (but this is a pure guess);
Set data = con.Execute(strQry)
You don't appear to use data after you try running it so I would in this situation just comment it out for now.
The next is;
objRecordset.Open strQuery, adoConnection, adOpenDynamic, adLockOptimistic
which tries to open the ADO.Recordset using strQuery which doesn't appear to defined and neither is adoConnection you likely meant (again guess work);
objRecordset.Open strQry, con, adOpenDynamic, adLockOptimistic
If this statement raises an error and is skipped the statement
recordCount2 = objRecordset.Count
will itself error because the objRecordset .State will be set to adStateClosed.
After these suggestions you should have something like;
'On Error Resume Next
Dim recordCount2, constr
Set con = CreateObject("ADODB.Connection")
Set objRecordset = CreateObject("ADODB.Recordset")
constr = "Provider=SQLOLEDB;Integrated Security=SSPI;Persist Security Info=False;Data Source=servername\logon;Initial Catalog=database_name"
con.Open constr
strQry = "SELECT * FROM smd..table_name (nolock) WHERE CAST(LastRunDate AS DATE) = CAST(GETDATE() AS DATE) AND TableNameKey in ('value1', 'value2')"
'Set data = con.Open(strQry)
objRecordset.Open strQry, con, adOpenDynamic, adLockOptimistic
recordCount2 = objRecordset.Count
MsgBox "TT " & recordCount2 & "RR"
objRecordset.Close: Set objRecordset = Nothing
con.Close: Set con = Nothing
Avoid writing code as much as possible by writing only those lines/statements you are absolutely sure of. If you haven't seen a bit of documentation that explains why the statement x is neccessary to solve your problem, leave x off.
Before writing code, read the docs: RecordCount property:
... The cursor type of the Recordset object affects whether the number
of records can be determined. The RecordCount property will return -1
for a forward-only cursor; the actual count for a static or keyset
cursor; and either -1 or the actual count for a dynamic cursor,
depending on the data source. ...
So your plan is:
Open a connection
Get a recordset
Access its .RecordCount
The general skeleton for a VBScript contains:
Option Explicit
It does not contain the evil global OERN.
The database work specific skeleton contains:
Dim oCn : Set oCN = CreateObject("ADODB.Connection")
oCn.Open "DSN=???"
...
oCn.Close
It does not contain distracting Set x = Nothing tails.
For production scripts or test code wrt connection problems, you need to write long/complicated/tailored connection strings; for experimental code wrt small specific database features/problems/surprises a (once-for-all-puzzled-together-with-GUI-support) ODBC/DSN connection is more efficient and less error-prone.
The Recordset should be .Opened to play with the Cursor Type:
oRs.Open "SELECT AddressId FROM Person.Address", oCn, adOpenDynamic
Optional Locking left off (until you have documentary or experimental evidence that Lock Type influences .RecordCount).
If you try to run
Option Explicit
Dim oCn : Set oCN = CreateObject("ADODB.Connection")
oCn.Open "DSN=AdvWork"
Dim oRs : Set oRS = CreateObject("ADODB.Recordset")
oRs.Open "SELECT AddressId FROM Person.Address", oCn, adOpenDynamic
WScript.Echo ".RecordCount:", oRs.RecordCount
oCn.Close
you will be told: Variable is undefined: 'adOpenDynamic'. A bit of further (re)reading and your code will look like
Option Explicit
Const adOpenKeyset = 1
Dim oCn : Set oCN = CreateObject("ADODB.Connection")
oCn.Open "DSN=AdvWork"
Dim oRs : Set oRS = CreateObject("ADODB.Recordset")
oRs.Open "SELECT AddressId FROM Person.Address", oCn, adOpenKeyset
WScript.Echo ".RecordCount:", oRs.RecordCount
oCn.Close
output:
cscript 39519953.vbs
.RecordCount: 19614
and the world is a (little bit) better place.
#Brad Larson and #Ekkehard.Horner.... thanks for the guidance, i too was struggling to get the record count from a recordset using VBS and obtaining some info from the windows SYSTEMINDEX (it kept returning -1).
Setting up the Const adOpenKeyset = 1 and then adding that term to the [recordset.oen "SELECT....", oCn, adOpenKeyset] line made it work!!
you will be told: Variable is undefined: 'adOpenDynamic'. A bit of
further (re)reading and your code will look like
Option Explicit
Const adOpenKeyset = 1
Dim oCn : Set oCN = CreateObject("ADODB.Connection")
oCn.Open "DSN=AdvWork"
Dim oRs : Set oRS = CreateObject("ADODB.Recordset")
oRs.Open "SELECT AddressId FROM Person.Address", oCn, adOpenKeyset
WScript.Echo ".RecordCount:", oRs.RecordCount
oCn.Close
output: cscript 39519953.vbs .RecordCount: 19614 and the world is a
(little bit) better place.
Related
I am converting some Excel-VBA code that uploaded a DAO recordset to an Access database. My new code uses ADODB objects and needs to push the data to Oracle 12c.
I reviewed some articles, and found a handy equivalency chart here: From-DAO-to-ADO. Using this information I created the following code.
This first bit just loads up the source recordset. No problems here, but published if it's relevant:
Dim CN As New ADODB.Connection, RS As New ADODB.Recordset
Dim SRC_CN As New ADODB.Connection, SRC_RS As New ADODB.Recordset, SRC_CMD As New ADODB.Command
strSQL = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & Src_WB_nm & _
";Extended Properties='Excel 12.0 Xml;HDR=NO';"
SRC_CN.Open strSQL
Set SRC_CMD.ActiveConnection = SRC_CN
SRC_CMD.CommandType = adCmdText
SRC_RS.Close
Set SRC_RS = Nothing
strSQL = "SELECT * FROM [" & TableNm & "]"
SRC_CMD.CommandText = strSQL
With SRC_RS
.CursorLocation = adUseClient
.CursorType = adOpenDynamic
.LockType = adLockOptimistic
.Open SRC_CMD
End With
In this segment, I open the destination connection and attempt to open the destination recordset. Thismethodology (AddNew, RS...value = RS.value, RS.Update...) worked when I was in DAO. I expect it may need to be modified somewhat, but its the RS.Open command that I can't get past now.
CN.Open CSTRG
strSQL = "DELETE FROM DFSTOOL.C_QUERYLIST"
Set RS = CN.Execute(strSQL)
Set RS = Nothing
RS.Open "DFSTOOL.C_QUERYLIST", CN, adCmdTable
Do Until SRC_RS.EOF
RS.AddNew
RS.Fields(0).Value = SRC_RS.Fields(0).Value
RS.Update
SRC_RS.MoveNext
Loop
RS.Close
SRC_RS.Close
Set RS = Nothing
Set SRC_RS = Nothing
The error thrown is:
I appreciate any help you can provide!
So happily this was a simple syntax issue. Perhaps I should delete the post, but I'll leave it for now in case it helps anyone else. The equivalency table I referenced noted that the "adCmdTable" is an option in ADO, whereas the equivalent "dbOpenTable" was a type in DAO. Thus, I was required to skip a few fields so that it was in the right location. Other fine-tuning followed and the final iteration works as expected:
RS.Open "DFSTOOL.C_QUERYLIST", CN, adOpenForwardOnly, adLockOptimistic, adCmdTable
Do Until SRC_RS.EOF
RS.AddNew
RS.Fields(1).Value = SRC_RS.Fields(0).Value
RS.Update
SRC_RS.MoveNext
Loop
Thanks for your patience community!
My question today is a rather simple one. What I have is a VB Module that contains the code to return me an ADODB.RecordSet object with records fetched from a SQL Query that has been executed. It works like this:
sSql_SerCheck = "SELECT DISTINCT Serial FROM dbo.WipReservedSerial WHERE Serial LIKE '" & serialTempSearch
sSql_SerCheck = sSql_SerCheck & "' ORDER BY Serial DESC "
dbGetRecordSet(sSql_SerCheck)
Then the results sit in object rs that is accessed like the following
temp = rs(0) 'For the value at the first column for the first record
rs.MoveNext 'This moves to the next record in the record set
Now what I am trying to do here is to the number of records contained within this recordset object. Now I did some research on the class and found there is a
RecordCount att.
So what I want to do is simple:
if( rs.RecordCount > 0) then
serCheck1 = rs(0)
MsgBox serCheck1
end if
The problem is my RecordCount returns -1. I found this article http://www.w3schools.com/asp/prop_rs_recordcount.asp that states that record count will return -1 for the following:
Note: This property will return -1 for a forward-only cursor; the actual count for a static or keyset cursor; and -1 or the actual count for a dynamic cursor.
Note: The Recordset object must be open when calling this property. If this property is not supported it will return -1.
How do I get this object to return the correct number of records??
The code for the VB Module is added below:
Public cn, rs
'Specify pSQL as SQL Statement
Function dbGetRecordset(sSql)
dbCloseConnection()
Set cn = CreateObject("ADODB.Connection")
cn.CommandTimeout = 600
cn.Open(Conn & SystemVariables.CodeObject.CompanyDatabaseName)
Set rs = CreateObject("ADODB.Recordset")
rs.Open sSql, cn, 3, 3
End Function
As your rs.RecordCount > 0 just checks whether the recordset is not empty, you can avoid .Recordcount (and all it's problems) by testing for Not rs.EOF
Don't trust secondary sources; the MS docs contain "... and either -1 or the actual count for a dynamic cursor, depending on the data source". So maybe your provider is to blame. In that case (or when you really need a specific number), a SELECT COUNT() would be a workaround
Don't use magic numbers as in rs.Open sSql, cn, 3, 3, but define (and doublecheck) your Consts like adOpenStatic, adLockOptimistic, ...
From Help
Set oRs = New ADODB.Recordset
oRs.CursorLocation = adUseClient
oRs.Open sSQL, sConn, adOpenStatic, adLockBatchOptimistic, adCmdText
Help has a full description of Cursors in What is a Cursor (ADODB Programmers Guide - Windows Software Development Kit).
You'll burn resources either locally or on the server to get a record count. If you are going through the data anyway, just count them.
This is how to go through a recordset one at a time.
Do While not .EOF
Outp.writeline .Fields("Txt").Value
.MoveNext
Loop
Set connection = CreateObject("adodb.connection")
connection.open "Driver=your driver details"
If connection.State = 1 Then
Set myRecord = CreateObject("ADODB.recordset")
sql= "select * from...."
myRecord.Open sql, connection
i = 0
Do While Not myRecord.EOF
i=i+1
myRecord.MoveNext
loop
msgbox "RecordCount="&i
myRecord.Close
END IF
set myRecord = nothing
set Connection = nothing
I was finding that the vbscript version of this was flaky... the best option seemed to use a CursorLocation of 3.. vbscript didn't seem to understand adUseClient.
set conn=CreateObject("ADOdb.connection")
conn.CursorLocation = 3
conn.open DSNQ
set rs = conn.execute("select * from sometable order by 1 desc")
msgbox (rs.RecordCount & " records" )```
So, I've been asked to update an old Classic ASP website. It did not use parameterized queries and there was very little input validation. To simplify things I wrote a helper function that opens a connection to the database, sets up a command object with any parameters, and creates a disconnected recordset [I think!?! :)] Here's the code:
Function GetDiscRS(DatabaseName, SqlCommandText, ParameterArray)
'Declare our variables
Dim discConn
Dim discCmd
Dim discRs
'Build connection string
Dim dbConnStr : dbConnStr = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=" & rootDbPath & "\" & DatabaseName & ".mdb;" & _
"Persist Security Info=False"
'Open a connection
Set discConn = Server.CreateObject("ADODB.Connection")
discConn.Open(dbConnStr)
'Create a command
Set discCmd = Server.CreateObject("ADODB.Command")
With discCmd
Set .ActiveConnection = discConn
.CommandType = adCmdText
.CommandText = SqlCommandText
'Attach parameters to the command
If IsArray(ParameterArray) Then
Dim cnt : cnt = 0
For Each sqlParam in ParameterArray
discCmd.Parameters(cnt).Value = sqlParam
cnt = cnt + 1
Next
End If
End With
'Create the Recordset object
Set discRs = Server.CreateObject("ADODB.Recordset")
With discRs
.CursorLocation = adUseClient ' Client cursor for disconnected set
.LockType = adLockBatchOptimistic
.CursorType = adOpenForwardOnly
.Open discCmd
Set .ActiveConnection = Nothing ' Disconnect!
End With
'Return the Recordset
Set GetDiscRS = discRS
'Cleanup
discConn.Close()
Set discConn = Nothing
discRS.Close() ' <=== Issue!!!
Set discRs = Nothing
Set discCmd = Nothing
End Function
My problem is that if I call discRS.Close() at the end of the function, then the recordset that is returned is not populated. This made me wonder if the recordset is indeed disconnected or not. If I comment that line out everything works properly. I also did some Response.Write() within the function using discRS values before and after setting ActiveConnection = Nothing and it properly returned the recordset values. So it seems to be isolated to discRS.Close().
I found an old article on 4guysfromrolla.com and it issues the recordset Close() in the function. I've seen the same thing on other sites. I'm not sure if that was a mistake, or if something has changed?
Note: I'm using IIS Express built into Visual Studio Express 2013
Disconnected recordset as far as I know refers to a recordset populated manually, not from database, e.g.used as multi dimensional array or kind of hash table.
So what you have is not a disconnected recordset since it's being populated from database, and by disposing its connection you just cause your code to not work properly.
Since you already have Set discConn = Nothing in the code you don't have to set it to nothing via the recordset or command objects, it's the same connection object.
To sum this all up, you should indeed get rid of tho following lines in your code:
Set .ActiveConnection = Nothing ' Disconnect!
discRS.Close() ' <=== Issue!!!
Set discRs = Nothing
Then to prevent memory leaks or database lock issues, you should close and dispose the recordset after actually using it in the code using the function e.g.
Dim oRS
Set oRS = GetDiscRS("mydatabase", "SELECT * FROM MyTable", Array())
Do Until oRS.EOF
'process current row...
oRS.MoveNext
Loop
oRS.Close ' <=== Close
Set oRS = Nothing ' <=== Dispose
To avoid all this hassle you can have the function return "real" disconnected recordset by copying all the data into newly created recordset. If relevant let me know and I'll come with some code.
In your function, you cannot close and clean up your recordset if you want it to be returned to the calling process.
You can clean up any connections and command objects, but in order for your recordset to be returned back populated, you simply do not close it or dispose of it.
Your code should end like this:
'Cleanup
discConn.Close()
Set discConn = Nothing
'discRS.Close()
'Set discRs = Nothing
'Set discCmd = Nothing
end function
In your code i can see:
Set .ActiveConnection = Nothing ' Disconnect!
So, this Recordset isn't already closed?
He is indeed using a disconnected recordset. I started using them in VB6. You set the connection = Nothing and you basically have a collection class with all the handy methods of a recordset (i.e. sort, find, filter, etc....). Plus, you only hold the connection for the time it takes to fetch the records, so back when Microsoft licensed their servers by the connection, this was a nice way to minimize how many userm were connected at any one time.
The recordset is completely functional, it's just not connected to the data source. You can reconnect it and then apply any changes that were made to it.
It was a long time ago, it seems that functionality has been removed.
You should use the CursorLocation = adUseClient. Then you can disconnect the recordset. I have created a function to add the parameters to command dictionary objects, and then return a disconnected recordset.
Function CmdToGetDisconnectedRS(strSQL, dictParamTypes, dictParamValues)
'Declare our variables
Dim objConn
Dim objRS
Dim Query, Command
Dim ParamTypesDictionary, ParamValuesDictionary
'Open a connection
Set objConn = Server.CreateObject("ADODB.Connection")
Set objRS = Server.CreateObject("ADODB.Recordset")
Set Command = Server.CreateObject("ADODB.Command")
Set ParamTypesDictionary = Server.CreateObject("Scripting.Dictionary")
Set ParamValuesDictionary = Server.CreateObject("Scripting.Dictionary")
Set ParamTypesDictionary = dictParamTypes
Set ParamValuesDictionary = dictParamValues
Query = strSQL
objConn.ConnectionString = strConn
objConn.Open
With Command
.CommandText = Query
.CommandType = adCmdText
.CommandTimeout = 15
Dim okey
For Each okey in ParamValuesDictionary.Keys
.Parameters.Append .CreateParameter(CStr(okey), ParamTypesDictionary.Item(okey) ,adParamInput,50,ParamValuesDictionary.Item(okey))
Next
.ActiveConnection = objConn
End With
objRS.CursorLocation = adUseClient
objRS.Open Command , ,adOpenStatic, adLockBatchOptimistic
'Disconnect the Recordset
Set objRS.ActiveConnection = Nothing
'Return the Recordset
Set CmdToGetDisconnectedRS = objRS
'Clean up...
objConn.Close
Set objConn = Nothing
Set objRS = Nothing
Set ParamTypesDictionary =Nothing
Set ParamValuesDictionary =Nothing
Set Command = Nothing
End Function
I'm trying to figure out how to speed up this operation. Before I import a record from the text file I first need to see if one exists in the database. If it does exist I'm going to perform an update operation on it. If it does not exist I'm going to create a new record.
Running the code you see below this operation takes somewhere in the neighborhood of 3 hours.
I've tried using ADO's find method and it actually appears to be slower than the filter method.
The database is a Visual Foxpro 6 database. The table does have an index on the item_cd field but the table does not have any primary key established. This is out of my control since I didn't write the software and I'm trying to stay away from making any structural changes to the database.
There are 46652 rows in the text file and about 650,000 records/rows in the ADO recordset. I think slimming down the recordset would be the biggest step in fixing this but I haven't come up with any way of doing that. I'm trying to prevent creating duplicate records since there is no primary key and so I really need to have the entire table in my recordset.
Because I'm running this on my local machine it appears that the operation is limited by the power of the CPU. In actuality this might be used across the network, especially if I can get it to go faster.
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
A simple solution to speed up Yours_Rs.find response is to use the Yours_Rs.move statement first if it is possible for you. What I have done is to use MyRs.move statement prior to using MyRs.find to come in the vicinity of my actual record. It had really worked for me as response of move statement is quite brisk.
I was using it to locate a patient record. So, moving the pointer to a record near the actual record made MyRs.find statement to work with the speed of light.
regards,
MAS.
doesn't answer your question and this is a pretty old thread, but
why don't you import your text file to a temporary table on your db then do a join?
something like
SELECT tt.* FROM texttemp tt left outer join xmsalinv xal on tt.field1=xal.item_cd where xal.item_cd is null
this should return the contents of your imported text file which don't have any item_cd matches in the database, since you're dealing with a text file that complicates the query which is why i'm wondering your not importing the contents to a temporary table.
now assuming you know the mapping of the fields, you can probably also use this to insert assuming your db accepts insert select notation it'd be insert into xmsalinv (fields) select (matching fields) from (as above...)
this moves your choke points to the import process, which i'm hoping is quick.
the ado collections seem like they're pretty stupid, so they don't benefit from any sort of knowledge about the data and are kinda slow.
ah next item on "vb6 filter" google http://www.techrepublic.com/article/why-ados-find-method-is-the-devil/1045830
this response is based on basic sql knowledge and not tailored to foxpro
Use a firehose cursor for the VFP query's results if you aren't, and see your other post here for suggestions regarding the text file Recordset.
Perhaps better yet though, you might try getting rid of your slow "loop and search" aproach.
I would probably create a temporary Jet 4.0 MDB from scratch for each text file you want to look up. Import the text data, index your key field. Use ADOX to define a linked table over in the VFP database. The use a query to do your matching.
Close and dispose of the MDB afterward.
In response to Bob Riemersma's post, the text file is not causing the speed issues. I've changed my code to open a recordset with a query looking for a single item. This code now runs in 1 minute and 2 seconds as opposed to the three to four hours I was looking at the other way.
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
My Functions:
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
First can try creating an in-memory index on item_cd with gXRst!item_cd.Properties("OPTIMIZE").Value = True which will speed up both Find and Filter.
For ultimate speed in searching initialize helper index Collection like this
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
And insetad of Find use some function like this
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
3 hours just for a few hundred thousands of records!!! You are doing it the wrong way. Simply:
-append text file to a VFP table,
-then insert the ones that do not exist in existing table with a single SQL
-and update the ones that exist with another Update sql.
That is all and should take less than a minute (a minute is even very slow). You can do all these using the VFPOLEDB driver and it doesn't matter that you have VFP6 database, VFPOLEDB has VFP9 engine built-in.
DBF file is in C:\dbase\clip53\PRG\stkmenu\WPACK3\
DBF file is called WPACKS.CFG (deliberately not .DBF)
The VB6 code in an ActiveX EXE for opening the database and recordset:
Function OpenDatabase(sFile As Variant, Optional sProvider As Variant = "Provider=Microsoft.Jet.OLEDB.4.0") As Variant ' ADODB.Connection
Dim nErr As Long
Dim sErr As String
Dim oConnection As Object 'ADODB.Connection
Set oConnection = CreateObject("ADODB.Connection")
On Error Resume Next
oConnection.open sProvider & ";Data Source=" & sFile
nErr = Err.Number
sErr = Err.Description
On Error GoTo 0
If nErr <> 0 Then
Err.Raise OPENDATABASE_E_NOTFOUND, , sErr
End If
Set OpenDatabase = oConnection
End Function
Function OpenRecordSet(ByRef oDb As Variant, sQuery As Variant, Optional bCmdText As Boolean = False) As Variant ''ADODB.Connection ADODB.Recordset
Const adOpenForwardOnly As Long = 0
Const adOpenStatic As Long = 3
Const adOpenDynamic As Long = 2
Const adOpenKeyset As Long = 1
Const adLockOptimistic As Long = 3
Const adCmdText As Long = 1
Dim oRecordSet As Object 'ADODB.Recordset
Set oRecordSet = CreateObject("ADODB.RecordSet")
If bCmdText Then
oRecordSet.open sQuery, , , adCmdText
Else
oRecordSet.open sQuery, oDb, adOpenKeyset, adLockOptimistic
End If
Set OpenRecordSet = oRecordSet
End Function
The script accessing these methods looks a little like VBScript. It is VBScript, but executed by the aforementioned ActiveX EXE which uses MSScript control and has a whole pile of objects which it can make available to the script engine. A kind of VBScript-on-steroids approach.
uses database
uses system
dim db
dim rs
set db = database.opendatabase("C:\dbase\clip53\PRG\stkmenu\WPACK3\","Provider=Microsoft.Jet.OLEDB.4.0;Extended Properties=dBase III;User ID=Admin;Password=")
set rs = database.openrecordset(db, "SELECT * FROM WPACKS.CFG",true)
system.consolewriteline rs.recordcount
My problem is that I keep getting The connection cannot be used to perform this operation. It is either closed or invalid in this context. when it hits the oRecordSet.open sQuery, , , adCmdText (which I got from a Microsoft site.)
'Tis a tad irritating.
The connection string I use when I need to connect a DBF file is usually something like:
"Driver={Microsoft dBase Driver (*.dbf)};dbq=<filePath>"
It works fine for me.
try using the latest and greatest FoxPro driver.