Object Required Error When Declaring String Variable - vba

I am fetching a set of names from a database query and then reformatting it to a comma separated list. As I am using this functionallity a few Places in my app, I try to write it as a function getting the sql-query and returning the string.
Public Function String_from_query(StrSQL As String) As String
Dim dbs As Database
Dim rs As Recordset
Set dbs = CurrentDb
Dim results As String
results = ""
Set rs = dbs.OpenRecordset(StrSQL)
If Not (rs.EOF And rs.BOF) Then
rs.MoveFirst
Do While Not rs.EOF
If results > "" Then
results = results & ", "
End If
results = results & rs("Navn")
rs.MoveNext
Loop
End If
Set String_from_query = results
End Function
This is then called from an event handler:
Private Sub Detalj_Format(Cancel As Integer, FormatCount As Integer)
Dim StrSQL As String
StrSQL = "SELECT Personer.Navn FROM Personer INNER JOIN Personoppgaver ON Personer.Initialer = Personoppgaver.Initialer WHERE Personoppgaver.Oppgaveid =" & Me.Oppgaveid.Value
Me.Tekst52.Value = String_from_query(StrSQL)
End Sub
If I have the code from the String_from_query function within the event handler and then directly assigns Me.Tekst52 to results, everything works fine. When I refactor the code as shown, I get a "Compile Error, Object required" when I try to run it and a marker on the last line in the sub. (Set String_from_query = results). I am not able to see what is wrong. Any help?

The keyword Set is only required when assigning variables to an Object. For Access, this would be Forms, Reports, Recordsets, etc. Or other Objects outside of Access (FileSystemObject, for example).
When setting strings, dates, numbers, etc, you do not need Set.
You can surmise this from the error message as well, Object required.

Related

Passing Variable to DoCmd.MoveSize to move Forms

I'm trying to keep the position of all forms uniform when switching between them all, but when calling the code in a function, MaxSize isn't working.
This records the WindowLeft and WindowTop value in a table, and I run this just before any form closes.
Function recordFormPosition(frm As Form)
On Error GoTo Error
DoCmd.SetWarnings False
'Table Field Window position
DoCmd.RunSQL "UPDATE FormPosition SET FormTop = """ & frm.WindowTop & """"
DoCmd.RunSQL "UPDATE FormPosition SET FormLeft = """ & frm.WindowLeft & """"
DoCmd.SetWarnings True
Exit Function
debug
Error:
MsgBox ("Error")
End Function
This function is called on any Form_Load to set the form to the position recorded in the table
Function setFormPosition(frm As Form)
Dim db As DAO.Database
Set db = CurrentDb
Dim tp As DAO.Recordset
Dim lft As DAO.Recordset
Dim wintop As String
Dim winleft As String
'Sets location of form
wintop = "SELECT FormTop FROM FormPosition WHERE ID = 1"
winleft = "SELECT FormLeft FROM FormPosition WHERE ID = 1"
Set tp = db.OpenRecordset(wintop)
Set lft = db.OpenRecordset(winleft)
frm.DoCmd.MoveSize tp.Fields(0).Value, lft.Fields(0).Value
End Function
And I use this to call the function and pass the form variable
Call setFormPosition([Form]) or Call recordFormPosition([Form])
My problem is with this line:
DoCmd.MoveSize tp.Fields(0).Value, lft.Fields(0).Value
Where do I pass on the frm. variable? I keep getting the error: An Expression in argument 2 has an invalid value, but I'm passing 2 intergers from a table that is formatted to numbers. I did some debugging and passed tp.Fields(0).Value & lft.Fields(0).Value in msgbox's and can verify that they are returning numbers, so the only explaination is that it has to be a focus issue

Microsoft Access - Crosstab of a filtered form

I'm trying to generate a report similar to a crosstab. The data are from a filtered form (Dates and WorkerID (String)).
form: frmReg
table with data: tReg
report: reportReg
On the following line:
Set qdf = dbsReport.QueryDefs(Me.RecordSource)
I'm getting the error:
Error 3265 Item not found in this collection
What am I doing wrong?
Private Sub Report_Open(Cancel As Integer)
' Create underlying recordset for report using criteria
Dim intX As Integer
Dim qdf As QueryDef
Dim frm As Form
' Don't open report if frmReg form isn't loaded.
If Not (IsLoaded("frmReg")) Then
Cancel = True
MsgBox "To preview or print this report, you must open " _
& "frmReg in Form view.", vbExclamation, _
"Must Open Dialog Box"
Exit Sub
End If
' Set database variable to current database.
Set dbsReport = CurrentDb
Set frm = Forms!frmReg
' Open QueryDef object.
' Set qdf = dbsReport.QueryDefs("ReportReg")
Me.RecordSource = "SELECT * FROM [tReg]"
Set qdf = dbsReport.QueryDefs(Me.RecordSource)
' Open Recordset object.
Set rstReport = qdf.OpenRecordset()
' Set a variable to hold number of columns in crosstab query.
intColumnCount = rstReport.Fields.Count
End Sub
It looks like the problem might be a relationship issue between the SQL and the commands and you probably do not have a query setup to take the information you are seeking.
Try this:
sSQL = "SELECT * FROM [tReg]"
Me.RecordSource = sSQL
Set qdf = dbsReport.CreateQueryDef("NewQuery", sSQL)
'This will purge the query after your inteactions are complete
dbsReport.QueryDefs.Delete "NewQuery"
Note: This will not include any interactions for the QueryDef.
The QueryDefs collection takes saved, named queries and not SQL statements. As #Jiggles32 demonstrates, you need to create a named query and then reference it with QueryDefs() call.
However, you can bypass the use of queries by simply directly opening recordsets with OpenRecordset() which is the end result of your needs:
strSQL = "SELECT * FROM [tReg]"
Me.RecordSource = strSQL
Set rstReport = dbsReport.OpenRecordset(strSQL)
' Set a variable to hold number of columns in crosstab query.
intColumnCount = rstReport.Fields.Count
In fact, you can directly extract a form's recordset using RecordsetClone property (preferred over Recordset if running various operations to not affect form's actual records):
strSQL = "SELECT * FROM [tReg]"
Me.RecordSource = strSQL
Set rstReport = Me.RecordsetClone
' Set a variable to hold number of columns in crosstab query.
intColumnCount = rstReport.Fields.Count

DAO recordset filter function cannot filter with 2 properties

I am using VBA in MS Access 2010.
I am currently trying to filter from a recordset with 2 fields.
However i tired, it will not filter as per what i want.
But if i were to filter based on only one field, the recordset is able to filter accordingly.
This is what I have now.
Private Function getCheckedRecordsFromDB(ByVal cmNum As String) As Boolean
Dim rs As Recordset
Dim rsFiltered As Recordset
Dim iSeral As Integer
'Gets different fields from different tables and store them into rs
Set rs = CurrentDb.OpenRecordset("QueryMemoOutFrm")
' Its not working during the filtering, keeps returning nothing found
rs.Filter = "Doctype='Outgoing' AND DocumentRef='" & cmNum & "'"
Set rsFiltered = rs.OpenRecordset
Do While Not rsFiltered.EOF
' Do Something
Loop
rs.Close
Set rs = Nothing
rsFiltered.Close
Set rsFiltered = Nothing
End Function
I have read the documentation on MSDN, and does not see where did i go wrong. (Maybe i've missed out something)
I have changed the codes a little bit and it works. But not sure why though.
Private Function getCheckedRecordsFromDB(ByVal cmNum As String) As Boolean
Dim rs As Recordset
Dim rsFiltered As Recordset
Dim dSerial As Double
'Gets different fields from different tables and store them into rs
Set rs = CurrentDb.OpenRecordset("QueryMemoOutFrm")
rs.Filter = "Doctype='Outgoing' AND DocumentRef='" & cmNum & "'"
Set rsFiltered = rs.OpenRecordset
' newly added
rsFiltered.MoveFirst
Do While Not rsFiltered.EOF
dSerial = rsFiltered!SerialNo
rsFiltered.MoveNext
Loop
rs.Close
Set rs = Nothing
rsFiltered.Close
Set rsFiltered = Nothing
End Function
It's much easier to troubleshoot if your provide your actual code via copy/paste.
That being said, I'm just wondering why you're using two recordsets?
Do you get the proper answer if you actually do your loop on the real filtered recordset?
Eliminate the line
Set rsFiltered = rs.OpenRecordset
Use this block of code on rs instead of rsFiltered
Do While Not rs.EOF
' Do Something
Loop

Returning a fields object - Access 2007 VBA

I've created the following code which I want to use in the future to get a list of all the fields in a table:
Private Sub btnGetFields_Click()
Dim myDBS As Database
Dim fldLoop As Fields
Dim fld As Field
Dim relLoop As Relation
Dim tdfloop As TableDef
Set myDBS = CurrentDb
With myDBS
' Display the attributes of a TableDef object's
' fields.
Debug.Print "Attributes of fields in " & _
.TableDefs("ALT_IDENTIFIER").Name & " table:"
'Error occurs in line below
Set fldLoop = .TableDefs("ALT_IDENTIFIER").Fields
For Each fld In fldLoop
Debug.Print " " & fld.Name & " = " & _
fld.Attributes
Next fld
.Close
End With
End Sub
But I'm getting a Type Mistmatch - Runtime Error 13 back when I run the code.
Why? fldloop is a Fields object - i.e. a collection of field objects right? which is what the TableDefs.Fields procedure returns so why am I getting this error?
Thanks
I was having same issue and i resolved it by changing "Field" to "DAO.Field":
Dim fld As DAO.Field
Maybe it helps another one.
Best regards
Sometimes passing values to their literal types in Access causes these kinds of errors, not sure why, a quick fix is usually to dimension your variable as an open data type instead e.g:
Dim fldloop as object
Otherwise you could re-write this line:
For Each fld In fldLoop
to
For Each fld In .TableDefs("ALT_IDENTIFIER").Fields
and forget dimensioning a separate variable all together
UPDATE:
Perhaps this would be more useful for SQL Server, if you only have access via MS Access then you should be able to use this example by looping through your linked tables and dynamically re-building a a Pass Through Query
What is the equivalent of 'describe table' in SQL Server?
Found the problem: the reason I was getting the error was because I wasn't referring to the exact field. Though I'm still unsure as to why an error was thrown on a Fields object that was assigned a Fields value.
Here's the code:
Dim f As Field
Dim fldTableDef As Field
Dim Rst As DAO.Recordset
Dim numField As Integer
Dim linkedTable As String
linkedTable = "ALT_IDENTIFIER"
Set Rst = CurrentDb.OpenRecordset(linkedTable)
numField = Rst.Fields.Count
'Loop through
Dim index As Integer
For index = 0 To numField - 1
If Rst.Fields(index).Type = dbDate Then
Debug.Print "Field: " & Rst.Fields(index).Name; " = Date/Time" & Rst.Fields(index).Value
End If
Next

Access VBA: Query returns no rows

I've written some VBA:
For x = LBound(resProdID) To UBound(resProdID)
CurrentDb.QueryDefs("qry_findID_vba").SQL = "" & _
"SELECT tbl_products.ProdID " & _
"FROM tbl_products " & _
"WHERE (tbl_products.Size = " & resSize(x) & " " & _
"AND tbl_products.SupplID = '" & Forms.frm_suppliers.SupplID & "')"
Dim dbs As DAO.Database
Dim rst As DAO.Recordset
Set dbs = CurrentDb()
Set rst = dbs.OpenRecordset("qry_findID_vba")
MsgBox rst.RecordCount
If rst.RecordCount <> 0 Then
rst.MoveLast
rst.MoveFirst
newProdID(x) = rst.Fields(0).Value
MsgBox "This never fires"
End If
rst.Close
Set rst = Nothing
dbs.Close
Set dbs = Nothing
Next x
What happens when I run it, is that a box pops up saying 0. I click Ok, and it repeats one more time. This is because I have two items in my resProdID-array.
However, if I open the query "qry_findID_vba" normally, it shows one row, like I expected.
Why doesn't VBA return this row? Have I done anything wrong?
Does this code messagebox the correct count? Can you use it instead?
(Note, I haven't actually run it, so watch out for slight syntax errors.)
For x = LBound(resProdID) To UBound(resProdID)
Dim sql as String
Dim rst As DAO.Recordset
sql = "Select tbl_products.ProdID FROM tbl_products " & _
"WHERE (tbl_products.Size = " & resSize(x) & " " & _
"AND tbl_products.SupplID = '" & Forms.frm_suppliers.SupplID & "')"
Set rst = dbs.OpenRecordset(sql)
if not rst.eof then
MsgBox rst.fields("ProdID")
else
Msgbox "None found!"
end if
rst.Close
Set rst = Nothing
Next x
Also, try copying everything to a new form, and compacting and repairing the database ...
First off, you really should use QueryDef parameters. They provide a number of benefits:
A safety net against malformed input and SQL injection.
You don't need to redefine the query SQL text every time a parameter value changes.
They make your VBA independent of the query text. This is a simple query, but more complex ones benefit if you don't have to change your VBA code just to change the SQL.
They provide type safety - you can use typed variables in VBA and be sure that the query cannot fail with data type errors.
They can be re-used - parameterized queries can be bound to forms, or executed directly, for example.
Last but not least, it looks much nicer and clearer when used in VBA.
Your situation is exactly what parameterized QueryDefs are for.
Change the query text of qry_findID_vba in Access to:
PARAMETERS [ProductSize] Text (255), [SupplID] Number;
SELECT ProdID
FROM tbl_products
WHERE [tbl_products].[Size] = [ProductSize] AND [tbl_products].[SupplID] = [SupplID];
Replace the parameter data types according to your actual data types in the table.
Next, when you're in a loop, don't re-define fixed variables again and again. dbs and rst don't need to be defined inside the loop at all.
Last point, the RecordCount property does not work the way you think. Quote from the docs, emphasis mine:
Use the RecordCount property to find out how many records in a
Recordset or TableDef object have been accessed. The RecordCount
property doesn't indicate how many records are contained in a
dynaset–, snapshot–, or forward–only–type Recordset object until all
records have been accessed.
[...]
To force the last record to be accessed, use the MoveLast method on the Recordset object.
Instead of calling MoveLast, you can also check the .EOF property. If it is false, at least one record is available.
For one-off query results like this one, I would recommend using the snapshot type Recordset. You can define which type you want to use when you call OpenRecordset on the QueryDef.
Now:
Dim qry_findID_vba As DAO.QueryDef
Set qry_findID_vba = CurrentDb().QueryDefs("qry_findID_vba")
qry_findID_vba.Parameters("SupplID") = Forms.frm_suppliers.SupplID
For x = LBound(resProdID) To UBound(resProdID)
qry_findID_vba.Parameters("ProductSize") = resSize(x)
With qry_findID_vba.OpenRecordset(dbOpenSnapshot)
If Not .EOF Then
newProdID(x) = .Fields("ProdID").Value
End If
End With
Next x
Note that I use With to save maintaining a helper rst variable.