I have written several subs which are then called from a main sub. Individual subs run very quickly, most are instantaneous (the DoFind sub takes a few seconds to run due to the large amounts of data in the table) however when I run the main sub it takes up to a minute to execute. Any ideas/tips on why this is the case?
Note, I haven't had much experience with VBA (all has been learnt in the past week). There are other macros used, but they are not shown since even the test sub takes approximately 1 minute
Sub DoFind()
Dim i As Long
i = 1
Do While Sheets("Temp").Cells(i, "A").Value <> Empty
Dim BearingArray(6) As String
BearingArray(0) = Sheets("Temp").Cells(i, "A").Value
BearingArray(1) = Sheets("Temp").Cells(i, "B").Value
BearingArray(2) = Sheets("Temp").Cells(i, "C").Value
BearingArray(3) = Sheets("Temp").Cells(i, "D").Value
BearingArray(4) = Sheets("Temp").Cells(i, "E").Value
BearingArray(5) = Sheets("Temp").Cells(i, "F").Value
BearingArray(6) = Sheets("Temp").Cells(i, "G").Value
With Sheets("Calculations")
.Cells(17, "K").Value = BearingArray(0)
.Cells(19, "O").Value = BearingArray(1)
.Cells(20, "O").Value = BearingArray(2)
.Cells(23, "O").Value = BearingArray(3)
.Cells(22, "O").Value = BearingArray(4)
.Cells(26, "O").Value = BearingArray(5)
.Cells(17, "L").Value = BearingArray(6)
End With
i = i + 1
If Sheets("Calculations").Cells(17, "M").Value = "PASS" Then
Exit Do
Else
End If
Loop
If Sheets("Temp").Cells(i, "A").Value = Empty Then
MsgBox "No available bearing."
End If
End Sub
Sub Create_Sheet_Temp()
ThisWorkbook.Sheets.Add
ActiveSheet.Name = "Temp"
' This creates a new worksheet called "Temp"
End Sub
Sub Copy_Paste()
Dim NewTable As ListObject
Sheets("Calculations").Activate
Set NewTable = Sheets("Calculations").ListObjects("Full_Bearings_List")
NewTable.Range.SpecialCells(xlCellTypeVisible).Select
NewTable.DataBodyRange.SpecialCells(xlCellTypeVisible).Copy
Sheets("Temp").Range("A1").PasteSpecial xlPasteAll
Application.CutCopyMode = False
'This sub copies all visible cells from a filtered table and pastes them to the new sheet called "Temp"
End Sub
Sub test()
Create_Sheet_Temp
Copy_Paste
DoFind
End Sub
You can speed up your code by storing the worksheets in variables (ahead of the loop).
Dim TempWS as Worksheet
Dim CalcWS as Worksheet
set tempws= Sheets("Temp")
set CalcWS=Sheets("Calculations")
Also declare the array outside of the loop. Also Id recommend to use numeric column index.
Sheets("Temp").Cells(i, "G").Value
to
TempWS.Cells(i, 7).Value
Comparing against Empty is not always the best choice, try
... <> ""
EDIT:
For the Copy try t use the destination parameter of the Copy method. Example from the help:
Worksheets("Sheet1").Range("A1:D4").Copy _
destination:=Worksheets("Sheet2").Range("E5")
Related
There’s one step that’s stuck, to update the stock number (column "D") in the database_ gudang (stock in the database_ gudang is added to the amount of receipt (column "K") from form_penerimaan)
The update is based on the name of the item (nama barang), so if the name of the item (column "C") in the form_penerimaan is the same as the name of the item (column "B") in the database_ gudang, the stock in database_ gudang will be updated.
but there’s a problem, which is updated only in rows 2,9,10 (yellow cell). A row of 3,4,5 should also be updated.
Thank you very much for your help.
Regards.
Sub Module1()
s = 10
OT1 = Sheets("Database_Gudang").Cells(Rows.Count, "D").End(xlUp).Row
For j = 2 To OT1
NB1 = Sheets("Database_Gudang").Cells(j, "B").Value
Sheets("Form_Penerimaan").Activate
If Cells(s, "C").Value = NB1 And Cells(s, "C").Value <> "" Then
Sheets("Form_Penerimaan").Cells(s, "Q").Copy
Sheets("Database_Gudang").Activate
Sheets("Database_Gudang").Cells(j, "G").Select
Selection.PasteSpecial Paste:=xlPasteValues
s = s + 1
End If
Next j
End Sub
Hi and Welcome to stackoverflow :)
Avoid the use of .Select and .Activate. Directly work with variables and objects. You may want to see How to avoid using Select in Excel VBA
You are facing that issue because you are not looping through the cells of the 2nd sheet.
Is this what you are trying? (UNTESTED)
I have commented the code so you may not have a problem in understanding it. If you do then share the exact error message and we will take it from there.
Sub Sample()
Dim wsThis As Worksheet, wsThat As Worksheet
Dim i As Long, j As Long
Dim wsThisLRow As Long, wsThatLRow As Long
'~~> Set your worksheets
Set wsThis = ThisWorkbook.Sheets("Database_Gudang")
Set wsThat = ThisWorkbook.Sheets("Form_Penerimaan")
'~~> Find relevant last row in both sheets
wsThisLRow = wsThis.Range("D" & wsThis.Rows.Count).End(xlUp).Row
wsThatLRow = wsThat.Range("C" & wsThat.Rows.Count).End(xlUp).Row
With wsThis
'~~> Loop through cell in Database_Gudang
For i = 2 To wsThisLRow
'~~> Loop through cell in Form_Penerimaan
For j = 10 To wsThatLRow
'~~> Compare values and get values across if applicable
If .Range("B" & i).Value = wsThat.Range("C" & j).Value Then
.Range("G" & i).Value = wsThat.Range("Q" & j).Value
Exit For
End If
Next j
Next i
End With
End Sub
I need your help please, I have 4 columns in an excel sheet and I need to compare them 2 by 2 i will explain to you :
In column A i have users(user1,user2,user3 ...)
In column B i have functionalities ( fonc1, fonc2, fonc3.....)
In column C i have users(user1,user2,user3 ...)
In column D i have functionalities ( fonc1, fonc2, fonc3.....)
The columns C and D are a new version of columns A and B in the columns C and D the users may change order or change functionalities .
When i execute my code i put the result in other new columns:
column F where i have the users
column G where i put the Deleted_functionalities
column H where i put the New_functionalities
The first problem is that the code doesn't get the users it get only the new and deleted functionalities. The second problem is that when the column A is more than column C where the users are stocked the code doesn't work. Can you please help me to find a solution? Thank you in advance .
Here is my code and the file I am working on :
Private Sub CommandButton2_Click()
Dim rngCell As Range
For Each rngCell In Range("B2:B2000")
If WorksheetFunction.CountIf(Range("D2:D2000"), rngCell) = 0 Then
Range("G" & Rows.Count).End(xlUp).Offset(1) = rngCell
End If
Next
For Each rngCell In Range("D2:D2000")
If WorksheetFunction.CountIf(Range("B2:B2000"), rngCell) = 0 Then
Range("H" & Rows.Count).End(xlUp).Offset(1) = rngCell
End If
Next
End Sub
and this is the excel file
http://www.cjoint.com/c/FCxnwjp22rv
try this
Private Sub CommandButton2_Click()
Dim ws As Worksheet
Dim cell As Range, funcCell As Range
Dim oldUserRng As Range, newUserRng As Range, reportRng As Range
Dim iReport As Long
Dim oldFunc As String, newFunc As String
Set ws = ThisWorkbook.Worksheets("users") '<== adapt it to your needs
With ws
Set oldUserRng = .Columns(1).Resize(.Rows.Count - 1).Offset(1).SpecialCells(xlCellTypeConstants, xlTextValues)
Set newUserRng = .Columns(3).Resize(.Rows.Count - 1).Offset(1).SpecialCells(xlCellTypeConstants, xlTextValues)
Set reportRng = .Range("F1:I1") '<== added one report column to account for unchanged functions
End With
reportRng.Value = Array("user", "deleted", "new", "same")
iReport = 1
For Each cell In oldUserRng
With cell
oldFunc = .Offset(, 1).Value
Set funcCell = FindAndOffset(newUserRng, .Value, 1)
If funcCell Is Nothing Then
reportRng.Offset(iReport) = Array(.Value, "", "", oldFunc)
Else
newFunc = funcCell.Value
If newFunc = oldFunc Then
reportRng.Offset(iReport) = Array(.Value, "", "", newFunc)
Else
reportRng.Offset(iReport) = Array(.Value, oldFunc, newFunc, "")
End If
End If
iReport = iReport + 1
End With
Next cell
For Each cell In newUserRng
With cell
Set funcCell = FindAndOffset(oldUserRng, .Value, 1)
If funcCell Is Nothing Then
reportRng.Offset(iReport) = Array(.Value, "", .Offset(, 1).Value, "")
iReport = iReport + 1
End If
End With
Next cell
End Sub
Not so sure it does what you need.
you'd better provide screenshots of "before" and "after" scenarios.
BTW, is it safe to assume that both old and new user columns cannot hold duplicates (i.e.: two or more "userX" in column A and/or column C?)
But it does speed up thing considerably since it iterates only through non empty cells.
I hope I get what you want to achieve. Does the following solve your problem?
Private Sub CommandButton2_Click()
Dim rngCell As Range
For Each rngCell In Range("A2:A20000")
If WorksheetFunction.CountIf(Range("C2:C20000"), rngCell) > 0 Then
Range("F" & Rows.Count).End(xlUp).Offset(1) = rngCell
Range("F" & Rows.Count).End(xlUp).Offset(0, 1) = rngCell.Offset(0, 1).Value
Range("F" & Rows.Count).End(xlUp).Offset(0, 2) = Application.WorksheetFunction.VLookup(rngCell.Value, Range("C2:D20000"), 2, 0)
ElseIf (rngCell <> "") Then
Range("F" & Rows.Count).End(xlUp).Offset(1) = rngCell
Range("F" & Rows.Count).End(xlUp).Offset(0, 1) = rngCell.Offset(0, 1).Value
End If
Next
For Each rngCell In Range("C2:C20000")
If (WorksheetFunction.CountIf(Range("A2:A20000"), rngCell) = 0 And rngCell <> "") Then
Range("F" & Rows.Count).End(xlUp).Offset(1) = rngCell
Range("F" & Rows.Count).End(xlUp).Offset(0, 2) = rngCell.Offset(0, 1).Value
End If
Next
End Sub
A user is only included in column F when he appears both in columns A and C.In case you want to include every user that is either in column A or C the code has to be altered.
I am a noob in vba. However, I would like to implement the following use case to make my cooperate life a lot of easier.
I have the following data:
I would like to copy the first row down until I hit a filled field, the second row also down until I hit a filled field, the third row up and down and the 4th row up.
This is how I would like to have my result sheet should look like.
Any recommendation how to implement this use case in vba?
I appreciate your replies!
can you please try this one? This routine is assuming column G as main start point and checks if column A,B,C,D is empty or not in the same row and fill it up accordingly.
Sub ASD()
Dim lastRow As Long
lastRow = Range("G" & Rows.Count).End(xlUp).Row
For Each c In Range("G:G")
If c.Value <> "" Then
If c.Offset(0, -3).Value = "" Then
c.Offset(0, -3).Value = c.Offset(0, -3).End(xlDown).Value
End If
If c.Offset(0, -4).Value = "" Then
c.Offset(0, -4).Value = c.Offset(0, -4).End(xlUp).Value
End If
If c.Offset(0, -5).Value = "" Then
c.Offset(0, -5).Value = c.Offset(0, -5).End(xlUp).Value
End If
If c.Offset(0, -6).Value = "" Then
c.Offset(0, -6).Value = c.Offset(0, -6).End(xlUp).Value
End If
End If
Next c
End Sub
Only problem is it do not do anything if the reference cell is empty in the column G. I think you don't need that anyway.
Hope this help.
It will run on your current selection
Copy the whole code into a module, run the fill_down() to fill down, fill_up() to fill up.
'======================
'******Filling*********
'======================
Sub fill_up()
Selection.SpecialCells(xlCellTypeBlanks).Select
Selection.FormulaR1C1 = "=R[1]C"
End Sub
Sub fill_down()
Call copy_last(Selection)
Call filling
End Sub
Function copy_last(r As range)
Dim arr() As Variant
Dim x As Double
Dim arr_size As Double
arr = r
arr_size = UBound(arr, 1)
For x = arr_size To 1 Step -1
If Not isempty(arr(x, 1)) Then
Exit For
End If
Next x
r(r.Row, 1) = arr(x, 1)
End Function
Function filling()
Selection.SpecialCells(xlCellTypeBlanks).Select
Selection.FormulaR1C1 = "=R[-1]C"
End Function
'=======================
'******End filling******
'=======================
I have opened a new question after people have helped me figure out how to get my first problem fixed, now I have a new problem. You will find the coding on the "INITIATING DEVICES" page below. The idea for the second to last part of the code was to add columns (text values) from B7:b and E7:E and display it on column J7:J. So if Photo is entered into B and Pass is entered into column E then the result will be Photopass in column J. The original code works fine if run via the macro command. The problem is that I tried to add it into some of my existing code and now the whole entire program will crash when information is entered into ANY cell. The program becomes unresponsive and then it shuts down and starts up again. I don't get a code or Debug message. Here is the entire code on the page.
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Column = 7 And UCase(Target.Value) = "YES" Then
Sheets("MESSAGE CHANGES").Cells(Rows.Count, 1).End(xlUp)(2).Resize(, 3) = Sheets("INITIATING DEVICES").Cells(Target.Row, 1).Resize(, 3).Value
Application.Goto Sheets("MESSAGE CHANGES").Cells(Rows.Count, 1).End(xlUp).Offset(, 3)
End If
If Target.Column = 6 And UCase(Target.Value) = "YES" Then
Sheets("DEVICE NOTES").Cells(Rows.Count, 1).End(xlUp)(2).Resize(, 3) = Sheets("INITIATING DEVICES").Cells(Target.Row, 1).Resize(, 3).Value
Application.Goto Sheets("DEVICE NOTES").Cells(Rows.Count, 1).End(xlUp).Offset(, 3)
End If
'(replace if new code fails)If Target.Column = 5 And UCase(Target.Value) = "FAIL" Or Target.Column = 5 And UCase(Target.Value) = "DAMAGED" Then
'(replace if new codes fails)Sheets("FAILED DEVICES").Cells(Rows.Count, 1).End(xlUp)(2).Resize(, 3) = Sheets("INITIATING DEVICES").Cells(Target.Row, 1).Resize(, 3).Value
If Target.Column = 5 And UCase(Target.Value) = "FAIL" Or UCase(Target.Value) = "DAMAGED" Then
Application.EnableEvents = False
Sheets("FAILED DEVICES").Cells(Rows.Count, 1).End(xlUp)(2).Resize(, 5) = Sheets("INITIATING DEVICES").Cells(Target.Row, 1).Resize(, 5).Value
Sheets("FAILED DEVICES").Cells(Rows.Count, 1).End(xlUp).Offset(, 5) = Sheets("INITIATING DEVICES").Cells(Target.Row, 11).Value
Application.EnableEvents = True
End If
'code that will place date/time when value is selcted in E
If Not Intersect(Target, Range("E:E")) Is Nothing Then
Range("I" & Target.Row).Value = Now
End If
Dim wb As Workbook
Dim ws As Worksheet
Dim lastRow As Long
Set wb = ThisWorkbook
Set ws = wb.Sheets("INITIATING DEVICES")
lastRow = ws.Range("B" & ws.Rows.Count).End(xlUp).Row
ws.Range("J7:J" & lastRow).Value = Evaluate("=B7:B" & lastRow & "&E7:E" & lastRow)
End Sub
Private Sub Workbook_BeforePrint(Cancel As Boolean)
With Sheets("INITIATING DEVICES")
.PageSetup.PrintArea = .Range("A1:H" & .Cells(Rows.Count, 1).End(xlUp).Row).Address
End With
End Sub
Thank you for any help that you can give me, if you need anymore information I can send the inspection file and also offer more information if needed. I'm sure I messed something up and that's why it keeps crashing. Still learning here.
IF it is crashing then the Change event is being called repeatedly. You need to use
Application.EnableEvents = False
at an appropriate point to prevent the Change event being triggered again, as you make changes (with your code) to the worksheet.
I'm attempting to create an inventory system at my work as the only software we have as Excel. Basically we have a Work order sheet that we enter the repairs as well as parts used on. I made a code that would pull the inserted part numbers & descriptions out of the individual work orders to keep track of everything used, but my boss wants me to create a system that will allow us to start typing the name/part number of something and have it guess or fill in for us. Hence where the combobox comes in.
I got It working up to a point. The lists are populated with the part inventory (we have a master EXTNERAL file listing) but my issue is this:
When you click the "add part" of the user form, I can't figure out how to have the parts be added in a certain range on the Work Order. All the tutorials I've been following here and here only have it set up to add the parts in order of the column. Can anybody look at my (terrible, I'm sorry) coding and see if they can help?
Private Sub UserForm_Initialize()
Dim cPart As Range
Dim cNum As Range
Dim ws As Workbook
'Dim ComboBox1 As Variant
Application.ScreenUpdating = False
Set ws = Workbooks.Open("\\Capserver\iso maintenance\CAPS MASTER PARTS & PRICE LIST 2012.xls")
Windows("CAPS MASTER PARTS & PRICE LIST 2012.xls").Visible = False
'ws.Sheets("CAPS ORDER FORM").Range("Name") = Sheet1.ComboBox1
'ComboBox1.Clear
For Each cPart In ws.Sheets("CAPS ORDER FORM").Range("Name")
With Me.cboPart
.AddItem cPart.Value
End With
Next cPart
For Each cNum In ws.Sheets("CAPS ORDER FORM").Range("Number")
With Me.cboNum
.AddItem cNum.Value
.List(.ListCount - 1, 1) = cNum.Offset(0, 1).Value
End With
Next cNum
End Sub
Private Sub cmdAdd_Click()
Dim lRow As Range
Dim lPart As Long
Dim ws As Worksheet
Dim something As Variant
Dim box As Object
Set ws = Worksheets("Sheet2")
With Worksheets(1).Range("A1:a500")
Set lRow = .Find(What:="", SearchOrder:=xlRows, SearchDirection:=xlNext, LookIn:=xlValues)
End With
'Set lRow = Range("A1")
' If VBA.IsEmpty(lRow.Value) Then
' MsgBox ("POOP!")
' Else
' Set box = lRow.End(xlDown)
' End If
'lRow = Worksheets("Sheet2").Range("A33:A37")
'ws.Cells.Find(What:="*", SearchOrder:=xlRows, (From tutorial, always returned lRow = Nothing)
' SearchDirection:=xlPrevious, LookIn:=xlValues).Row 1
lPart = Me.cboPart.ListIndex
'check for a part number
If Trim(Me.cboPart.Value) = "" Then
Me.cboPart.SetFocus
MsgBox "Please enter a part name or number"
Exit Sub
End If
'copy the data to the database
'use protect and unprotect lines,
' with your password
' if worksheet is protected
With ws
' .Unprotect Password:="password"
.Cells(lRow, 1).Value = Me.cboPart.Value
.Cells(lRow, 2).Value = Me.cboPart.List(lPart, 1)
.Cells(lRow, 3).Value = Me.cboNum.Value
' .Cells(lRow, 4).Value = Me.txtDate.Value
.Cells(lRow, 5).Value = Me.txtQty.Value
' .Protect Password:="password"
End With
'Combobox1.linkedcell=C4
'clear the data
Me.cboPart.Value = ""
Me.cboNum.Value = ""
Me.txtQty.Value = ""
Me.cboPart.SetFocus
End Sub
Private Sub cmdClose_Click()
Unload Me
End Sub
The goal is to be able to click the "Add part" button and add multiple files and have it output to the work order (I think the range for the parts is A33:A55 or something similar)
I ALSO would like to know if there is a way to make BOTH the part name AND part numbers dependent in the UserForm depending on which one you enter? Though that is a lower priority.
I'm still not 100% on what you are trying to do, and you may have some sections commented out that you may want to use (ie the combobox). Your 1row range is finding the next empty cell which I don't think you want to do. But as for how you would input the information into a range, you probably need to change your ws With statement:
1row = 35 'or whatever row number
For n =0 to CountOfItemsToAdd 'could also be done with a For Each statement
'You will also need another for statement here to go through your part list
With ws
1row = 1row + (n*3)
' .Unprotect Password:="password" [you only need this if using passwords]
.Cells(lRow, 1).Value = Me.cboPart.Value
.Cells(lRow+1, 1).Value = Me.cboPart.List(lPart, 1)
.Cells(lRow+2, 1).Value = Me.cboNum.Value
' .Cells(lRow, 4).Value = Me.txtDate.Value
.Cells(lRow+3, 1).Value = Me.txtQty.Value
' .Protect Password:="pasword"
But to awnser your specific question, you change the first value in the .Cells reference which is the row index, not the second number, which is the column index.