I have a part of code in VB6 which i'm trying to convert to VB.net. Specifically recordsets.
This is a part of VB6 code:
Data19.RecordSource = "select id from headers where type=12"
Data19.Refresh
If Data19.Recordset.RecordCount > 0 Then
Data6.RecordSource = "select sum(left * right) from footers where type=12"
Data6.Refresh
ss = Format(Data6.Recordset.Fields(0), "0.00")
Data19.Recordset.MoveLast
a = Data19.Recordset.RecordCount - 1
Data19.Recordset.MoveFirst
For i = 0 To a
If i > 0 Then Data19.Recordset.MoveNext
Data22.RecordSource = "select * from documents where type=12"
Data19.Recordset.Fields(0)
Data22.Refresh
With Data22.Recordset
If .RecordCount > 0 Then
.MoveLast
b = .RecordCount - 1
.MoveFirst
For j = 0 To b
If j > 0 Then .MoveNext
If .Fields("link1") < ra And .Fields("code") <> "00" Then
.Edit
.Fields("link1") = ra
.Fields("pc") = Format(.Fields("dpc") * (100 - ra) / 100, "0.00")
.Fields("total") = Format(.Fields("amount") * .Fields("dpc") * (100 - ra) / 100, "0.00")
.Update
End If
Next
End If
End With
Next
End If
I am a bit confused about .MoveLast, .MoveFirst,.Recordset.
In my VB.net code i have been using this function to get the table that i want from the database:
Public Function returnTable(ByVal queryString As String)
Dim query1 As String = queryString
'Console.WriteLine(query1)
Dim table As New DataTable
Using connection As New MySqlConnection(connection)
Using adapter As New MySqlDataAdapter(query1, connection)
Dim cmb As New MySqlCommandBuilder(adapter)
table.Clear()
adapter.Fill(table)
Return table
End Using
End Using
End Function
So the part with recordsources should go something like this if i'm not wrong:
Dim data19 as new datatable
Data19 = returnTable("select id from headers where type=12")
But later on, in the code, i cannot figure out how to write the equivalents to .MoveLast, .MoveFirst,.Recordset,.MoveNext, etc...
It looks like you're trying to write the VB.Net equivalents to VB6 .MoveLast, .MoveFirst, .Recordset, .MoveNext, etc.
.Recordset
You're very close to your solution with the returnTable function that you created. Datatables are just collections of DataRow objects, which is a bit different than recordset objects in VB6. You created a DataTable with your .Net function:
Dim data19 as new datatable
Data19 = returnTable("select id from headers where type=12")
In this case, Data19 is a DataTable that takes the place of a recordset in the VB6 example.
.MoveLast
In your VB6 example, the reason for using .MoveLast is to expose the count of records in the recordset. The count of records is not known until you move to the last record.
After you move to the last record, then you can load the count into a variable.
With ADO.Net, you don't need to use .MoveLast to get the count. You can simply get the row count like this:
Dim row_count As Integer = Data19.Rows.Count
You'll see below that this variable is not needed when you convert to .Net. You use it in VB6 to know how many records to loop through (and when to stop). In .Net you will use For Each.. Next to achieve the same purpose.
.MoveFirst
For your example, .MoveFirst is used only because you used .MoveLast to get the record count. In order to walk through the recordset, you have to go back to the first record.
Since you no longer need to use .MoveLast in .Net, you also don't need to use .MoveFirst.
.MoveNext
In your VB6 example, .MoveNext is used to walk through the recordset and do some actions on each row. To walk through the DataTable that you created, you can do something like this:
Dim my_row as DataRow
For Each my_row in Data19.Rows
If my_row("link1") < ra And my_row("code") <> "00" Then
.. do some actions
End If
Next
This will walk through the recordset in a similar fashion. One thing to consider is that you are working with a disconnected recordset when you use your DataTable. When you get to the .Edit and .Update parts of your VB6 procedure, you might need to use a parameterized query to perform the actual update to any records. This will use a command object and .ExecuteNonQuery() method to perform an SQL update statement.
Related
I have two recordsets, the first (rs1) is a query result from Access database, the other (rs2) was created by converting an array built inside VBA. The first contains a series of data, the second, some descriptions related to one of the columns of the first.
I'd like to join these two recordsets into a third, having the records from the first one, and the description fields of the second. Something similar to an INNER JOIN in a SQL query.
Is it possible in VBA? Something like a query using rs1 and rs2 as tables and the necessary query structure (SELECT xyx FROM rs1, rs2 WHERE abc)...
For the array in recordset conversation, I've found out this code and it works pretty well:
Private Function ADOCopyArrayIntoRecordset(argArray As Variant) As ADODB.Recordset
Dim rsADO As ADODB.Recordset
Dim lngR As Long
Dim lngC As Long
Set rsADO = New ADODB.Recordset
For lngC = 1 To UBound(argArray, 2)
rsADO.Fields.Append "Fld" & lngC, adVariant
Next lngC
rsADO.Open
For lngR = 1 To UBound(argArray, 1)
For lngC = 1 To UBound(argArray, 2)
rsADO.AddNew
rsADO.Fields(lngC - 1).Value = argArray(lngR, lngC)
Next lngC
rsADO.MoveNext
Next lngR
rsADO.MoveFirst
Set ADOCopyArrayIntoRecordset = rsADO
End Function
I am working on a visual basic project. I have a mdb database connected to my project. I want to add a SELECT query that finds the results which are in array that i give it on my program
I have tried to write a statement like that:
SELECT kodu, adi_soyadi, sectigi_ders_say
FROM ogrenciler
WHERE kodu IN ?
But it does not work. In my page codes I have an array and I want to find results from "ogrenciler" table where the "kodu" is in my array.
Well, you could send that array to a temp table in Access, but that would prevent more then one user using the software at the same time. (or you could add some user name to the temp table. However, if the array of choices is small, say about max 50, then you can create the sql string.
eg:
Dim MySQL As String = "SELECT * from tblHotels WHERE ID IN("
Dim IdList(5) As Integer
Dim i As Integer
For i = 1 To 5
IdList(i) = i
Next
Dim MyList As String = ""
For i = 1 To 5
If MyList <> "" Then MyList = MyList & ","
MyList = MyList & IdList(i)
Next
MySQL = MySQL & MyList & ")"
Using MyCon2 As New OleDbConnection(My.Settings.OLESQL)
Dim da As New OleDbDataAdapter(MySQL, MyCon2)
Dim rstDat As New DataTable()
da.Fill(rstDat)
For i = 0 To rstDat.Rows.Count - 1
Debug.Print(rstDat.Rows(i).Item("HotelName"))
Next ' etc etc. etc.
End Using
So you can use the SQL format of:
SELECT * FROM tblHotels where ID IN (1,2,3)
And thus build up the "list". The only downside to this approach is that the sql string is limited to 2000 characters. So, if your list is larger then say 50 or so items, then you have to adopt a different approach.
I am creating a DB in Access and using VBA to implement a feature to run several INSERT queries into multiple tables, based on values found in other 'template' tables.
At a certain point in the process, I am retrieving the results of a SELECT query and using the results of the query as parameters in an INSERT.
I have built and tested the query using the Access query builder so I know that the query functions as expected.
I am using the DAO library to interface with the DB and run my queries.
I have the function below which converts a recordset returned from the latter function to a collection of collections.
In the function below have run into a problem where the recordset I return apparently contains zero records. This causes it to throw a 'No Current Record' exception on the line 'records.MoveLast'.
What I should be seeing, which I know from the query, is a Recordset containing 2 records, with 5 fields each.
Private Function RecordsetToCollection(records As RecordSet) As Collection
Dim recordCollection As New Collection
Dim i As Integer
'Go to first record?
'Exception thrown here
records.MoveLast
records.MoveFirst
'Check if current record position before first record
If Not records.BOF Then
'While not after last record
While Not records.EOF
'Collection to hold field values
Dim fieldCollection As New Collection
'Loop through fields
For i = 0 To records.Fields.Count - 1
'Add to collection
fieldCollection.Add records.Fields(i).Value
Next i
'Add field collection to record collection
recordCollection.Add fieldCollection
Set fieldCollection = Nothing
'Go to next record
records.MoveNext
Wend
End If
'Return collection
Set RecordsetToCollection = recordCollection
End Function
The recordset being fed into this function is retrieved using the following function:
Private Function GetTemplateDeliverables(TemplateProjectActivityID As Integer) As Collection
'Get Template Deliverables recordset from tbl_TemplateDeliverables using given ProjectActivityID
'Open query
Dim qdf As DAO.QueryDef
Set qdf = CurrentDb.QueryDefs("qry_GetTemplateDeliverables")
'Add parameters
qdf.Parameters("Project Activity ID") = ProjectActivityID
'Get return recordset
Dim rst As RecordSet
Set rst = qdf.OpenRecordset()
Dim recordCollection As New Collection
Set recordCollection = RecordsetToCollection(rst)
'Get ProjectActivityID from recordset
Set GetTemplateDeliverables = recordCollection
'Clean up
qdf.Close
Set qdf = Nothing
Set rst = Nothing
End Function
Does anyone have any suggestions as to why this may be the case?
I can't see why this isn't working given that I already have functions to retrieve recordsets that are working fine, the only difference being that in those functions each record has only 1 field, whereas this has 5 fields, but I can't think why this would be a problem.
Any help would be much appreciated!
(P.S. any tips on how to improve my code would also be of help.)
I hope this is a simple question and you don't have to waste too much of you time on it.
I have an report (called repRAD78) which contains a textbox (called txtRAD8). I would like to populate txtRAD8 with a calculated value based on numbers pulled from a query called qryrRAD78.
Looking through the forums it looks like recordsets would be the solution but this is my first foray into recordsets and it's not going well. :(
The code I have pasted in below I have pulled together from a number of places and it doesn't produce any errors but puts the same value into txtRAD8 for all the records.
I'm sorry if this is a stupid question but it's been driving me potty.
Many thanks for your time.
Al.
Public Sub Calc()
Dim dbs As DAO.Database
Dim rst As DAO.Recordset
Set dbs = CurrentDb
Set rst = dbs.OpenRecordset("qryrRAD78")
rst.MoveFirst
Do Until rst.EOF = True
Dim lngMean As Long
Dim lngRAD78max As Long
Dim lngRAD78_1 As Long
Dim lngRAD78_2 As Long
Dim lngRAD78_3 As Long
Dim lngRAD7 As Long
Dim lngRAD8 As Long
lngRAD78_1 = rst![RAD78_1]
lngRAD78_2 = rst![RAD78_2]
lngRAD78_3 = rst![RAD78_3]
lngRAD8b_c = rst![RAD8b_c]
lngMean = (lngRAD78_1 + lngRAD78_2 + lngRAD78_3) / 3
lngRAD78max = Maximum(Abs(lngRAD78_1), Abs(lngRAD78_2), Abs(lngRAD78_3))
lngRAD7 = ((lngRAD78max - lngMean) / lngMean) * 100
lngRAD8 = ((lngMean - lngRAD8b_c) / lngRAD8b_c) * 100
txtRAD8.Value = lngRAD8
rst.MoveNext
Loop
rst.Close
dbs.Close
End Sub
Private Sub Detail_Format(Cancel As Integer, FormatCount As Integer)
Calc
End Sub
Here's a second approach to this. Rather than using a function in the code, take the calculations from your Calc() routine and put them in another query.
SELECT idrRAD78,
(RAD78_1 + RAD78_2 + RAD78_3) AS Mean,
(IIf(Abs(RAD78_1) > Abs(RAD78_2),
IIf(Abs(RAD78_1) > Abs(RAD78_3), RAD78_1, RAD78_3),
IIf(Abs(RAD78_2) > Abs(RAD78_3), RAD78_2, RAD78_3))) AS RAD78Max,
(((RAD78max - Mean) / Mean) * 100) AS RAD7,
(((Mean - RAD8b_c) / RAD8b_c) * 100) AS RAD8
FROM qryrRAD78
This will give you a query that performs the same calculations as your existing function. Then just edit the report query to join to this new query (just like joining a table) using something like:
FROM ReportQuery INNER JOIN NewQuery ON ReportQuery.idrRAD78 = NewQuery.idrRAD78
Change the query names to match the real names. Add the fields from the new query in the SELECT part of your report query:
SELECT <existing field list>, RAD7, RAD8
Then set txtRAD8 to the RAD8 field.
I'm just doing this from memory as I'm not in front of my own computer, but hopefully that makes sense and is close enough to the correct code.
The problem with this function is that every row on the report is going to have a textbox called txtRAD8. So what you are really doing is updating every textbox on the report with the same value (once for every loop through the recordset). You are not actually setting the value for each individual row.
What you need to do is make the value of the textbox = Calc(RowID). Then your query uses the passed-in parameter to get the value for that one record instead of looping through the whole recordset, and updates just that one row on the report.
So your Sub becomes a Function, and returns the calculated value.
I have an MS Access (.accdb) table with data like the following:
Location Number
-------- ------
ABC 1
DEF 1
DEF 2
GHI 1
ABC 2
ABC 3
Every time I append data to the table I would like the number to be unique to the location.
I am accessing this table through MS Excel VBA - I would like to create a new record (I specify the location in the code) and have a unique sequential number created.
Is there a way to setup the table so this happens autmatically when a record is added?
Should I write a query of some description and to determine the next number per location, and then specify both the Location & Number when I create the record?
I am writing to the table as below:
Set rst = New ADODB.Recordset
rst.CursorLocation = adUseServer
rst.Open Source:="Articles", _
ActiveConnection:=cnn, _
CursorType:=adOpenDynamic, _
LockType:=adLockOptimistic, _
Options:=adCmdTable
rst.AddNew
rst("Location") = fLabel.Location 'fLabel is an object contained within a collection called manifest
rst("Number") = 'Determine Unique number per location
rst.Update
Any help would be appreciated.
Edit - Added the VBA code I am struggling with as question was put on-hold
I suspect that you are looking for something like this:
Dim con As ADODB.Connection, cmd As ADODB.Command, rst As ADODB.Recordset
Dim newNum As Variant
Const fLabel_Location = "O'Hare" ' test data
Set con = New ADODB.Connection
con.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Users\Public\Database1.accdb;"
Set cmd = New ADODB.Command
cmd.ActiveConnection = con
cmd.CommandText = "SELECT MAX(Number) AS maxNum FROM Articles WHERE Location = ?"
cmd.CreateParameter "?", adVarWChar, adParamInput, 255
cmd.Parameters(0).Value = fLabel_Location
Set rst = cmd.Execute
newNum = IIf(IsNull(rst("maxNum").Value), 0, rst("maxNum").Value) + 1
rst.Close
rst.Open "Articles", con, adOpenDynamic, adLockOptimistic, adCmdTable
rst.AddNew
rst("Location").Value = fLabel_Location
rst("Number").Value = newNum
rst.Update
rst.Close
Set rst = Nothing
Set cmd = Nothing
con.Close
Set con = Nothing
Note, however, that this code is not multiuser-safe. If there is the possibility of more than one user running this code at the same time then you could wind up with duplicate [Number] values.
(To make the code multiuser-safe you would need to create a unique index on ([Location], [Number]) and add some error trapping in case the rst.Update fails.)
Edit
For Access 2010 and later consider using an event-driven Data Macro and shown in my other answer to this question.
You need to add a new column to your table of data type AutoNumber.
office.microsoft.com: Fields that generate numbers automatically in Access
You should probably also set this column as your primary key.
For Access 2010 and newer, this is a better way to do it. It uses the table's Before Change Data Macro to derive the next sequential number and put it in the [Number] field of the new record:
The advantages of this approach are:
The sequence number will be applied whenever a new record is added, regardless of how it is added.
The Excel VBA code does not have to worry about creating the sequence number; it "just happens".
Since this code resides at the table level it should be safe for a multi-user environment.
For more information on Data Macros, see
Create a data macro