i need advice about vba scripting, macros.
So, i have excel table with 3 sheets named: Predujam, OSTATAK NAKNADE, vlookup
Predujam and OSTATAK NAKNADE contains vlookup/xlookup formula which are pulling data from vlookup sheet.
All 3 sheets contain like 4000 lines of data, columns range is A:Q.
For sheet Predujam i use this code (this code is supposed to do calculation only in the rows where the changes occurred):
Private Sub Worksheet_Change(ByVal Target As Range)
Dim KeyCells As Range
Set KeyCells = ThisWorkbook.Sheets("PREDUJAM").Range("A2:O5000")
If Not Application.Intersect(KeyCells, Range(Target.Address)) _
Is Nothing Then
Range("A" & Range(Target.Address).Row & ": O" & Range(Target.Address).Row).Calculate
End If
End Sub
For sheet OSTATAK NAKNADE use same code:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim KeyCells As Range
Set KeyCells = ThisWorkbook.Sheets("OSTATAK NAKNADE").Range("A2:O5000")
If Not Application.Intersect(KeyCells, Range(Target.Address)) _
Is Nothing Then
Range("A" & Range(Target.Address).Row & ": O" & Range(Target.Address).Row).Calculate
End If
End Sub
In sheet vlookup
I am using only this code to calculate that sheet only when it is opened:
Private Sub Worksheet_Activate()
Worksheets("vlookup").Calculate
End Sub
And in ThisWorkbook i inserted code that stops automatically calculation:
Private Sub Workbook_Open()
Application.Calculation = xlManual
Application.CalculateBeforeSave = False
End Sub
So basically i wanted to stop all calculations and to calculation only in the rows where the changes occurred.
Problem is: table is still laggy, saving still takes like 20-30 sec to save, and calculation in rows where the changes occurred is also doing with some delay.
My Question: Do i have some mistakes in the codes which could cause lagginess? If there is any better way to write this, can you please tell me how to rewrite it?
I am just beginner in vba scripting so i need some advices.
Thanks! :D
This isn't a proper answer, but a comment doesn't afford me enough space to write all of this...
As someone using VBA you probably appreciate the value of variables, i.e.
calculate something once,
'remember' it in a variable, and then
you can reference that variable efficiently multiple times, rather than, inefficiently, recalculating the something afresh every time you need to use it
Helper cells in the user-interface, while much maligned, are the equivalent of variables in this regard, i.e.
something is calculated once,
'remembered' in a designated cell, and then
that cell can be referenced whenever the result is required, rather than having every formula that needs the result recalculate it from first principles.
Why am I rambling about this ? Given that you are setting Workbook calculation to manual, your file is obviously spending a lot of time calculating: if you audit the formulae in the workbook then you will likely find at least some calculations common to several formulae - if you extract these calculations to designated helper cells, then you can improve performance by having those results calculated only once, and then just referenced from the 'old formulae' rather than having the calculations repeated in each formula in which they currently occur.
Related
I'm looking to try and run a macro when data is added to a cell. All I've been able to find advice on so far is how to run a macro when data is changed in a cell, which won't work. if data is removed from a cell then i don't want the macro to run. I'm fairly new to VBA so any advice would be appreciated.
I have tried using an intersect function as well as other, but I can only make my code run macros when the cells change as oppose to when data is added.
this is my current code
Sub Worksheet_Change(ByVal Target As Range)
'detect data in cell
If Not Intersect(Target, Range("J13:J27")) Is Nothing Then
Call Copy_Cell
End If
End Sub
There is no other event that you can use, the Change-event is the right place to go. All you need to do is to check if the modified cell(s) contain something or not.
Now when the change-event is triggered, more than one cell can be modified (eg by Cu&Paste), so you will likely need to check all modified cells individually.
As you don't show the code of Copy_Cell, I can only assume that this routine is copying something - likely using ActiveCell. You should change that routine and let it receive the cell to be copied as parameter.
Also, you need to be aware that if Copy_Cell is writing something in the same worksheet, the Change-Trigger is called recursively. To avoid that, use Application.EnableEvents = False.
Your code could look like this:
Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo Change_exit ' Ensure that Events are enabled even if an error occurs
Application.EnableEvents = False
Dim cell As Range
For Each cell In Target
If Not Intersect(cell, Range("J13:J27")) Is Nothing And Not IsEmpty(cell) Then
copy_Cell cell
End If
Next cell
Change_exit:
Application.EnableEvents = True
End Sub
I tried to work a bit more with dynamic arrays in excel in combination with vba. My problem is that I cant return a table-column with vba. Here a minimal example of what I want to do:
I have two Tables TabFeb and TabMar (see image below). Each of them has a column costs which I want to sum up individually. The results shall be put into a new Table. This can be easily done in excel with =SUM(TabFeb[Costs]) and =SUM(TabMar[Costs]), respectively. My idea is now to write a VBA function which takes a string as input, in this example it will be the month, and returns the table acording to the input. After that it will be summed up and the result is given in a cell.
I tried the following:
Function Selectmon(mon As String) As Range
If mon = "Feb" Then
Set Selectmon = Worksheets("Sheet1").ListObjects("TabFeb").ListColumns("Costs").DataBodyRange
ElseIf mon = "Mar" Then
Set Selectmon = Worksheets("Sheet1").ListObjects("TabMar").ListColumns("Costs").DataBodyRange
End If
End Function
The problem of this idea is that this function just copy the table data. Hence, if I would change the input table data the sum would not change. One has to recalculate every cell by hand. Somehow I need VBA to return TabFeb[Costs] for the input "Feb". Does anyone have an idea how this can be done?
Example
It's really just a one-liner (unless you want to do some in-function error checking)
Function Selectmon(mon As String) As Range
Set Selectmon = Range("Tab" & mon & "[Costs]")
End Function
As implied by #ceci, this formula will not update with changes in the table. Depending on other particulars of your worksheet, you can have it update either by
embedding it in a worksheet change event code;
or by adding the line Application.Volatile to the function itself.
The latter method will force a recalculation when anything changes on the worksheet that might cause a recalculation.
The first method can limit the recalculation only when there has been a change in the data, but has other limitations.
One of the limitations of the Worksheet Change method is that it will only work on the relevant worksheet.
If you use the Workbook sheet change method, you won't have that limitation.
In either event you can limit your code to run only when the table has changed.
Here is one generalized method:
Option Explicit
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
Dim LOS As ListObjects
Dim LO As ListObject
Set LOS = Sh.ListObjects
For Each LO In LOS
'could select relevant tables here
'Could also select relevant worksheets, if you like
'for example
Select Case LO.Name
Case "TabFeb", "TabMar", "TabApr"
If Not Intersect(LO.DataBodyRange, Target) Is Nothing Then
Application.EnableEvents = False
Application.Calculate
End If
End Select
Next LO
Application.EnableEvents = True
End Sub
And there is other code you could use to find the relevant formula and just update that formula -- probably not worth the effort.
What if we could fill a date by only referring a number ? This type of feature would improve user-friendliness in some excel scenarios.
For instance : In March sheet, when I type "7" in a given dates column, the cell in which I inserted the value would return "07/03/19" (or 03/07/19).
If possible, this means I need to specify in the VBA code the month and year for this sheet, and change this variable for every single sheet (february etc.). If the sheet names are months names (Eg "March"), there could even be a way to do it with a one solution VBA code. The following formula takes advantages of it, so I guess VBA could do it to.
=MONTH(DATEVALUE(MID(CELL("filename";$A$1);FIND("]";CELL("filename";$A$1))+1;255)&" 1"))
Enter this formula in a sheet named "March" and it will return "3".
I have been looking for a simple way to do this, but there is none to my knowledge (until you bring your light :)). Data validation feature won't help as far as I know. Also, it's important that the cell in which the number is inserted autofill itself (not another cell).
Is this at least possible ? I bet yes. I've been told to look at event function, but I know too little with VBA.
This may need modified to fit your needs, but maybe a solution like this using the Worksheet_Change event.
Worksheet Change portion:
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo SafeExit:
Application.EnableEvents = False
If Target.Cells.Count = 1 Then '
If Not Intersect(Target, Me.Columns("A")) Is Nothing Then 'change as needed
Target.Value = DateFromDay(Me.Name, Target.Value)
End If
End If
SafeExit:
Application.EnableEvents = True
End Sub
Main Function
Public Function DateFromDay(monthName As String, dayNum As Integer) As Date
On Error Resume Next
DateFromDay = DateValue(dayNum & " " & monthName & " " & Year(Now()))
End Function
You might consider the Workbook_SheetChange event as well to add this functionality to multiple sheets.
I have 7 worksheets which do exactly what I want. I am now being asked for a filter to show specific years. Done. However to look at a year of trend data, I have to manually filter each sheet.
I wouldn't mind going the extra mile, and if it's possible, have a filter in one of these sheets that organises the year in all the other sheets.
I have=YEAR(O9:O29148) on my largest sheet. A8:O8 and everything above is exactly the same on each sheet, every sheet has the same type of data in the same column. The only thing that does change is the unique data itself.
What I want is to have a Year filter (2000-2018) on my dashboard, which will then filter all the worksheets to show the same year, or all data if required.
Is this even possible?
(I do not understand VBA code, but I am capable of inserting it into VBA editor and then running said macro).
Any help would be greatly appreciated, thank you!
Not really knowing a lot about the way your data is set up, I build the following, with this code on the worksheet_change event of the dashboard sheet, where I have E6 controlling the year. I have 3 other sheets with data in column A with year numbers, you can use this as a base. You will need to experiment with your column, on the filter, number most likely.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim wsWorksheet As Excel.Worksheet
If Target.Cells(1, 1).Address(False, False) = "E6" and Target.Cells.Count=1 Then
For Each wsWorksheet In ThisWorkbook.Worksheets
With wsWorksheet
If .Name <> Target.Worksheet.Name Then
If .UsedRange.AutoFilter Then
.UsedRange.AutoFilter 1, Target.Value
End If
End If
End With
Next wsWorksheet
End If
End Sub
Public Sub Filter_Sheets()
Dim i As Long
Dim comboBox As ControlFormat
With ThisWorkbook
Set comboBox = .Worksheets(9).Shapes("Drop Down 229").ControlFormat
For i = 1 To Worksheets.Count
.Worksheets(i).UsedRange.AutoFilter Field:=15, Criteria1:=comboBox.List(comboBox.ListIndex)
Next
End With
End Sub
This is the best fit I have managed to discover. I still get an error (AutoFilter method of Range class failed). However this does work. I am now using a combobox to change the auto filter on all 7 sheets as needed. In order to go back to select all, having "<>" in a cell the dropdown references, works to select all the data again.
I have an excel sheet with 1,373,760 used cells and each of them have a formula inside it unique in their own way. But every cell refers to a file path, which needs to be changed. Search and replace is taking huge amount of time and fails often at certain places. Is there an efficient way to replace these cells?
Sample formulae: '\root\folder\subfolder\another_folder[workbook_name]worksheet_name'cellNumber
Workbook, Worksheet, cell number are unique. Only the path is constant.
I tried referring to other cells by storing path and sheet names, but it's not working that way:
Reference another workbook with dynamic worksheet name
All these cells are populated using VBA which took around 15 hours. So any efficient way to create the new workbook is appreciated as well.
Thanks in advance!!
Something like this, depending on what exactly you are looking for:
Public Sub TestMe()
Dim myCell As Range
For Each myCell In ActiveSheet.UsedRange
If myCell.HasFormula Then
If InStr(1, myCell, "\root\folder\subfolder\another_folder") Then
myCell.Formula = "=root\folder\subfolder\another_folder" + something
End If
End If
Next myCell
End Sub
Turning Off automatic calculations here is a good idea.
The code goes around each cell in the UsedRange of the ActiveSheet and in case that it is a formula, it checks whether it contains '\root\folder\subfolder\another_folder.
If this is the case, a new formula is generated.
Search and replace from my humble opinion is your fastest solution, disable calculations via VBA or manually. after the replace has finished enable automatic calculations.
VBA code to enable and disable automatic calculations:
Sub Disbale_Automatic_Calc()
Application.Calculation = xlCalculationManual
End Sub
Sub Enable_Automatic_Calc()
Application.Calculation = xlCalculationAutomatic
End Sub