I have a form which will generate a report for mailing.
The form opens a recordset with all the fields needed. I'm wandering how can i pass this recordset to the report, so that i don't need to open the same recordset once again.
Similarly, sometimes i also want to pass recordset between forms (no main/sub form relation), how can i do this?
Another little question, when i open a form in datasheet view, it's always very big. How can i limit its size when opening?
Thanks!
EDIT:
To be clearer, say i have "FORM", when the user hit a button on it, "Report" will be open. I want "Report" to use the recordset that is already created(opened) in "FORM".
Also on "FORM", there is a textbox, filled by users, i want also to show it on the "Report".
EDIT2:
I tired but cannot passe the recordset, nor populating a field in my report from a textbox on my form, very annoying ..
You should be able to accomplish this by simply passing the correct SQL statement to your report. This can be done using the Opening Arguments.
Assuming that you really do want the identical set of records on your report as you see on your form, what you need to do depends on how you have opened your form, or how you are filtering your form. You could indeed use an identical DAO Recordset object and set your reports Recordset object to a copy or clone of the Form's Recordset object. However, this might not be necessary to get the results your look for.
Solution #1
If your form uses a query or SQL statement you can use this solution.
Code on your form:
DoCmd.OpenReport "rptReportName", acViewPreview, , , acWindowNormal, Me.RecordSource
Code on your report:
Private Sub Report_Open(Cancel As Integer)
Me.RecordSource = Nz(Me.OpenArgs, "")
End Sub
Solution #2
Use this solution if your form is using the form's filter property to filter down to the correct set of records. I'm assuming you then want to pass that filter condition on to the report. You'll need to configure the report so that it uses the same RecordSource as your Form (or it must at least contain the table/fields that will be included in your filter statement). The problem is that passing the recordsource of your report to your form doesn't pass any filter that you might have set on the form.
DoCmd.OpenReport "rptReportName", acViewPreview, , Nz(Me.Filter, ""), acWindowNormal
As a final note, it is not possible to set a Report's recordset property. You can assign a Recordsource as I've already shown (a recordsource is a tablename, a queryname, or an SQL statement) but you cannot use the Recordset property unless the database is an Access Data Project, which I don't recommend using at all.
Edit1
It wasn't clear from the original post what problem the OP was trying to solve. I incorrectly assumed he was having trouble getting the same records to show on his report as what he has on his form. It appears that rather the OP is concerned about making two trips to the server to retrieve records.
Because you cannot set the Recordset value on an Access report, your best option might be to create a local Access table and simply use it as a temp table. I don't know what size your recordset typically is. If it's quite large (5000+ records) this solution may not be a good idea. One problem I can think of is that it will cause your front-end database application file to bloat over time unless you have the file setup to run Compact and Repair on close.
I think the worry about two trips to the server is unwarranted.
If you were using a Jet/ACE back end, Jet would cache the data locally, and there wouldn't be a re-retrieval unless the data had changed (in which case I think you'd probably want the up-to-date data, no?).
With a server database, the server itself is likely to cache the results, particularly if the SQL statement used is identical in both cases.
This looks to me like a case of premature optimization.
Well If I have understand from your question you need to manipulate the data in a table by knowing/unknowing the record set number.
If that is then you need to reform the mentality of how to access data in a table, because to promote the record set number in your list is not the quite right way, usually we promote the data and the record numbers are hidden.
So when you read your table try to pass your fields into variables for later use or pass them directly to your list view.
The way of accessing the table to get each data it comes from another process which always varied.
But even when you want to keep the record numbers for later use try to declare a name as public variable as ArrayList() then when you read from table you may use the 'Variable'.add(RecordNumber) .
So when you need to access a particular number take the reading line number from your list view by calling VariableName(ListViewLineNumber)
Please inform me if this solution comes closely to you issue solution.
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 11 months ago.
Improve this question
My question: Why would a SQL string function properly as a query but not as the RecordSource of a report?
I apologize for the lengthy background, but I think the context in which this issue is arising is probably helpful. In Access 2019, I have a MainQuery that is based upon multiple other queries, some of which are parameter queries. So, MainQuery itself does not contain any parameters as part of its definition, but because its "constituent queries" contain parameters for which the user is prompted, running MainQuery prompts the user for said parameter values. Note that I call them "constituent queries" because they are all standalone query objects in my database as opposed to true subqueries within MainQuery.
MainQuery is the RecordSource of MainReport. Of course, upon opening MainReport, the user is prompted for the parameters that allow MainQuery (i.e. all its constituent queries) to run. Naturally, the user must reopen or refresh MainReport and supply different parameter values each time to see a different set of data inside MainReport.
This arrangement has worked well for years and continues to work, but I recently decided I wanted to increase the functionality of the database by allowing the user to print out multiple MainReport reports (for different records) in rapid succession.
The strategy I used was to consolidate MainQuery and all of its constituent queries into one big, complex MasterQuery made up of legitimate subqueries defined inside of it. The parameters for which the user used to be prompted each time the MainQuery was run were replaced with a simple string place holder, and the entire body of the MasterQuery is saved in a SQL_Table so that my VBA code can access it. Now, the user can select the records for which he or she wishes to print on a form designed for this purpose. Then, upon clicking a button, my VBA code gathers the user's choices, retrieves the template SQL string from the SQL_Table, and for each record that the user wants to print, replaces the string place holders in the SQL string (previously known as the parameters) and assigns the RecordSource of MainReport to that SQL string.
As I was working on this project for the last week or so, I was able to get this process to work very beautifully and successfully. But, as I continued developing it, I ended up making several additions and alterations to the MainReport and MasterQuery to improve the functionality of the database.
So, the current state of affairs is that I am successfully passing the SQL string to the RecordSource property of the report, but the problem is that Access tells me there is an error in the SQL string, and it will not actually run the report. Instead, the report stays in DesignMode and will not go into Report View. The error message says,
Syntax error in query. Incomplete query clause
In attempting to diagnose the problem, I took the exact same SQL string (I literally copied and pasted it from the RecordSource property of report into the SQL View of a blank query), and to my surprise, the query runs beautifully. It returns the result set that I expect to see. As a bonus, I even saved the query as Query1 and set the RecordSource of MainReport to Query1, and even this works!
I don't understand this behavior. I might expect that either they both work with the same SQL string or they both don't work, but I don't understand why they might behave differently; and this is the crux of my question.
So, why would a SQL string function properly as a query but not as the RecordSource of a report?
Thanks in advance for anyone who might have any ideas!
Well, since the query in its raw form will not run, and you fully pointed out that you "replace values in that query?
And since you ALSO point out that you are replacing the reports query source (and you MIGHT WANT to show and explain how you doing that?
So, given that you EACH TIME are going to modify the raw query? Then obvislity if you modify it for one time, then the values in that report (the critera) will have be changed. So, it ALSO not clear how you deal with that issue??
In most cases, becuase the query in its raw format has "values" that you going to replace, then in theory those values need to be "known" so you can repalce them.
Thus, as a general apporach for the above?
I save the raw query in the querybuilder, but then MAKE A COPY of that query, make the critera replacments and then SAVE that query to a 2nd query - a query that the report is based on.
So, say we have a simple query and report.
qryHotelsS (I put a "s" on such querys - means "source"
qryHotels (the final fixed up and changed query).
So, say I have this:
SELECT * FROM tblHotels WHERE City = '#City' ORDER BY HotelName.
So, I now might have some really nice prompt form, or form that allows the person to enter the city criteria, and then hit a view report button.
The code would thus look like this:
Dim strSQL As String
Dim strCity As String ' for test - the value would no doubt come
' from a very nice form wiht a text box, and
' button to launch the report.
strSQL = CurrentDb.QueryDefs("qryHotelsS").SQL
strCity = "Calgary" ' as noted, this would be some text box value on
' a nice report form
strSQL = Replace(strSQL, "#City", strCity)
CurrentDb.QueryDefs("qryHotels").SQL = strSQL
DoCmd.OpenReport "rptHotels", acViewPreview
So, we grab our "source" query, replace the values we want, and then save the query to a query that the report is based on, and then simple launch that report.
I mean, it really comes down to how your replacing and setting up the query now? The REALLY nice feature of above? You can run the code (comment out the open report command above). Once you run the above code, then you are rather free to open up the report and do further design and tweaking of the report, and since it has a valid query source, then you are free to design, change and setup the report any way you want. (since it has a valid sql query source).
so, adopt the idea of a having two queries for the report. The "source" query - with some values that you replace in VBA code. I just out of the blue used '#City', so I use # signs, and you can roll your own quite much anyway you want. You could even leave in the [] and have your VBA replace the [], but I like using # signs for this. And it works well even for dates.
eg:
WHERE InvoiceDate = ##InvoiceDate#
So, in code, you can just replace #InvoiceDate with a date string, but the delimiters (#) don't need to be added.
Edit: Why some query can't work in code?
Well, if you can take that string, shove it (save it) into a query, and then run the report on that query, then this can work.
However, there is a SIGNFICANT difference to use the DAO object model, and shoving sql into a reocrdset vs that of basing a form, or query on the same source.
Remember, the DAO recordset object does NOT allow and does NOT know about forms! Expressions etc., and they can't be used.
And if you introduce ANY VBA function into the sql? Once again, you cannot use the DAO querydef object.
The pure data engine (DAO) can be used from vb6, vb.net, c# etc. The data engine thus does not allow for example forms! expressions, and it ALSO does not allow use of custom VBA functions in that sql.
So, basing a reprot on a saved query def? No problem, both forms! expresisons, and even VBA functions can be contained in the sql query.
However, if you attempt to use the SAME query, and shove it into a DAO recordset? Then the so called "expression" service. (the system that resolves forms! expresions, and resolves VBA function calls) cannot be used.
That is due to the fact that the data base engine (JET/ACE) does not know about VBA, does not know aobut forms!, and it is a stand alone data engine, and a data engine NOT limited to being used with Access, but as noted can be used from FoxPro, vb.net, c#, vb6 etc. So, a pure DAO data engine call does not have nor allow forms! expressions, and as noted does not even allow you to have VBA expressions. It has to be 100% valid JET ONLY sql syntax. So, while some functions exist in JET sql, a good number of VBA ones are NOT allowed.
The JET database engine thus does NOT even know nor even assume that a forms! object exists, can be open, or closed, and the same goes for VBA functions used in that sql - in fact they can't be used.
So, if you put such expresisons in a query, and use from MS-ACCESS? Then the query can be run from the UI, can be used as a data source to a report, but in pure VBA code, setting the same query to a DAO recordset object will fail.
So, it probably better to either:
Figure out if you have any VBA functions used in the sql
Figure out if you have any forms! expressions used in the sql
And then remove the above.
However, you might have a bit of a difficult time working through that messy sql, so you can then just shove that query string into a querydef ojbect (save it into the .SQL property as I did above), and then simple base the report on that saved query as opposed to trying to create a DAO recordset object in code, and using that for the forms reocrdsource, which as I point out above can't use forms! or VBA functions - and has to be pure valid JET/ACE syntax without such expressions.
For a client I have to use MS Access 2007 as a lightweight front-end to SQL Server 2008.
One of the requirements, security-wise, is that I need to fetch my recordset data via stored procedures. I'm doing this via SQLOLEDB, based on tutorials provided here: http://accessexperts.com/blog/2011/07/29/sql-server-stored-procedure-guide-for-microsoft-access-part-1/
I have a mainform with some general data, on which I also set the form recordset (Me.recordset) in the code-behind. At the bottom of the form, there is a subform, with a table of rows of data. This is also an ADO recordset being set in the code. In order to make those subform records editable, I made a extra recordset in which I cloned the data in, then I bound it to the subform's recordset (Me.recordset -> but in context subform). I added a save button after each row in the subform, and via an event I'm doing the writes manually via another stored procedure.
This all works, but I get some very weird behavior when I edit some data in the first row (for example), and then click on a field in another row (for example the second row). Now suddenly all my fields change to '#Name?'; basically my subform gets unbound.
Now I'm not sure how to debug this, let stand fix it. Any help on this error is greatly appreciated.
I forget to put tempRs.ActiveConnection = Nothing
It is working as intended now :)
I have an Access 2000 database that sits on a peer to peer network. (All computers are connected via ethernet 100 to a simple 4 port switch)
The database is designed with a back-end and a front-end.
There are 2 users that connect to the back-end via the network. One user connects to the back-end directly as the back-end sits on her computer.
All the computers are fairly low spec, with either 500 meg or 1 gig of ram
I have one form, frmInvoice, which is based on the table "tblInvoice" There are about 60,000 records in the table. There is also the table "tblInvoiceProducts" which is linked to "tblInvoice". There are about 150,000 records in the table "tblInvoiceProducts".
When I open form "frmInvoice" on the computer which hosts the back-end, the form opens very quickly. However if I open the form on one of the other computers it is very slow to open. However, once opened, record navigation is fast.
I think the problem is that when the form is opened, Access opens the entire table and all the records are accessible. Am I correct in this assumption?
I need to rectify this problem and welcome all suggestions.
I am going to install a D-Link DNS-323 2-Bay Network Storage Enclosure and store the back-end database on this as this will improve security / data protection.
Thanks in advance
"I think the problem is that when the form is opened, Access opens the entire table and all the records are accessible. Am I correct in this assumption?"
My bet is you are absolutely correct.
If you open the property sheet for the form, choose the Data tab, and look at the value for Record Source, do you see the name of one of your tables?
Or a query without a WHERE clause, perhaps like "SELECT * FROM tblInvoice;"?
If your answer is yes to either of those questions, Access will have to pull every single record from the table across the wire.
So, don't do that! :-) The key to decent performance is to limit the form's Record Source to a reasonable subset of rows.
Choose some criterion which makes sense in your situation, perhaps invoice date, and build a query which incorporates that criterion into its WHERE clause. Base that criterion on an indexed field --- add an index if your table doesn't already have one on that field. You can experiment by creating a new query in the query designer ... maybe the SQL View would look like this:
SELECT Invoice_ID, Customer_ID, invoice_date
FROM tblInvoice
WHERE invoice_date = Date();
That would retrieve only rows where invoice_date matches today's date.
(If you've already loaded the form, pulling down all the rows into local cache, you won't get a true indication of the speed of that query. It would be better to test the query from a new Access session without first loading the form.)
So then give the user a method to select a different invoice_date. For example, a text box control named txtSelectDate. And in the After Update event of that control, you can write an updated SELECT statement which you apply as the form's Record Source.
Private Sub txtSelectDate_AfterUpdate()
Dim strSql As String
If Len(Me.txtSelectDate & "") > 0 Then
strSql = "SELECT Invoice_ID, Customer_ID, invoice_date" & vbCrLf & _
"FROM tblInvoice" & vbCrLf & _
"WHERE invoice_date = " & Format(Me.txtSelectDate, "\#yyyy-m-d\#")
Debug.Print strSql
Me.RecordSource = strSql
End If
End Sub
The Debug.Print line will print the SELECT statement in the Immediate Window so if anything goes wrong you can view the completed statement, and copy & paste it into SQL View of a new query for testing. Changing the Record Source property automatically causes Access to requery the data source. And you can validate the user's entry in the text box's Before Update event to make sure it's a valid date.
Another possibility is to have the form first load with a single non-editable dummy record(1). Then display records only after the user chooses an invoice_date. To do that, you could use something like this as the form's Record Source:
SELECT TOP 1 Null AS Invoice_ID, Null AS Customer_ID, Null AS invoice_date
FROM tblInvoice;
In addition to the excellent suggestions made by HansUp, I would add the following:
Detailed forms should never take more than a couple of seconds to load. If they do, you're either doing something wrong or you need to optimize your binding/code.
Datasheet and reports can take longer depending on their complexity and how often people need them. Basically, if it's something they need a lot, it need to be fast. On the other hand, if it's a monthly report, waiting 30s is acceptable.
You don't say if your front end is split from the back-end or if you're using the same .mdb for your data and UI. If you do, then stop and split your database, even for the person on the same computer as the data.
Your back-end mdb file should only contain your data tables, and nothing else. The front-end mdb must be installed on the user's individual machines.
Make sure that your tables have the right indexes for your filter and sorting criteria. If you're binding your form to your tblInvoice and set the property to order by invoice reference and your table doesn't have an index for this, Jet will need to load all invoice references from the table to check their order.
If all your indexes are OK, Access will not load all the data to display a single record in a detailed form. It will only load lots of data if you're using a datasheet or a continuous form.
So the issue is probably with something else: maybe you're binding to a complex query from within your form, like a combo box or a datasheet, that it's that particular thing that's dragging your down.
Try to bind/unbind controls one by one to see which one has the biggest impact, then concentrate on optimizing that particular area.
You can also bind the controls as needed from code. For instance you can keep your form or the expensive controls unbound and wait for the user to select the record they need before you bind it to the form/control.
If you have lots of information on the form that loads related data from other queries and tables, you could re-design the form to group data in a Tab Control, then have a sub-form in each tab-control to display the data and load this only the user selects the tab.
Make sure that if you display lots of data in continuous forms/datasheets, that they are bound as snapshot to make them read-only. If they are read/write and someone else is trying to load a detailed form, Access has to do extensive lock check to make sure that the data is not being edited in multiple places. The more you make your data read-only, the better.
A final tip for for better performance: create a dummy table with a DUMMY field in your backend, link to it from the front-end. Bind this table to a form that has a single control for the DUMMY field. Make this form invisible and open it automatically when you start your application.
This form will keep the dummy table open and will ensure that your front-end doesn't have to open/close the connection to the backend every time it needs to query it.
Locking the database is time-expensive, especially over the network. If this is done very often, it can really decrease performance.
Maybe it won't change anything for your particular case, but I've found it to be good practice as the impact can be tremendous.
I have found the reasons for forms being very slow to load.
They include:
1. Having active filters on the form
2. Having combo boxes which allow users to navigate / select records
If I remove these I don't experience and speed issues.
To allow users to navigate / select records, I have a cmd button on the form. When clicked it opens a pop up form allowing the user to select the record of choice. I store the record id as a variable, close the pop up form and filter the recordset of the form e.g
select * from tblInvoice where InvoiceId = tmpInvoiceId
works for me!
yes Access (Jet) sucks bad across a network.. to make things more complex, there aren't real 'indexing tools' in Access like there are in SQL Server..
If I were you, I'd go get ONE solid PC, install SQL Server 2008 R2 on it, and upgrade to Access Data Projects.
this has me pretty confused and I can't find the answer anywhere else so thought I'd post here to see if anyone can help!
I have a form in an Access 2007 database with a subform (sfSubform) embedded in it. The subform control's SourceObject is set to be another form (fForm). fForm's RecordSource starts out as a table.
At one point I want to change the data displayed in the subform to the result of a SQL statement, so I use
sfSubform.Form.RecordSource = strSQL.
This works fine. However, if I ouput the name of the RecordSource for fForm after making this change, it still gives the name of the table that I orginially set.
Does sfSubform.Form.RecordSource not change the source of fForm? Is it a copy of fForm that is embedded in the control?
Hope all that makes sense.
The sub-form and the form each have their own record source (or are unbound). That's the whole point, actually -- the ability to present two different data sets. Typically the two forms have related record sources and this relationship is declared using Master/Child Link, but that also is optional according to the need.
So no, changing one won't cause the other to be changed.
i am new to access and i am debugging someone's database
how do i know if i am using ADO?
i have stuff in a listbox, how do i use the .GetRows method to put the recordset into a two-dimensional array?
Do you have a reference to the ADO libraries (In a code window, under Tools->References: Microsoft ADO Ext x.x For DLL and Security, Microsoft ActiveX Data Objects x.x Library) Have you set the recordset to an ADO recordset?
You will find details of GetRows here: http://www.w3schools.com/ado/met_rs_getrows.asp
You can use it like GetString, as illustrated in a reply to your previous post.
EDIT
You do not have to have a reference to the ADO libraries to use ADO, but it can be easier. To use ADO without the libraries (late binding), try something like:
Dim cn As Object
Dim rs As Object
Set cn=CreateObject("ADODB.Connection")
Set rs=CreateObject("ADODB.Recordset")
cn.Open strConnect ''Any suitable connection string
rs.Open strSQL, cn ''Any suitable SQL string
As others mentioned, to check if the using ADO, you as a general rate ruled can look in your code. However what's probably confusing you and not has been explained is that it's ONLY in your coding practices do you choose ADO or DAO.
For the rest of the application (ie: BUILT IN OBJECTS) you will use the concept of what's called linked tables. These linked tables handle all of the connection strings and all of the internal workings for you. There's no additional effort required on the user's part when they're developing the application. For all of these internal objects, you don't choose a particular data object type (ADO or DAO).
What this means is that for filling data into forms, data into reports, data into listbox, data into combox boxes etc. you DO NOT write code, and YOU DO NOT use connection strings. And you DO NOT HAVE a choice of ADO or DAO. What this means is that when you look at a combo box or list box or form, you'll get off the confused really fast if you start looking for some type of connection string or asking if that combo box is based on ADO or DAO (hint: you as a genera rule don't have a choice in this matter: all of these object and how they work are handled automatically for you internally inside of a MS access). What this means is all you have to do supply the given object with some SQL, and you're done.
So, all of these objects will as a rule work from your linked tables in access. Once these linked tables are set up, then you build forms, or reports, or list box is based on these linked tables. As a result a as mentioned, you'll not be choosing ( or even have to bother) with DAO or ADO when working with these objects.
The choice of ADO or DAO We'll generally only come into play in your actual code that opens a record set or query directly. Most of the code in a form can be based on referencing the controls or objects such as an combo box that exist on that form. Once again the choice of ADO or DAO it not relevant, and you don't have control over that aspect.
For example, if you wanted to use code to setup the values for a combo box, you would use the following code: (we assume the combo box is a two column combo box, 1st column is id (hidden) and 2nd column company name.
The VBA code to fill or setup the combo box via sql is:
Me.Mycombobox.RowSource = "select id, CompnayName from tblCustomers"
That is it!
Notice how there's no connection strings, no record sets, you just shoved some clean SQL right into the combo box row source and away you go.
Now of course the above assumes you have a linked table called "tblCustomers". You can see/view those tables in the tables tab.
( By the way our combo boxes and listbox is allow more than two columns, to hide columns you set the format lengthy equal to zero for that column. That way you can still access the data in your code, but not have that column displayed in the list box).
So the choice of ADO/ADO is often not an issue. Note that even when you do choose ADO or DAO in your code, you as a general rule should use the built in table objects and this again will not have to bother with an connection string built in code. There are some exceptions here, but let's keep this general for the time being.
The above should clear up a lot of your questions about how come you can't see or tell how the form is using DAO or ADO ( the answer is you don't bother with this, and the answers you don't have a choice, and the answer is this is all automatically handled internally for you by MS access)
2.i have stuff in a listbox, how do i use the .GetRows method to put the recordset into a two-dimensional array?
For what reason do you want to do the above? There is VERY little reason to pull data from a listbox into a array. You have the list box ALREADY loaded up with data, so why then waste time loading this data into an array? For what reason would you do this?
You can reference the data in that listbox, but you might want explain what you're trying to accomplish here. There's no need to pull the data out of the listbox, but you can use code in a form to look at value(s) selected in the listbox, and do a useful things with those selections made. Again, to work with the selected data from that listbox, you don't need an array, you don't need connection strings, and you don't need to write a whole bunch of code to repeat what is our data currying and is available inside of that form (hint: the data inside of that list box is available for you as a collection).
So you don't need a record set, you don't need to write a whole bunch of code nor do you need some connection strings. And you don't need nor want to pull that data from that list box into an array, it's simply not required and is completely overkill. Simply stay what you're trying to do with that listbox, and we'll come up with a solution that's likely only a couple of lines of code.
As mentioned, look at that data tab of a listbox, and take a look at the SQL used to fill that listbox. Does that sql run correctly, and furthermore check of the number of columns specified in the listbox matches the number of columns you have in your SQL that set for that list box in the data tab. Funny things can happen if the number of columns don't match the settings for the number of columns in the listbox.