I have noticed that a lot of code I have found on the internet will have variables defined and set that I would simply avoid...such as:
Dim db as Database
Dim rs as Recordset
Set db = CurrentDB
Set rs = db.OpenRecordset
I would write this as:
Dim rs as Recordset
Set rs = CurrentDB.OpenRecordset
I save 2 lines of code at the top plus one to Set db = Nothing at the end. Which, over hundreds of subs and functions can really add up...
But is one more preferred over another by most coders? Is there an actual reason to use one or the other? Is there an advantage to actually defining and spelling out the whole thing?? Is there any real savings in doing it my way, other than staving off carpel tunnel for a few more minutes?
In terms of execution, there is no real difference between the two VBA methods. However, the first is a more generic version as the db object can be set to either the local DAO database or an external DAO database using the Workspace.OpenDatabase method. The second is a more shortcut version as usually needed recordsets, querydefs, etc. derive from the working, local database. Below are examples of referencing external databases.
Short cut version:
Set db = OpenDatabase ("C:\Path\ToExternalDB\someotherdb.accdb")
Full generic version:
Set db = DBEngine.Workspaces(0).OpenDatabase("C:\Path\ToExternalDB\someotherdb.accdb")
Therefore, should a developer change database environments (i.e., transition from local to external database source) and even workspaces, they can easily change the set db = ... line instead of adding this line to the code if developer had originally used only the CurrentDb namespace.
This is a good example of the trade-off decision between efficiency (less scripted lines) and scalability (translatable lines).
"The CurrentDb method creates another instance of the current database... The CurrentDb method enables you to create more than one variable of type Database that refers to the current database" from https://msdn.microsoft.com/en-us/library/office/aa221178%28v=office.11%29.aspx?f=255&MSPPError=-2147217396.
So if you use CurrentDB just once in your code than its fine not to declare it, however using it multiple times it won't be the same instance but always created a new one which may create strange bugs for you.
Related
I've inherited an Access Database that has a lot of tables, forms, and queries in it. However, I'm a PHP programmer and VBA is pretty foreign to me. I was asked to make some changes, which over the course of a few days I was able to get working (with a lot of help from old random SO posts).
After passing the database back to the users, the code that works on my computer is not working on theirs. It seems I have Access 2010 and they have 2007. As best I can tell, the function DoCmd.SetParameter doesn't exist in VBA in Access 2007.
Here's a snippet of the code :
DoCmd.SetParameter "ReportYear", Year.Value
DoCmd.SetParameter "ReportMonth", Month.Value
DoCmd.OpenQuery "doFillData"
doFillData is a query inside of Access that inserts in to another table automatically, requiring 2 parameters (year and month) before running.
The obvious answer is, make them upgrade to 2010, but I don't have that power. I assume I'll be able to create conditional code to do something different in 2007, but I can't find a similar function to use. Anyone have any ideas?
Instead of using DoCmd.OpenQuery, you want to manipulate the querydef object's named parameters and then execute it. You can use Execute options like acFailOnError when executing this way (not available with OpenQuery) and you can detect the number of records affected.
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Set db = CurrentDb
Set qdf = db.QueryDefs("doFillData")
qdf.Parameter("ReportYear") = Year.Value
qdf.Parameter("ReportMonth") = Month.Value
qdf.Execute
MsgBox qdf.RecordsAffected & " records were affected."
So I have a query where I select a field and set the criteria so that it only selects records based on the current value of a particular field in my form. The criteria looks like this.
[Forms]![FORMAL_CERT_REVIEW_CHECK_FORM]![REVIEW_CHECK_ID]
Pretty simple stuff. But I am running into the issue where when I run the query I get the prompt that says I need to enter a value. I know that this usually happens when you set the criteria to something that may not exist or you spelt it incorrectly, but I have checked all of this and it seems like everything looks fine.
I was curious if there is something I could be missing, like a property on the field or something that I have not thought of.
When you directly open a query which includes a reference to a form control, Access is able to retrieve the query's parameter value from that control on the open form.
However, if you attempt to use the same query as the source for a recordset, Access does not resolve the query parameter from the open form.
For example, this is my query, qryREVIEW_CHECK_ID.
SELECT f.id, f.datetime_field, f.some_text
FROM tblFoo AS f
WHERE f.id=[Forms]![FORMAL_CERT_REVIEW_CHECK_FORM]![REVIEW_CHECK_ID];
With FORMAL_CERT_REVIEW_CHECK_FORM open, everything works fine when I open the form directly ... like this for example ...
DoCmd.OpenQuery "qryREVIEW_CHECK_ID"
However, using that query as the source for a recordset triggers error 3061, "Too few parameters. Expected 1."
Dim db As DAO.database
Dim rs As DAO.Recordset
Set db = CurrentDb
Set rs = db.OpenRecordset("qryREVIEW_CHECK_ID")
The workaround is to open the recordset from the QueryDef object. And include Eval() so that Access will resolve the parameter using the parameter's name.
Dim qdf As DAO.QueryDef
Set qdf = db.QueryDefs("qryREVIEW_CHECK_ID")
qdf.Parameters(0) = Eval(qdf.Parameters(0).Name)
Set rs = qdf.OpenRecordset
Your description doesn't specify whether your form is a simple form or a sub-form. I came across the same issue and realized that I was only entering the sub-form's name in the criteria.
Assuming that you have FORMAL_CERT_REVIEW_CHECK_FORM sub-form under PARENT_FORM, your criteria should read
[Forms]![PARENT_FORM]![FORMAL_CERT_REVIEW_CHECK_FORM]![REVIEW_CHECK_ID]
I hope this helps you or others. Used in Access 2016.
I have experienced this on several occasions after making a design change to the form. There is no fault with what you have done - it is an access corruption. The solution is to copy the database to another file name, delete your forma and query, compact and repair, and then import the form and query again. This normally solves the problem. It appears importing it resets the internal references allowing the form to work as it should.
Doesn't seem like this was ever solved, and I just came up with the same issue. I solved it by deleting the form and recreating it. Like you I had, [Forms]![MyForm]![ID] and out of no where it started asking for user input for the criteria on my listbox query. Making a new form and copying over the fields seems to have fixed it.
I have some task regarding do operations on database using ADODB in Vb.NET. Can any help me regarding using the same record set multiple times?
I have tried on this topic just by closing the recordset after performing First operation and doing second operation e.t.c.
Example:
Dim rs As New ADODB.Recordset()
OpenConnection()
rs.Open("table1")
//Some Operations
//Now using same recordSet for Second Operation
rs.Close()
rs.Open("table2")
In ADO.NET we have "MultipleActiveResultSets=True" in ConnectionString for SqlDataReader.Do we have any property like this in ADODB?
The more similar thing that exists in ADODB is sending multiple queries in your Sql String then proccessing them one by one using rs.NextRecordset (rs is the Recordset), here is an example: ADO NextRecordset Method Demonstration
But, personally, i prefer doing it as you're doing it now, using just one recordset for each query. Take into account that using multiple recordsets inside one, like in the previous example may require some additional commands in some dbs to ensure no additional unwanted recordsets are created from messages coming from the backend, for example for Sql Server it's a good idea using:
Set NoCount On and Set ANSI_Warnings OFF
I am at the moment trying to improve the performance in my ASP Classic application, and I am at the point of improving SQL transactions.
I have been reading a little from http://www.somacon.com/p236.php
So, my question is:
How does the line set rs = conn.execute(sql) actually work?
I believe that the line defines a variable named rs and binding ALL the data collected from the database through the SQL sentence (fx select * from users).
Then afterwards I can actually throw the database-connection to hell and redefine my sql variable if I please, is this true?
If that is true, will I then get the best performance by executing my code like this:
set conn = server.createobject("adodb.connection")
dsn = "Provider = sqloledb; Data Source = XXX; Initial Catalog = XXX; User Id = XXX; Password = XXX"
conn.open dsn
sql = "select id, name from users"
set rs = conn.execute(sql)
conn.close
-- Do whatever i want with the variable rs
conn.open dsn
sql = "select id from logins"
set rs = conn.execute(sql)
conn.close
-- Do whatever i want with the variable rs
conn.open dsn
sql = "select id, headline from articles"
set rs = conn.execute(sql)
conn.close
-- Do whatever i want with the variable rs
set conn = nothing
In this example i open and close the connection each time i do a sql transaction.
Is this a good idea?
Is this a good idea?
No but not for the reasons indicated by Luke. The reality is that ADODB will cache connections anyway so opening and closing connections isn't all that expensive after all. However the question proceeds from the mis-information you appear to have over the behaviour of a recordset...
From you comment on Lukes answer:-
But it is correct, that it stores all the data in the variable when executed?
Not unless you have carefully configured the recordset return to be a static client-side cursor and even then you would have to ensure that the cursor is completely filled. Only then could you disconnect the recordset from the connection and yet continue to use the data in the recordset.
By default a SQL server connection will deliver a simple "fire-hose" rowset (this isn't even really a cursor) the data is delivered raw from the query, only a small amount of buffering occurs of incoming records and you can't navigate backwards.
The most efficient way to minimise the amount of time you need the connection is to use the ADODB Recordset's GetRows method. This will suck all the rows into a 2-dimensional array of variants. Having got this array you can dispense with the recordset and connection.
Much is still made of minimising the number of connections maintained on a server but in reality on modern hardware that is not a real issue of the majority of apps. The real problem is the amount of time an on going query is maintaining locks in the DB. By consuming and closing a recordset quickly you minimise the time locks are held.
A word of caution though. The tradeoff is an increased demand for memory on the web server. You need to be careful you aren't just shifting one bottleneck to another. That said there are plenty of things you can do about that. Use a 64Bit O/S and stuff plenty of memory in it or scale out the web servers into a farm.
Nope, opening and closing connections is costly. Open it, reuse the recordset like you are, then close it.
Ok this is vexing me. I have a query I created with an in statement in the from clause. What I am looking to do is have a global variable populate that from statement. Example
Select *
Form query1 in <Global Variable filename>
What is going on is I link to a file that has hundreds of linked table and queries in it. My database only has a few select queries and link table to different database. I did not want to re-build all the queries and linked table is my database. The issue is the file with all the links changes name once a month. I just want to read a text file with the current name of the database in it so I do not have to keep changing my queries every time the database name changes. Also this has to a query since I have other queries using the externally linked query.
I have one suggestion, but its pretty kludgy.
Rewrite the query on the fly with VBA call
Private Sub update_qtest()
Dim db As Database
Dim qd As QueryDef
Set db = CurrentDb
Set qd = db.QueryDefs("qtest")
qd.SQL = "SELECT * from query1 in " & g_file_name
End Sub
As I said, it's kludgy, but I don't think there's a way to pass the from clause as a parameter.
Another way to do this would be to just use the same file name each month so you wouldn't have to change anything in your Access app at all. You could easily code copying the file to the standard name over top of the previous copy (you'd have to delete it before copying, of course), which would retain the history.