Excel VBA UDF Recalculation Indicator Parameter - excel

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.

Related

Excel Refreshes whole sheet even when a single cell is changed

I am working on an Excel Add-in and have created some custom functions in it. Now my user has created some functions (150+ custom functions) and all of these custom functions have parameters as references to other cells in sheet. When user updates some text cell (whether referred in some function call or not) , whole sheet is refreshed and Excel shows busy in all cells having custom functions it. I understand that if a cell is referred in some custom function and it is updated, that custom function is called to show updated data, but it should not happen when user edits a cell not referred in any of the functions.
I cannot change sheet calculation mode to manual as it will stop updating other cells which are changed by user. Also If I change calculation mode to Manual and on changing it back to Auto again refreshes whole sheet, so setting it to manual has drawbacks without gaining anything in my case.
Is it possible that your UDFs use volatile formulas such as
NOW,TODAY,RANDBETWEEN, OFFSET, INDIRECT, INFO, SUMIF, RAND or you declared your UDFs to be volatile?
In this case functions are recalculated if changes in any cells are made. For example if you fill hundred thousand lines with RAND it takes few seconds to recalculate after entering any value to a blank cell.
More on volatile formulas:
Volatile values in functions
More on volatile formulas in UDFs:
Excel Recalculation

Dynamic Worksheet Title Reference Optimization

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.

Auto calculation VBA excel

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.

how would you write an excel udf that's manually calculated regardless of calculation mode?

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

Point to a relative other Sheet in Excel

I'm trying to find a way to from a Cell get the data from a cell in the Sheet that lies to the Left (down in the tray) of the current Sheet.
I know how to call to other sheets via
=Sheet1!A1
But now I need something best explained with
=Sheet[-1]!A1
Any ideas?
Using the tab order as a fundamental part of your calculations is a complicated and risky approach to Excel calculations. Excel offers many alternatives which you'd be better off using:
A simplified version of belisarius's suggestion is: =INDIRECT(A1 & "!A2") where cell A1 has the name of your datasource sheet and A2 has the name of your target cell in your datasource sheet. If you know the name of your sheet of interest (or can look it up in some way), use this method.
If you need to do this often, you might want to export the data into an actual database (i.e. MS Access). Then you can make standard SQL queries and import the results into your Excel file.
If you absolutely want to go the VBA route, then you'd have to write some code that:
3a. Grabs all the names of the active workbook and stores them in an array.
3b. Identifies the index number of the currently active workbook in that array. Subtract 1 from that index to get the sheet to the left.
3c. Gets the cell value from that sheet.
You can also get freaky with Named Ranges. In Excel 2003, go to Insert->Name->Define, add a new Named Range and you can use that name in your calculations instead of referring to the cell by row and column.
Edit
The whole Idea with this one, is that
you have the Sheets arranged, and are
able to move them around, and that
shall change the calculations. – Gnutt
1 hour ago
Please, please, don't do that. For starters, this isn't a standard method of interaction with a spreadsheet. Your end-users will likely be confused and may not even ask for clarification.
You'll want to explore the idea of data validation:
Using Data->Validation, make a drop-down menu listing all the sheets in the workbook (if the names of all the sheets are static, you can just hardcode them, otherwise, you'll need some VBA to pull them).
Then the user just picks the sheet of their choice and indirect() will automatically update everything.
Alternatively, you can also check out Tools->Scenarios. I don't know anybody who uses this feature, but you might be a good candidate for it. Basically, it lets you see the results of calculations using different datasets (i.e. "scenarios") so the user can go back and forth between them.
Using either of the 2 methods above, there's a good chance you can avoid VBA entirely, thus saving users that annoying warning message when they open your file.
=INDIRECT("Sheet"&TEXT(VALUE(MID(CELL("filename",A8),FIND("]",CELL("filename",A8))+1,256))-1,"#")&"!A1")
Caveats:
Your workbook must be saved previously
A8 may be replaced by a reference to ANY non-error cell
I know it's not seen here as good practice, but I want to do something similar. And it does replicate database functionality to an extent but I don't have the time or support to build one from scratch when there's something already half in place.
The reason I want to be able to do this is to create a summary table that links to all the worksheets in the workbook, and automatically extends if you insert a new worksheet. This is to manage a large sales / reporting spreadsheet with lots of different business units that all have the same structure (ie use the same worksheet format to report the same outcomes for different people. There is a high turnover. I want to have several summary sheets reporting different aspects of the source sheets. This is very time consuming to manage if recreating all of the tables each time.
You should be able to use the row() as an index marker to define the information that you want using something like REPLACE, OFFSET or INDEX but you can't as they only refer to 2D arrays.
Whereas Excel treats 3-D references as arrays for statistical functions it does not seem to do the same for reference functions. You might have SUM(sheetX:sheetY!A1) and be able to add a sheet in between, there is not (eg) a INDEX(sheetX:sheetY!A1,n) function. I've tried experimenting using these 2D functions as part of array formulas, and defining the 3D reference as an array or a named range... well it was worth a go :).
So I believe it's a valid action. I also believe there must be a way to do it, but for now I'm falling back on a UDF that has the risk of errors caused by calculation issues, or manipulating a Workbook_SheetChange function or similar. Or creating a single master sheet to control all the others which is populated by using a subroutine based on an array of all workbooks.
these functions work well for me. They get the worksheet index (the parent of
the range you call them with), add or subtract from that index, and then create
a range from that (relative) sheet and the address passed in.
Function relativeSheet(r As Range, iRelative As Integer)
Application.Volatile
relativeSheet = Sheets(r.Cells(1, 1).Parent.Index + iRelative).Range(r.Address)
End Function
Function prevSheet(r As Range)
prevSheet = relativeSheet(r, -1)
End Function
Function nextSheet(r As Range)
nextSheet = relativeSheet(r, 1)
End Function

Resources