please pardon my virgin post and if my question somehow got answered elsewhere, please kindly point me to that link (i did read related dlookup topic but didnt get what i want).
i freq use dlookup (due to newbie in access and lazy too) on forms for my textbox to hold values for my purpose (calculations, display) and knowing dlookup is random and takes time if db isnt local (which i am task to move it online) and time for me to utilize more professional approach to replace dlookup usage in general.
what i used to do is to retrieve data from 1 table (inventory) to populate textbox in a form:
txtItemName=dlookup ("ItemName","Inventory","Inv_ID=" & cboItem)
txtItemDesc=dlookup ("ItemDesc","Inventory","Inv_ID=" & cboItem)
txtItemPrice=dlookup ("ItemPrice","Inventory","Inv_ID=" & cboItem)
and such for data i need and sometimes could be more 10 dlookup(s) to lookup data for my textboxes.
i did read about recordset (DAO/ADO) but due to newbie in ms-access usage, i couldnt figure how to write (or copy) code that could perform what i need like the above dlookup(s).
i did try creating query to pull fields (say 10 fields) i need in 1 query and use dlookup (again) in recordsource to hold the value. (if this method has any advantage compared to 10x original dlookup i did before...)
i am willing to learn the right method to do the right thing in ms-access and seek all seniors to show me the way and kick my bad/newbie habit of abusing dlookup.
i thank you all in advance.
Actually, using dlookup() or using VBA code and a reocrdset?
The performance is 100% the SAME.
I repeat the performance is 100% the SAME.
You gain ZERO benefits by writing some VBA code that creates a recordset, and then creates the sql, and then pulls the one record. It is in fact a waste of time to pursue such a road and concept. It does NOT HELP performance at all, and using VBA code + a reocrdset to pull the one record and value is NOT faster.
They both perform the SAME speed!!!
What can make a difference? If you have dlookup() over and over in the sql, and you execute dlooup() for EACH ROW of the sql. Then that is slow.
So, say we had a combo box, and we have say the "id, HotelName".
So, in a query you might do this:
select FirstName, LastName, dlookup("HotelName","tblHotels","ID = " & Hotel_ID) from bookings
In above, we used dlookup() to get the Hotel name, since I did not want to display the "hotel_id". Above is going to be VERY slow. The faster way is to use a sql join like this:
SELECT FistName, LastName, Hotel_id, tblHotels.HotelName
LEFT JOIN tblHotels on tblBookings.Hotel_id = tblHotels.id
FROM tblBookings.
So, in above, we dumped the dlookup() (slow), to above, which will run very fast.
So, running ONE dlookup() vs VBA + reocrdset to get the ONE value? Same speed, no need to use VBA code to replace the dlookup() function.
But, if you run the function over and over - one time for each row? Then BOTH use of dlookup(), and use of a custom VBA function to do that for each row will BOTH be slow.
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.
I have a small little Access program that sums hours of employees, with queries for Daily, Weekly, Monthly, and from the start of the year, all accessed from an easy to read form.
I have been asked to make an option group to filter employees by fulltime, seasonal, or both. But an issue I have is that I can only access the PAYRULENAME from the table which begins 'FT' for Fulltime such as FTADBRK1 but Seasonal can be a whole mishmash of possibilities, just not beginning with "FT". Still, I did not think this to be an issue.
Since I already have the original queries done, I thought I could simply add an additional column and constraint for each query instead of making additional queries. So I tried some various methods. Such as:
Switch([Forms]![MyForm]![EmpType].Value="FT",Left([PAYRULENAME],2)="FT",[Forms]![MyForm]![EmpType].Value="S",Left([PAYRULENAME],2)<>"FT"[Forms]![MyForm]![EmpType].Value="All",[PAYRULENAME])
Where EmpType is a textbox that programmatically gives a value for the option group instead of 1,2, or 3. This works and I don't think this is where the issue is.
But using the above switch function gives a -1 for Fulltime or a 0 for Seasonal (S) and doesn't filter the query. (The last option is fine - but it would be nice to give an FT or S in a column instead of the entire PAYRULENAME).
I have also tried nested iif statements to no avail. Here is one: IIF([Forms]![MyForm]![EmpType].Value="All",[PAYRULENAME],IIF([Forms]![MyForm]![EmpType].Value="FT",Left([PAYRULENAME],2)="FT",Left([PAYRULENAME],2)<>"FT"))
Any suggestions would be appreciated!
I have job in Talend that is designed to bring together some data from different databases: one is a MySQL database and the other a MSSQL database.
What I want to do is match a selection of loan numbers from the MySQL database (about 82,000 loan numbers) to the corresponding information we have housed in the MSSQL database.
However, the tables in MSSQL to which I am joining the data from MySQL are much larger (~ 2 million rows), are quite wide, and thus cost much more time to query. Ideally I could perform an inner join between the two tables based on the loan number, but since they are in different databases this is not possible. The inner join that is performed inside a tMap occurs after the Lookup input has already returned its data set, which is quite large (especially since this particular MSSQL query will execute a user-defined function for each loan number).
Is there any way to create a global variable out of the output from the MySQL query (namely, the loan numbers selected by the MySQL query) and use that global variable as an IN clause in the MSSQL query?
This should be possible. I'm not working in MySQL but I have something roughly equivalent here that I think you should be able to adapt to your needs.
I've never actually answered a Stackoverflow question and while I was typing this the page started telling me I need at least 10 reputation to post more than 2 pictures/links here and I think I need 4 pics, so I'm just going to write it out in words here and post the whole thing complete with illustrations on my blog in case you need more info (quite likely, I should think!)
As you can see, I've got some data coming out of the table and getting filtered by tFilterRow_1 to only show the rows I'm interested in.
The next step is to limit it to just the field I want to use in the variable. I've used tMap_3 rather than a tFilterColumns because the field I'm using is a string and I wanted to be able to concatenate single quotes around it but if you're using an integer you might not need to do that. And of course if you have a lot of repetition you might also want to get a tUniqueRows in there as well to save a lot of unnecessary repetition
The next step is the one that does the magic. I've got a list like this:
'A1'
'A2'
'B1'
'B2'
etc, and I want to turn it into 'A1','A2','B1','B2' so I can slot it into my where clause. For this, I've used tAggregateRow_1, selecting "list" as the aggregate function to use.
Next up, we want to take this list and put it into a context variable (I've already created the context variable in the metadata - you know how to do that, right?). Use another tMap component, feeding into a tContextLoad widget. tContextLoad always has two columns in its schema, so map the output of the tAggregateRows to the "value" column and enter the name of the variable in the "key". In this example, my context variable is called MyList
Now your list is loaded as a text string and stored in the context variable ready for retrieval. So open up a new input and embed the variable in the sql code like this
"SELECT distinct MY_COLUMN
from MY_SECOND_TABLE where the_selected_row in ("+
context.MyList+")"
It should be as easy as that, and when I whipped it up it worked first time, but let me know if you have any trouble and I'll see what I can do.
I have a query in a base-data table that (given that my search criteria are correct) gives back approx. 950 records.
Except of the 3 criteria fields, i want to have about 10 more fields (the Project is still at the beginning) , every single one based on sub-queries, some of them normal select queries, some are aggregated queries.
As far as i know every sub-query must give 1 and only one value back.
This value school be individual for every Record of the top query.
My Problem now is, that i don't know how to pass the search criteria from the top query (simple select query) to the sub-query in the in 10 fields i mentioned before.
Is this possible at all, or is my Approach to complicated. Is there possibly an easier way?
I have a Windows 7 System with Office 2010 installed.
Your help is much appreciated.
Thanks a lot.
PS
The sub-queries are based on the same table as the top query. Sorry, I forgot to mention.
You can pass arguments between things with a function call to set a public variable. This vba must be in a Module, not behind a Form Module. I don't use this approach very often, because the global value is in volatile memory, I prefer to save the variable in a special data Table.
Public strGlobal As String
Function Func_ReadGlobal() As String
Func_ReadGlobal = strGlobal
End Function
Function Func_WriteGlobal() As String
strGlobal = Func_WriteGlobal
End Function
In all subqueries create parameter(s) and use it as search criteria. Parameter name should be the same for same column. Now, if you use those subqueries in your main query, Access will ask only once per each parameter name, you don't need to pass them explicitly to subqueries.
Thank you guys.
I did'nt think of the most obvious solution with the Globals. I will try it out as soon as my Boss gives me the time to continue with the Project.
#Sergey
I can't use the Parameter(s) way, because the whole query, incl. Subqueries shall run completely alone in VBA, without human input at all.
I want to use the macro' "send-object command" to email the records of a bunch of queries on a daily basis, but I only want it to send the given email if there are records in the given query. if the query doesnt output any records, then I dont want that email to be sent. I know how to accomplish this by using an Acces report by setting the "on no data event", but I would prefer that my macro doenst run the Access reports and just runs the queries , because outputting the data in reoprt format uses to much resources on my computer. please note that I do not know VBA, so I would like to know if I can accomplish my objective without any VB programming. thank you very much for your advi
In a macro, you can use the CONDITIONS column to test a DCount() expression to see if the results are >0 and then your SendObject will run.
To do this, open your Macro with the SendObject line.
From the VIEW menu, select CONDITIONS. This will add a column to the left.
In that column, put something like this:
DCount("*", "MyQuery", "[conditions that are being tested, if necessary]")>0
This is a DCount() to see how many records the query returns. You'd leave the third argument out (along with it's leading comma) if your query is already appropriately filtered (e.g., it has a reference to a form control as criterion). If the query returns 0, the CONDITION returns FALSE and your SendObject command won't execute.
You'll have to figure out how to construct the DCount(), but the point is that by testing how many records will display in the query, you can conditionally execute the SendObject.
Unfortunately you need VBA to accomplish this. Are you willing to put some code in ?