EXCEL VBA - Select Case - Index / Match - excel

I have a spreadsheet which contains a massive IF then Formula and it's getting a bit out of control, the spreadsheet is getting slower and becoming more difficult to maintain.
Is it possible to move the formulas from the cells to VBA? Would this have any improvement on performance? I was thinking about using a Select Case but need some help as to whether a select case would work. The select case will need to look at multiple cells i.e. If A1 = Yes AND A2 = "Cash" AND A3 = No" then call the Index Match function to retrieve the value. Is this possible?
Is there a big performance gain moving formula's out of the worksheet into VBA?
Thanks for any advice.
Brett

I don't think you find any performance improvement simply from moving the same code from the cell into VBA.
However it will be easier to read, and you may then see that not all of the conditionals are necessary, so you may be able to simplify it - which would speed it up.
"Select Case" will probably not be helpful though as it can only be used for a single comparison, not three like you mention.
I would suggest taking it one step at a time, move the formula into VBA, and convert it to a VBA "if" statement. Then you may be able to see ways to tidy it up.

Yes and No.
The main advantage to using VBA is that you could then also use for or while loops.
So instead of writing the same formula for each cell, you can develop your code to do these calculations in as many cells as you need/want.
Its not an easy question to answer, due to the little amount of information provided in the question.

Having the code in Excel formulas mean that it will be recalculated when any of its parameters changes i.e. you change the value of one Excel cell -> all Excel cells that use this value will be recalculated. This may significantly slow down Excel when you are making constant changes.
One way of handling this is to manually turn off Automatic calculations (Formulas->Calculation Options->Manual). Make your changes and run the calculations manually by hitting Formulas->Calculate Now for recalculating the whole workbook or Formulas->Calculate Sheet for a recalc of only the worksheet.
Another way is to move the formulas to a VBA procedure. This way you can execute the calculation manually while all the other calculations in the workbook are carried out automatically.
EDIT1:
To make an VBA procedure from an Excel formula create a procedure like this:
Sub CalculateFormula
Range("A1").Value = Evaluate("YOUR EXCEL FORMULA WITHOUT '='")
'Simple example below
Range("A1").Value = Evaluate("IF(A1=1,1,2)")
End Sub
EDIT2:
With looping:
Sub CalculateFormula
for i = 1 to 10
Range("A" & i).Value = Evaluate("IF(B" & i & "=""Hello"",2,1)")
Next i
End Sub

Related

All Excel Formulas Updating at Once

I am trying to use to use the following formula in an excel document: =IF(C2="","",NOW()). I'm just trynig to capture the time that a cell is populated. The problem I am running into is that everytime one formula updates, they all update.
For example, if I have the two formuals:
=IF(C2="","",NOW())
=IF(C3="","",NOW())
If I update C3, the formula that relates to C2 also upates. So my times are always identical to the last updated cell. I can't get them to update when only there dependent cells update.
One other thing of note, this spreadsheet is being used in MS Teams, so macros are not an option.
Is there a setting I have incorrect potentially? Or a work around?
Thanks in advance!
Have you looked into Office Scripts? They work a lot like macros in VBA, but they are for Excel Online.
https://learn.microsoft.com/en-us/office/dev/scripts/overview/excel
The behavior is expected since =NEW() always returns the current time, and you have no control of when a sheet might recalculate.
The best way to handle this situation is to have a macro that runs when the worksheet updates and it manually writes a value into the desired cell from the current time.
For example if the values to be tracked are in column "C" (the 3rd column) then the code below will write the current time of the adjacent cell (one column over) every time that value changes.
Place the code below under the worksheet
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Column = 3 Then
If IsEmpty(Target.Offset(0, 1)) Then
Target.Offset(0, 1).Value = Now()
End If
End If
End Sub
The code above checks to see if a timestamp is already filled before setting it.
Try this:
=IF(CELL("row")=ROW(C2), NOW(), C2)
Drag down your C column as needed.
NOTE: You'll need to enable iterative calculations for formulas (Options -> Formulas).

VBA - Place function in cell. Application-defined object-defined error

Noob VBA user here, please excuse any lapses in correct terminology.
I have a macro that combines a number of spreadsheets, performing a number of calculations etc along the way.
As part of this I wish to place a function in a cell, that would update as data in the spreadsheet changes. (ie, the calculation needs to be performed within the worksheet, rather than in the macro code.)
The function is to check if a particular cell is blank, if so adding it to the sum function, if not it will just add zero to the sum function.
I used this line in the macro to accomplish this:
Range("C2:C" & 25).Value = "=SUM(IF(ISBLANK($F$1),E2,"")+(IF(ISBLANK($H$1),G2,"")))"
Note.. this works fine when entered directly into a cell as:
=SUM(IF(ISBLANK($F$1),E2,"")+(IF(ISBLANK($H$1),G2,"")))
However I get a application-defined/object-defined error when trying to run it. Any ideas on how I can make this work? Or is there another route entirely to get a function in a cell using VBA?
Cheers.

Alternate for Indirect used with Sumifs?

I am using the formula to ~300 cells which takes the address dynamically using INDIRECT function, but it is slowing down the calculation time.
please suggest me an alternative method to make the calculation faster.
I have done basics as it has macro with screen updating= false and calculation=xlmanual.
=SUMIFS(Sheet1!$L:$L,Sheet1!$I:$I,Bookings_QTD!$F51,Sheet1!$B:$B,Bookings_QTD!I$2,INDIRECT($I$8),$K$8,Sheet1!$C:$C,$M$8)/1000000
here cell $I$8 is dynamic where values will varies based eg:-Sheet5!$A:$A, Sheet5!$B:$B...
$I$8=Sheet5!$E:$E
I need to use it for many cells ~400+ with other criterias in future.
kindly suugest me better formula or method which will decrease the calculation time.
Thanks in advance
INDIRECT() will be slow for many sheets as it is a Volatile function. i.e. every time there is a change in any cell in the workbook, it will get triggered.
If your cell values are relatively static. i.e. if you are indirectly referring to cell "B6" and you expect the content of B6 to remain the same and only expect the input of your function to change, say from "B6" to "Z8", you can use the following code:
Function MyIndirect(RangeStr as String) as Variant
MyIndirect = ActiveSheet.Range("RangeStr").Value
End Function
This should work. And should you need to 'refresh' this value, simply run an Application.Calculation (i.e. press Ctrl-Alt-F9)

Apply formula to large range

I have a large range (upto 60k lines) which I would like to apply a formula too.
A loop would take too long. The formula would apply to info on the row so wouldn't be the same exactly but the row would be the only variable.
What's the best way to do this? I thought I would use VBA rather than Excel to make sure the spreadsheet didn't get too cumbersome.
Below is a simple way to do what you want. If the formula takes too long to update it may be better to calc the result using VBA and output the result to the range.
Assume you want the formula =INDEX($M:$M,MATCH(A2,$N:$N,0),0) in rows D2:D60000
i. Turn on the macro recorder
ii. Enable Use Relative References
iii. Enter the formula required in a single cell (ensure if dragged down it will be correct for all rows)
iv. Turn off the macro recorder
v. In the VB Editor find the formula recorded - eg
ActiveCell.FormulaR1C1 = "=INDEX(C13,MATCH(RC[-5],C14,0),0)"
vi. Apply the formula to the required range using
Sheet1.range("D2:D60000").FormulaR1C1 = "=INDEX(C13,MATCH(RC[-5],C14,0),0)"

Conditional formatting using AND() function

I'm trying conditional formatting on a sheet. I need to fill the cells with a certain color according to the following conditional statement:
=AND((INDIRECT(ADDRESS(4;COLUMN()))>=INDIRECT(ADDRESS(ROW();4)));(INDIRECT(ADDRESS(4;COLUMN()))<=INDIRECT(ADDRESS(ROW();5))))
When I try the statements in the AND() function separately, they seem to work, but when I put them together in the function I don't see any formatting happening.
Here is some background:
Row 4 of the "current column" has a date (DATE1) in it. There are also dates on the D and E columns of the "current row" (DATE2 and DATE3). So, I would like to fill the cell with a color if DATE1 is between DATE2 and DATE3.
I cannot see why the formula is not working. Any help is much appreciated.
Update (Dec 13, 2011):
I implemented a function that I call from the cells I need this functionality. The function returns integer values. Then conditional formatting only uses the integers in the cells. This way, the conditional formatting is less complicated. I'm passing INDIRECT(ADDRESS(ROW();COLUMN())) into the function I implement. So, I have all the information I need when working on relative and/ or absolute cells. Would be great to know a simpler way to pass the current cell as range into the function.
Note: ActiveCell didn't seem to work for me. It uses the data from the cell which is selected by the time the function is run. That's not what I'm looking for. I could of course pass the cell itself (as in A4, B7, etc.) but I'm not sure if it really matters in terms of performance.
Thanks to all of you who responded to my question.
I was having the same problem with the AND() breaking the conditional formatting. I just happened to try treating the AND as multiplication, and it works! Remove the AND() function and just multiply your arguments. Excel will treat the booleans as 1 for true and 0 for false. I just tested this formula and it seems to work.
=(INDIRECT(ADDRESS(4,COLUMN()))>=INDIRECT(ADDRESS(ROW(),4)))*(INDIRECT(ADDRESS(4,COLUMN()))<=INDIRECT(ADDRESS(ROW(),5)))
You can use a much simpler formula. I just created a new workbook to test it.
Column A = Date1 | Column B = Date2 | Column C = Date3
Highlight Column A and enter the conditional formatting formula:
=AND(A1>B1,A1<C1)
I had a similar problem with a less complicated formula:
= If (x > A & x <= B)
and found that I could Remove the AND and join the two comparisons with +
= (x > A1) + (x <= B1) [without all the spaces]
Hope this helps others with less complex comparisons.
This is probably because of the column() and row() functions. I am not sure how they are applied in conditional formatting. Try creating a new column with the value from this formula and then use it for your formatting needs.
COLUMN() and ROW() won't work this way because they are applied to the cell that is calling them. In conditional formatting, you will have to be explicit instead of implicit.
For instance, if you want to use this conditional formating on a range begining on cell A1, you can try:
`COLUMN(A1)` and `ROW(A1)`
Excel will automatically adapt the conditional formating to the current cell.
I am currently responsible for an Excel application with a lot of legacy code. One of the slowest pieces of this code was looping through 500 Rows in 6 Columns, setting conditional formatting formulae for each. The formulae are to identify where the cell contents are non-blank but do not form part of a Named Range, therefore referring twice to the cell itself, originally written as:
=AND(COUNTIF(<rangename>,<cellref>)=0,<cellref><>"")
Obviously the overheads would be much reduced by updating all Cells in each Column (Range) at once. However, as noted above, using ADDRESS(ROW(),COLUMN(),n) does not work in this circumstance, i.e. this does not work:
=AND(COUNTIF(<rangename>,ADDRESS(ROW(),COLUMN(),1))=0,ADDRESS(ROW(),COLUMN(),1)<>"")
I experimented extensively with a blank workbook and could find no way around this, using various alternatives such as ISBLANK. In the end, to get around this, I created two User-Defined Functions (using a tip I found elsewhere on this site):
Public Function returnCellContent() As Variant
returnCellContent = Application.Caller.Value
End Function
Public Function Cell_HasContent() As Boolean
If Application.Caller.Value = "" Then
Cell_HasContent = False
Else
Cell_HasContent = True
End If
End Function
The conditional formula is now:
=AND(COUNTIF(<rangename>,returnCellContent()=0,Cell_HasContent())
which works fine.
This has sped the code up, in Excel 2010, from 5s to 1s. Because this code is run whenever data is loaded into the application, this saving is significant and noticeable to the user. It's also a lot cleaner and reusable.
I've taken the time to post this because I could not find any answers on this site or elsewhere that cover all of the circumstances, whilst I'm sure that there are others who could benefit from the above approach, potentially with much larger numbers of cells to update.
Same issues as others reported - using Excel 2016. Found that when applying conditional formulas against tables; AND, multiplying the conditions, and adding the conditions failed. Had to create the TRUE/FALSE logic myself:
=IF($C2="SomeText",0,1)+IF(INT($D2)>1000,0,1)=0

Resources