I want to automatically update sheet 2 & sheet 3 from sheet 1 as per data. It will be helpful if it gets resolved.
I am new to visual basic coding so unaware of its script but aware of c++. Whatever the solution may be it will be helpful.
Screen shot of the three sheets - I was unable to upload the Excel files
I have tried the following code:
Sub FindMatches()
Dim oldrow As Integer
Dim newrow As Integer
For oldrow = 4 To 14
For newrow = 3 To 20
If Cells(oldrow, 12) = Cells(1, newrow) And Cells(oldrow, 13) = Cells(newrow, 1) Then 'date and brand
If Cells(1, 14) = Cells(newrow, 2) Then
Cells(newrow, 3).Value = Cells(oldrow, 14).Value ' m1
End If
If Cells(1, 15) = Cells(newrow + 1, 2) Then
Cells(newrow + 1, 3).Value = Cells(oldrow, 15).Value ' m2
End If
If Cells(1, 16) = Cells(newrow + 2, 2) Then
Cells(newrow + 2, 3).Value = Cells(oldrow, 16).Value ' m3
End If
If Cells(1, 17) = Cells(newrow + 3, 2) Then
Cells(newrow + 3, 3).Value = Cells(oldrow, 17).Value ' issue
End If
If Cells(1, 18) = Cells(newrow + 4, 2) Then
Cells(newrow + 4, 3).Value = Cells(oldrow, 18).Value ' repack
End If
If Cells(1, 19) = Cells(newrow + 5, 2) Then
Cells(newrow + 5, 3).Value = Cells(oldrow, 19).Value ' extra
End If
If Cells(1, 20) = Cells(newrow + 6, 2) Then
Cells(newrow + 6, 3).Value = Cells(oldrow, 20).Value ' wastage
End If
End If
Next newrow
Next oldrow
End Sub
Maybe you even don't need VBA to update the data. You can simply enter a formula =C2 into a cell to reference (and retrieve the data from) a cell.
You also can reference cells from other sheets like this =Sheet1!C2.
And you can use Functions such as IF to do more complex cases and logic.
You just cant use Cells without sheet referencing. Because excel dont know which sheet you wana to use and assuming active sheet. So you need to you ActiveSheet.cells() and switching active sheet (but its not highly recommended). Instead use sheet declaration like this
Dim myLovelySheet as worksheet
Set mylovelySheet = Sheets("yourCuteSheetname")
and then you can work with sheet like with object (you will be familiar to it from C++)
myLovelySheet.cells()
or you can perform multiple operations on sheet with construction like this
with myLovelySheet
.cells()
.cells()
'etc
end with
Basicaly your approach is almost correct, but try study more code. I can recommend this which have multiple examples with good practise
Related
Private Sub CommandButton1_Click()
Dim nbp As Long
Dim i As Long
Dim p As Long
Dim FV As Variant
Dim CS As Variant
Dim K As Variant
Dim iFV As Integer
Dim iCS As Double
If Range("B9") = "Semi-Annual" Then
p = DateDiff("yyyy", Cells(4, 3), Cells(5, 3))
nbp = p * 2
For i = 5 To nbp + 4
Cells(5, 10).Value = Cells(4, 3).Value
Cells(i + 1, 10).Value = DateAdd("m", 6, Cells(i, 10).Value)
Next i
For i = 6 To nbp + 5
Cells(i, 14).Value = Cells(7, 2).Value * (Cells(8, 2).Value / 2)
Next i
FV = Sheet2.Range("J5:J10").Value
CS = Sheet3.Range("F1:G8000").Value
For iFV = 1 To UBound(FV)
For iCS = 1 To UBound(CS, 2)
If FV(iFV, 1) = CS(iCS, 1) Then
K(iFV, 1) = CS(iCS, 2)
End If
Next
Next
Sheet2.Range("K5:K10").Value = K
End If
End If
If Range("B9") = "Annual" Then
nbp = DateDiff("yyyy", Cells(4, 3), Cells(5, 3))
For i = 5 To nbp + 4
Cells(5, 10).Value = Cells(4, 3).Value
Cells(i + 1, 10).Value = DateAdd("m", 12, Cells(i, 10).Value)
Next i
End if
If Range("B9") = "Quarterly" Then
p = DateDiff("yyyy", Cells(4, 3), Cells(5, 3))
nbp = p * 4
For i = 5 To nbp + 4
Cells(5, 10).Value = Cells(4, 3).Value
Cells(i + 1, 10).Value = DateAdd("m", 3, Cells(i, 10).Value)
Next i
End If
If Range("B9") = "Monthly" Then ' to choose from a list .
p = DateDiff("yyyy", Cells(4, 3), Cells(5, 3))
nbp = p * 12
For i = 5 To nbp + 4
Cells(5, 10).Value = Cells(4, 3).Value
Cells(i + 1, 10).Value = DateAdd("m", 3, Cells(i, 10).Value)
Next i
End If
End Sub
I have added all the code in the button to help. i am not sure if that will help, anyway here is it. if the user chooses semi annual then couple of things take place. Same goes for the rest "ifs" but i need to fix this issue first then move on to the rest. the code to too long, it is simple and not complicated.
Now that more of the code is posted, I think I understand what the problem is.
Wherever you reference Cells() VBA assumes it applies to ActiveSheet. And I think you should fully qualify the calls to be Sheet2.Cells() for example or whatever you need.
When you call the code behind a button, the button resides on a sheet and it references the cells on that sheet. But when you moved the code to a module it no longer referenced the sheet with the button, but whatever other sheet was active at the time.
So whenever you see Cells() or Range() without a worksheet specification in front of it, change it so that it you target a specific worksheet.
PS. Avoid using Integer and prefer Long instead. Also, prefer relative referencing such as Sheet2.Range("G2").Cells(i,j) instead of absolute referencing Sheet2.Cells(1+i, 6+j) or string math such as Sheet2.Range("G" & 1+i & ":G" & 5+i).
I have 30 excel files need to be done with the same operation. I have non-fixed number of rows in my excel files. For all excel files, I want to apply VBA to write IF function into the column"H" from the second row to the last row.
Here is the formula I need to write (for the second row): =IF(AND(C2=C3,C2=C1,G2=TRUE,G1=FALSE,G3=TRUE),"O",IF(AND(C2=C3,C2<>C1,G2=TRUE),"FromYes",IF(AND(C2=C1,C2=C3,G2=TRUE,G1=TRUE,G3=FALSE),"D",IF(AND(C2=C1,C2<>C3,G2=TRUE),"ToTmr",""))))
Other rows shall have similar formula.
Is there any way for me to write a formula directly in vba for the whole column "H"?
I have tried some code like this, but it run extremely slow
For x = 2 To lastRow
If ws.Cells(x, 3) = ws.Cells(x + 1, 3) And ws.Cells(x, 3) = ws.Cells(x - 1, 3) And ws.Cells(x, 7) = True And ws.Cells(x - 1, 7) And ws.Cells(x + 1, 7) = True Then
ws.Cells(x, 8) = "O"
Else
If Cells(x, 3) = Cells(x + 1, 3) And (Cells(x, 3) <> Cells(x - 1, 3)) And Cells(x, 7) = True Then
Cells(x, 8) = "FromYes"
Else
If Cells(x, 3) = Cells(x - 1, 3) And Cells(x, 3) = Cells(x + 1, 3) And Cells(x, 7) = True And Cells(x - 1, 7) = True And Cells(x + 1, 7) = False Then
Cells(x, 8) = "D"
Else
If Cells(x, 3) = Cells(x - 1, 3) And Cells(x, 3) <> Cells(x + 1, 3) And Cells(x, 7) = True Then
Cells(x, 8) = "ToTmr"
End If
End If
End If
End If
Next x
Next d
There are a few things to change in your approach. Firstly, use the fact that you can set cell formulas with VBA. Secondly, use the fact that you can set cell formulas for an entire range in VBA. Thirdly, if your formula is fast, use it instead of code (this is not a hard and fast rule, but if you're new to VBA or not using it often, it's simpler to use formulas).
Using those ideas, I came up with this:
With Sheet1
.Range(.Cells(2, 8), .Cells(lastRow, 8)).FormulaR1C1 = _
"=IF(AND(RC[-5]=R[1]C[-5],RC[-5]=R[-1]C[-5],RC[-1],NOT(R[-1]C[-1]),R[1]C[-1]),""O""," & _
"IF(AND(RC[-5]=R[1]C[-5],RC[-5]<>R[-1]C[-5],RC[-1]),""FromYes""," & _
"IF(AND(RC[-5]=R[1]C[-5],RC[-5]=R[-1]C[-5],RC[-1],R[-1]C[-1],NOT(R[1]C[-1])),""D""," & _
"IF(AND(RC[-5]<>R[1]C[-5],RC[-5]=R[-1]C[-5],RC[-1]),""ToTmr"",""""))))"
End With
This is setting the formula for the range of column H, rows 2 through the lastRow, for Sheet1 equal to your formula. The removes the need to loop rows, making it much quicker.
I always find it is easier to work with R1C1 format when entering formulas through VBA. It's easy enough to get that format; you just need to go the formula options in Excel and select R1C1 Reference Style. Then you can copy it into the VBA and escape quotation marks with another quotation mark.
Using this, you can easily write a tool that loops through the files, finds the last row on the appropriate sheet, and sets the formula.
The macro below opens a series of workbooks from a list, then copies some data from them. It works fine for the first workbook, then crashes on the second. I've tried changing the order, and it's always the second workbook that causes it to crash.
Sub ImportData()
Dim lastRow As Long
Dim lastSumRow As Long
Dim j As Long
Dim k As Long
With ActiveSheet
lastRow = ActiveSheet.Cells(1048576, 1).End(xlUp).Row
End With
For k = 2 To lastRow
k = 2
lastUsedRow = ThisWorkbook.Sheets("Summary").Cells(1048576, 1).End(xlUp).Row
If ActiveSheet.Cells(k, 2).Value <> "Imported" Then
Workbooks.Open Filename:=ThisWorkbook.Path & "\Analysis\" & Cells(k, 1), UpdateLinks:=False
ActiveWorkbook.Sheets("Summary").Activate
For j = 3 To 100
If j Mod 3 = 0 Then
ThisWorkbook.Sheets("Summary").Cells((j / 3) + lastUsedRow, 1).Value = ActiveWorkbook.Sheets("Summary").Cells(j, 1).Value
ThisWorkbook.Sheets("Summary").Cells((j / 3) + lastUsedRow, 2).Value = ActiveWorkbook.Sheets("Summary").Cells(j + 1, 2).Value
ThisWorkbook.Sheets("Summary").Cells((j / 3) + lastUsedRow, 3).Value = ActiveWorkbook.Sheets("Summary").Cells(j + 1, 3).Value
ThisWorkbook.Sheets("Summary").Cells((j / 3) + lastUsedRow, 4).Value = ActiveWorkbook.Sheets("Summary").Cells(j + 1, 4).Value
ThisWorkbook.Sheets("Summary").Cells((j / 3) + lastUsedRow, 5).Value = ActiveWorkbook.Sheets("Summary").Cells(j + 2, 2).Value
ThisWorkbook.Sheets("Summary").Cells((j / 3) + lastUsedRow, 6).Value = ActiveWorkbook.Sheets("Summary").Cells(j + 2, 3).Value
ThisWorkbook.Sheets("Summary").Cells((j / 3) + lastUsedRow, 7).Value = ActiveWorkbook.Sheets("Summary").Cells(j + 2, 4).Value
ThisWorkbook.Sheets("Summary").Cells((j / 3) + lastUsedRow, 8).Value = ActiveWorkbook.Sheets("Summary").Cells(j + 1, 5).Value
End If
Next j
ActiveWorkbook.Close
End If
ThisWorkbook.Sheets("Setup").Cells(k, 2).Value = "Imported"
Next k
End Sub
I'm guessing your error is here:
Workbooks.Open Filename:=ThisWorkbook.Path & "\Analysis\" & Cells(k, 1), UpdateLinks:=False
'Ooops ^^^^^
The .Activate and .Select calls are convoluted enough that I'm not really going to expend the effort figuring out what should be the active worksheet at that particular point in your code on the second run through the loop. Whatever it is, it's different than it was when you started and an unqualified call to Cells implicitly refers to whatever worksheet is the ActiveSheet at the time. This builds a bad file name (or fails completely) and then the wheels come off.
The best thing to do is not use the Active* objects at all. Get references to the objects that you're using, and well, use them. That way there is no chance that you'll get wires crossed. While you're at it, you can give them names that make it obvious what you're working with at a glance.
Couple other things before we get to the code that doesn't use Activate and Select.
lastSumRow is never used and lastUsedRow is never declared. I'm assuming they were supposed to be the same thing. You should put Option Explicit at the top of your modules to avoid this type of error (and worse ones).
These 2 lines of code make very little sense together:
For j = 3 To 100
If j Mod 3 = 0 Then
If you only want to copy every 3rd row, skip all the division and just increment your loop counter with a Step of 3:
For j = 3 To 99 Step 3
Note that you can stop at 99, because 100 Mod 3 is never going to be 0.
Your With block here isn't using the captured reference...
With ActiveSheet
lastRow = ActiveSheet.Cells(1048576, 1).End(xlUp).Row
End With
...but you continually use this pattern that would be useful in a With block:
ThisWorkbook.Sheets("Summary").Cells((j / 3) + lastUsedRow, 1).Value = ...
ThisWorkbook.Sheets("Summary").Cells((j / 3) + lastUsedRow, 2).Value = ...
ThisWorkbook.Sheets("Summary").Cells((j / 3) + lastUsedRow, 3).Value = ...
Hard-coding Cells(1048576, 1) will fail on older versions of Excel. You should use Rows.Count instead.
As mentioned in the comments, k = 2 creates an infinite loop.
You don't need to repeatedly find the last row of the sheet you're copying to with this code:
lastUsedRow = ThisWorkbook.Sheets("Summary").Cells(1048576, 1).End(xlUp).Row
Each time you go through your "j" loop, the last row increases by one. Just add 1 to lastUsedRow instead of doing all the row counting gymnastics.
If you're working with Worksheets, use the Worksheets collection instead of the Sheets collection:
ThisWorkbook.Sheets("Summary") '<--I could return a Chart!
Put all of that together, and you come up with something like the code below. Note that I have no clue what the ActiveSheet is supposed to be when you start this macro, so I just named the variable it's stored in active. It's quite possible that it's one of the other worksheets it grabs a reference too (I have no clue) - if so, you should consolidate them into one reference:
Public Sub ImportData()
Dim lastRow As Long
Dim lastUsedRow As Long
Dim dataRow As Long
Dim fileNameRow As Long
Dim active As Worksheet
Set active = ActiveSheet
With active
lastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
End With
Dim setupSheet As Worksheet
Set setupSheet = ThisWorkbook.Worksheets("Setup")
With ThisWorkbook.Worksheets("Summary")
lastUsedRow = .Cells(.Rows.Count, 1).End(xlUp).Row
For fileNameRow = 2 To lastRow
If active.Cells(fileNameRow, 2).Value <> "Imported" Then
Dim source As Workbook
Set source = Workbooks.Open(ThisWorkbook.Path & "\Analysis\" & _
active.Cells(fileNameRow, 1), False)
Dim dataSheet As Worksheet
Set dataSheet = source.Worksheets("Summary")
For dataRow = 3 To 99 Step 3
.Cells(lastUsedRow, 1).Value = dataSheet.Cells(dataRow, 1).Value
.Cells(lastUsedRow, 2).Value = dataSheet.Cells(dataRow + 1, 2).Value
.Cells(lastUsedRow, 3).Value = dataSheet.Cells(dataRow + 1, 3).Value
.Cells(lastUsedRow, 4).Value = dataSheet.Cells(dataRow + 1, 4).Value
.Cells(lastUsedRow, 5).Value = dataSheet.Cells(dataRow + 2, 2).Value
.Cells(lastUsedRow, 6).Value = dataSheet.Cells(dataRow + 2, 3).Value
.Cells(lastUsedRow, 7).Value = dataSheet.Cells(dataRow + 2, 4).Value
.Cells(lastUsedRow, 8).Value = dataSheet.Cells(dataRow + 1, 5).Value
lastUsedRow = lastUsedRow + 1
Next
source.Close
End If
setupSheet.Cells(fileNameRow, 2).Value = "Imported"
Next
End With
End Sub
I received many Excel files from a client.
Their system extracted the data into a spreadsheet, but one column is having issues. If the text was too long, it would put the remaining text into the cell below it.
This causes all the other fields in that row to be blank, except for the overflow.
How can I merge cells at issue into one for all files I received?
I uploaded a screen shot of the file as an example. Notice on row 8 that H8 is the only cell. That needs to be merged with H7. Not every row is at issue though.
asuming A is the main (and empty for doubles)
asuming H holds the text
then in L1 and copy down
=H1&IF(LEN(A2),H2,"")
simplest way (then copy values from L to H and delete empty lines (simply with filter)
when having unknown number of lines (after splitting) you better use vba (or simply repeat the whole procedure till there no empty lines anymore...
doing it in VBA:
Sub testing()
Dim i As Long
While Len(Cells(i + 1, 8))
i = i + 1
While Len(Cells(i + 1, 1)) = 0 And Len(Cells(i + 1, 8))
Cells(i, 8) = Cells(i, 8) & Cells(i + 1, 8)
Rows(i + 1).Delete
Wend
Wend
End Sub
most programs skip spaces so you may want to use:
=H1&IF(LEN(A2)," "&H2,"")
or for vba change Cells(i, 8) = Cells(i, 8) & Cells(i + 1, 8) to Cells(i, 8) = Cells(i, 8) & " " & Cells(i + 1, 8)
This will concatenate the texts in H and delete the row that is not useful :
Sub test_bm11()
Dim wS As Worksheet, _
LastRow As Long, _
i As Long
Set wS = ActiveSheet
With wS
LastRow = .Range("H" & .Rows.Count).End(xlUp).Row
For i = LastRow To 2 Step -1
If .Cells(i, "A") <> vbNullString Then
Else
.Cells(i, "H").Offset(-1, 0) = .Cells(i, "H").Offset(-1, 0) & .Cells(i, "H")
.Cells(i, "H").EntireRow.Delete
End If
Next i
End With
End Sub
I cannot seem to solve this VBA riddle I've been working on, please help. I'm new at this and I'm probably over complicating it
Essentially, there are two worksheets - one titled Master and the other will be created fresh daily by date. The Master tab contains 10000 rows of historical data filled from Columns A:X. The other tab generally has about 300 rows of fresh data and also contains like Columns A:X, only with blank cells in Columns A:B. I'm trying to find matches with the master tab, and if so, populate the corresponding results in cells A and B from the master to the daily. If nothing, leave blank. It is crucial that Cells H:M and R:W are identical matches.
Below is my crazy attempt, Thank you in advance for helping
Sub Previous()
Dim u As Long
u = 2
Do While ActiveSheet.Cells(u, 6) <> ""
Dim i As Long
i = 2
Do While Worksheets("Master").Cells(i, 6) <> ""
If ActiveSheet.Range(Cells(u, 8), Cells(u, 13)) _
= Worksheets("Master").Range(Cells(i, 8), Cells(i, 13)) _
And ActiveSheet.Range(Cells(u, 18), Cells(u, 23)) _
= Worksheets("Master").Range(Cells(i, 18), Cells(i, 23)) _
And ActiveSheet.Cells(u, 2) = "" Then
ActiveSheet.Range(Cells(u, 1), Cells(u, 2)) _
= Worksheets("Master").Range(Cells(i, 1), Cells(i, 2))
Else: i = i + 1
End If
Loop
u = u + 1
i = 2
Loop
End Sub
First of all, I don't believe this snippet does what you think it does.
Worksheets("Master").Range(Cells(i, 8), Cells(i, 13))
In that snippet Cells(i,8) references the ActiveSheet, not Sheets("Master").
There is a note on this about halfway down the page on msdn's Range Object documentation.
You can simplify your code a great deal by assigning some worksheet variables.
dim actWs as Worksheet
dim mstWs as Worksheet
Set actWs = Activesheet
Set mstWs = Sheets("Master")
'then reference your ranges like this
mstWs.Cells(i,8)
But, that's not what is causing your runtime error.
Simply put, you can not compare ranges that way. You need to check the value of each cell, so you end up with another layer of nested loops.
dim u as long ' active sheet row counter
dim i as long ' master sheet row counter
dim c as long ' column counter
For u = 2 to actWs.Range("A" & .Rows.Count).End(xlUp).Row 'find last row in column "A" of active sheet
For i = 2 to mstWs.Range("A" & .Rows.Count).End(xlUp).Row 'find last row in column "A" of master sheet
For c = 8 to 13
If actWs.Cells(i,c) = mstWs.Cells(i,c) Then
'Do stuff
End if
next c 'next column
next i 'next master sheet row
next u 'next active sheet row
This is obviously a simplified version of what you'll need to do. Be careful of line continuations (" _ ") and code indentation. It's easy to trick yourself into thinking your program should flow in a way that it isn't. It would be advisable to store the value's you're checking for equality in variables to make it easier to read. You might more readily notice where you're going wrong.
Sub Previous()
Dim actWs As Worksheet
Set actWs = ActiveSheet
Dim mstWs As Worksheet
Set mstWs = Sheets("Master")
Dim u As Long
Dim i As Long
u = 2
Do While actWs.Cells(u, 6) <> ""
For i = 2 To mstWs.Range("C" & Rows.Count).End(xlUp).Row
If actWs.Cells(u, 8) = mstWs.Cells(i, 8) And actWs.Cells(u, 9) = mstWs.Cells(i, 9) And actWs.Cells(u, 10) = mstWs.Cells(i, 10) And actWs.Cells(u, 11) = mstWs.Cells(i, 11) And actWs.Cells(u, 12) = mstWs.Cells(i, 12) And actWs.Cells(u, 13) = mstWs.Cells(i, 13) And actWs.Cells(u, 18) = mstWs.Cells(i, 18) And actWs.Cells(u, 19) = mstWs.Cells(i, 19) And actWs.Cells(u, 20) = mstWs.Cells(i, 20) And actWs.Cells(u, 21) = mstWs.Cells(i, 21) And actWs.Cells(u, 22) = mstWs.Cells(i, 22) And actWs.Cells(u, 23) = mstWs.Cells(i, 23) Then
mstWs.Select
Range(Cells(i, 1), Cells(i, 2)).Select
Selection.Copy
actWs.Select
Range(Cells(u, 1), Cells(u, 2)).Select
actWs.Paste
End If
Next i
u = u + 1
Loop
End Sub