When you create a custom-user defined function in VBA that can be used as a regular spreadsheet function, I can't seem to stop it from auto calculating during, before, and after run time.
The data is in a table format and auto populates when adding extra rows. When I set application.calculation to xlmanual, the macro still iterates over each row to ensure that the user defined function is calculating - but there doesn't seem to be a way to pause that and significantly decrease runtime. Why is it that every row in a table with a custom-user defined function keeps getting triggered when something interacts with it?
Another potential symptom - what is what's even more problematic is that when I click on a cell(when the macro isn't running) containing a formula, each and every row still auto calculates even when set on manual.
This seems to be a bug, or maybe the downside of using a table list object. Any way around this?
Let me know if I explained the question well, or I can continue to revise this for a productive answer.
Related
I need to provide a current list of files in a directory in an Excel workbook and everything is working as required, just too slowly. I really only need the list to check it is current once upon opening the workbook. It takes around 11 seconds to do this which is acceptable but the problem is it keeps rechecking this every time I carry out even minor edits to the workbook (I guess due to the fact that it is brought in as an Excel table). I determined the lag in my workbook using the rangetimer() function that is provided and it is the only thing taking a long time to calculate. I should also state that the table containing the list of files is finally used in a cell on another worksheet to provide a data validation drop-down list but don't believe this is really the issue.
I did some Googling on reducing Excel calculation times and discovered that there are some Excel functions that are definitely culprits for increasing calculation times (described as volatile) and three of these (NOW,INDEX and ROW) are used in providing the functionality I would like in this part of the workbook.
I have tried two solutions so far:
1. Force Full Calculation set to True in VBA properties window
2. Switched calculations to manual. I set this back to automatic once I identified that this part of the workbook was the issue as I don't want manual calculation generally.
The formula I have in the 'refers to' box of the named range (TutorFileList) is:
'''=FILES("\O008DC01\Shared\Tutor Qualifications*")&T(NOW())'''
The formula I have in each cell of the excel table is:
'''=IFERROR(INDEX(TutorFileList,ROW()-1),"")'''
What I would like to have is the ~11secs of calculated time to find these files reduced down to just one check of the networked directory rather than it taking 11secs of automatic recalculation every time the workbook is modified.
If there is a more efficient way to achieve what I am doing I am prepared to redesign things but I do need the functionality of a drop-down list of files in the specific directory in a cell.
Many thanks for assistance from anyone on this.
I have resolved my issue by reducing the number of rows back to around 200 instead of 500 rows. This brings the calculation lag back to about a second which I can live with.
I have a UDF function within a xlam workbook that connects to a database and executes a query, based on the parameters entered. This function is going to be used hundreds of times within different workbooks. Since it is going to be used in a series of different workbooks, that is the reason why I used a xam workbook.
The issue is that using the UDF function so many times, slows the Recalculation down to much since it is recalculating when it doesn't need to. I was thinking about adding another indicator parameter in the UDF that if set to zero, it will just return the value of the cell before the Recalculation started. Is there anyway to do this? I was thinking about adding a conditional statement that would use something like application.caller.value to get the value.
Scenario: I have a workbook that needs to be able to dynamically reference different worksheets based on user input. I was able to achieve this by using several INDIRECT functions and some other basic functions such as INDEX.
My problem is that the workbook is taking several minutes to load on a fairly powerful computer with no other strenuous processes running.
Question: Is there a simple way to optimize workbooks? I tried looking online for some standard methods to run through to catch unnecessarily strenuous processes to no avail. I am willing to use VBA if it would help speed the sheet up.
Thanks
Don't use INDIRECT or any other volatile functions (OFFSET, RAND, NOW, TODAY) in your workbooks if you can help it, because they get recalculated automatically pretty much at the drop of a hat...even if you’re doing something to the workbook that has absolutely nothing to do with those formulas whatsoever, such as:
Entering new data in a completely unrelated cell
Editing any existing, completely unrelated cells
Deleting or inserting a row or column anywhere in the workbook
Performing certain AutoFilter actions
Double-clicking a row or column divider (in Automatic calculation
mode)
Adding, editing, or deleting a defined name
Renaming a worksheet
Changing the position of a worksheet in relation to other worksheets
Hiding or unhiding rows (but not columns)
This means that if you have lots and lots of volatile functions like OFFSET or INDIRECT in your formulas, those formulas are going to be recalculating pretty much constantly. And if those formulas do some pretty resource-intensive number-crunching, Excel will be re-crunching those numbers pretty much every time you touch the mouse or keyboard. Yikes!
But that’s not the half of it: After Excel recalculates those functions, it merrily starts recalculating all cells downstream of them—regardless of whether those downstream cells have volatile functions in them. And regardless of whether anything actually changed as a result. And I mean all cells...not just the cells that directly point to your volatile functions but all the cells that point to those cells, and then the next bunch of cells that point to that last bunch, and so on and so on, all the way through your workbook. If you’ve used volatile functions at key points within a spreadsheet, pretty much the entire spreadsheet could be recalculating constantly. Double yikes!
But even this is not the worst of it! The problem isn’t limited to the workbook you happen to be doing something with at the time. Rather, every volatile function in every open workbook gets recalculated when you make a change in any workbook anywhere—even if there aren’t any formula links between those workbooks. So if you’ve got a very large spreadsheet model with volatile functions open—and then you start typing, say, a shopping list in another, it could take minutes for you to add each item to that shopping list, as everything you type in the shopping list triggers a new avalanche of unnecessary and pointless recalculation in the large spreadsheet model. Triple yikes!
The fact that each and every cell downstream of any volatile functions get recalculated is an important point to get your head around. Many people think that just the volatile functions themselves get recalculated. They are mistaken. It’s the subsequent forced recalculation of all cells downstream of all volatile functions in all open workbooks that causes you grief. In fact, even just one volatile function in one workbook could cause you grief if you have a very long calculation chain hanging off it.
What to use instead?
Use INDEX, or even better, CHOOSE.
Note that while INDEX isn’t fully volatile, it ain’t perfect: It still gets recalculated whenever the workbook opens. This is known as is semi-volatility, and it means that you still might notice recalculation delays when initially opening a complicated model that uses INDEX. Which is one of the reasons I go for CHOOSE
=CHOOSE(index_num,value1,value2,...)
Here’s the translation from Microsoft-speak into Jeff-speak:
=CHOOSE(Which area do you want?, First area, Second area, ...)
You need to translate your dropdown choice into a number in order to drive the Which area argument. But that’s no problem; you just use exactly the same setup as you do for INDEX—a lookup table that converts the output of the dropdown into an index number that tells CHOOSE which range you want.
If your input areas are turned into Tables, then great...you can just use the Table names.
I currently have a macro that uses a form in order to perform some calculation with the help of an external program. All it does is writing some worksheet values to a file for use by another program, an external .exe, as input (that program is closed source). Said program then writes a custom format output, I scrap it with VBA and then pass the return back to the worksheet. The macro is invoked via a userform where the source and destination ranges are specified.
What I'm trying to do is to replace the method of invoking the calculation via a userform with a UDF. With the GUI method, updating the calculation is cumbersome; it is also impossible to know what computation was performed in the destination range data. This path was chosen over the UDF because of performance concerns. Since the calculation is quite slow, I can't just reuse the part of the userform code that invokes the external program as a UDF and be done with it, since it would be way too slow. There seems to be no async execution in VBA (as opposed to xll's in Excel 2010).
A possible solution would be to exit the UDF as soon as it begins executing unless a global is set to true. That global would always be false, unless a recalc is invocated from a specific ribbon button. The problem is that this executes constantly UDFs, sending those ranges to #N/A values all the time, so I can't reference to those ranges. Another would be to create fake formulas, ie a comment on the cell that specifies both the parameters and the destination range. This too has many problems.
So. Any idea on how to implement a fake-async calculating UDF?
Thanks!
If I understand you correctly you want to have a VBA UDF that returns the value from the previous calculation if the global is false, otherwise do the full slow calculation. There are several different possible approaches to this, which all have different drawbacks:
use application.caller.value. This will cause a circular reference and require you to switch on iterative calculation
use application.caller.text. This is simple and works without iterative calculation but gets the formatted value so you need to be sure that your UDF does not lose precision etc
at udf exit store the value in a global array indexed by the
full calling cell address and at udf entry retrieve it. This will
not persist in a saved workbook unless you store the global array
somewhere at save/close and retrieve on open.
Write an XLL or XLM macro-equivalent UDF that retrieves the
previous value
In short, I would either:
Create a macro that runs every time a cell within a certain range is changed, and then writing out the result based on those cells.
-OR-
Create a macro to automatically insert functions that would do the same thing
Things to consider are that this would need to work on multiple tables of varying length without need to manually change anything, and that the results of individual sheets would need to be also displayed in summary on another page.
Which would be more desirable/more efficient/easier to implement?
The subject data entails testing procedures. Sheets contain testing criteria and a column containing PASS/FAIL results. I need to tabulate these. The start of the table and the end of the table are at different rows and are subject to changing.
Without seeing your actual problem I would definitely go for the first option. Having many formulas inserted into cells dynamically I think would be a pain.
Use the Worksheet_Change event and test whether the cell changed is one of your target range, then perform whichever action is required.