I have a macro that builds charts on one sheet from data in other sheets in the same book. Currently, it compiles but stops on a countifs statement that looks at dates on one of the other sheets.
The offending line is:
thisBook.rSheet.Range(i, "T") = Application.WorksheetFunction.CountIfs( _
thisBook.sSheet.Cells("K2", "K" & sSheet.Cells(Rows.Count, "K").End(xlUp).Row), "APPROVED", _
thisBook.sSheet.Cells("M2", "M" & sSheet.Cells(Rows.Count, "M").End(xlUp).Row), ">=" & (rSheet.Range(i, "S").Value2 - 6), _
thisBook.sSheet.Cells("M2", "M" & sSheet.Cells(Rows.Count, "M").End(xlUp).Row), "<=" & (rSheet.Range(i, "S").Value2))
The statement is intended to count rows on another sheet where the text in column K equals "APPROVED" and has a date in column M within a specific week . i loops through the 8 rows of this table. Column S is the date that week ends.
Everything has been fully referenced to avoid
runtime error 1004
thisBook is the workbook, rSheet is the sheet with the charts, sSheet is the sheet with the table to evaluate.
Which object doesn't support the property or method here?
Try this, slightly tweaked to make it easier (I hope) to read/follow:
Dim outputRng As Range
Dim rSheetVal as Date
Set outputRng = rsheet.Range(i, "T")
rSheetVal = rsheet.Cells(i, "S").Value2 ' OR, rsheet.Range("S"&i).Value2
With thisBook.sSheet
outputRng = Application.WorksheetFunction.CountIfs( _
.Range("K2", "K" & .Cells(Rows.Count, "K").End(xlUp).Row), "APPROVED", _
.Range("M2", "M" & .Cells(Rows.Count, "M").End(xlUp).Row), ">=" & (rSheetVal-6), _
.Range("M2", "M" & .Cells(Rows.Count, "M").End(xlUp).Row), "<=" & (rSheetVal))
End With
Assuming the thisBook and rsheet are set like:
Set thisBook = ActiveWorkbook
Set rSheet = thisBook.Sheets("r Sheet")
Related
I am working on ws1 which is a worksheet in "New" workbook. it contains Bill No in column 1 and other relevant information in other columns.
I want to Vlookup in another workbook called "PDA" and in that worksheet with sheet name "Sheet1" which has Bill no in Column F and the data that I want to copy in column G. If the Bill No is not present in PDA>Sheet1, then I want to return the value 0, because I have to sum the amount later.
I have written this code, but it is giving error. Kindly help me with the issue.
I am getting error in this line
.Cells(i, 4) = "=If(ISNA(VLookup(.Cells(i, 1).Value2, y, 2,
False)),0,VLookup(.Cells(i, 1).Value2, y, 2, False))"
Dim i As Long
Dim x As Range
Dim b As Range
Dim wb1 As Workbook
Dim ws1 As Worksheet
Set wb1 = Workbooks.Open("C:\Users\mrisingh\Desktop\6-Eccs Billing List.xlsx")
Set x = wb1.Worksheets("Billing Sheet").Range("B2:E100000")
Set ws1 = Workbooks("New").Worksheets("Reconciliation")
With ws1
For i = 2 To .Cells(Rows.Count, 1).End(xlUp).Row
.Cells(i, 3) = Application.VLookup(.Cells(i, 1).Value2, x, 4, False)
Next i
End With
Set b = wb1.Worksheets("Billing Sheet").Range("B2:K100000")
With ws1
For i = 2 To .Cells(Rows.Count, 1).End(xlUp).Row
.Cells(i, 9) = Application.VLookup(.Cells(i, 1).Value2, b, 10, False)
Next i
End With
wb1.Close savechanges:=False
Dim y As Range
Dim wb2 As Workbook
Set wb2 = Workbooks.Open("C:\Users\mrisingh\Desktop\PDA.xlsx")
Set y = wb2.Worksheets("Sheet1").Range("F845:G10000")
With ws1
For i = 2 To .Cells(Rows.Count, 1).End(xlUp).Row
.Cells(i, 4) = "=If(ISNA(VLookup(.Cells(i, 1).Value2, y, 2, False)),0,VLookup(.Cells(i, 1).Value2, y, 2, False))"
Next i
End With
wb2.Close savechanges:=False
You are mixing VBA pieces into an Excel formula - that will not work. Excel does not know what .Cells(i, 1).Value2 or y means, but that is what you write into the cell. VBA, on the other hand, doesn't replace .Cells(i, 1).Value2 or y with the range itself as is it inside a constant string (within quotes), and VBA leaves those strings alone.
So a first attempt would be to write (untested as I don't have your data, but you hopefully get the idea)
dim lookupAdr as string, f as string
lookupAdr = "[" & wb2.name & "]" & "Sheet1!" & y.address
f = "=If(ISNA(VLookup(" & .Cells(i, 1).address & ", " _
& lookupAdr & ", 2, False)),0,VLookup( " _
& .Cells(i, 1).address & ", " & lookupAdr & ", 2, False))"
Debug.Print lookupAdr
Debug.Print f
.Cells(i, 4).formula = f
Note that I write the formula and the address of the lookup first into string variables - this helps to find errors. Note also that you should write formulas always to the property .formula of a cell, not to the .value property.
However, there is room for improvement:
Unless you are still on Excel 2003, you can use the IFERROR formula so that you don't have to repeat the VLOOKUP-part. This would lead to a much easier formula (again untested):
f = "=IFERROR(VLookup( " & .Cells(i, 1).address & ", " & lookupAdr & ", 2, False), 0)"
As a general rule when you try to create a formula within VBA, it is often helpfull to go the different way first: Write the final formula that you want to have into one cell, go to the VBA-Editor, open the Immediate Window (press [Ctrl]+[G]) and enter ? activecell.formula. Then, write your code and compare the formula you create within VBA with the one you saw in the immediate window.
And, as advice: Use meaningfull variable names. y is not really a good name for a range variable - use something like lookupRange. Everyone that will read the code (and that includes you!) will be thankfull.
I am positively stuck!
I need to loop in column C of "Raw Data" Sheet till the last row and if the date value is between a starting date and an end date then copy that row to a new sheet "Week". Dates are defined by input box
inizio = InputBox("Data Inizio") 'start date
fine = InputBox("Data Fine") 'end date
Then
I store the last row of the "week" sheet with this formula borrowed from a google search (apology to the author but I cannot remember his/her name)
PriRigVuot = Worksheets("Week").Cells.Find(What:="*", _
After:=Range("A1"), _
LookAt:=xlPart, _
LookIn:=xlValues, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
and then I run into problems while tring to copy the row that match the If statement
For Each cella In Range("c1:c50")
If Worksheets("Raw Data").Range(cella).Value >= inizio And Worksheets("Raw Data").Range(cella).Value <= fine Then
Worksheets("Raw Data").Range(cella).EntireRow.Copy _
Destination:=Worksheets("Week 34").Range("A" & PriRigVuot + 1)
Else
End If
Next Cella
I am aware that this code (if it worked at all) would copy the row in the same place over and over again but I am trying to tackle a step at a time
Thanks for any help in advance
First, you can store the last row used in sheet Week like this:
LastRow = ThisWorkbook.Sheets("Week").Range("A" & Rows.Count).End(xlUp).Row
In your For loop, you dont need to specify the whole object again, since the Range is already specified when you wrote the sentence. So, the code will be like this:
Dim sh As Worksheet
Dim LastRow As Long
Set sh = ThisWorkbook.Sheets("Raw Data")
LastRow = ThisWorkbook.Sheets("Week 34").Range("A" & Rows.Count).End(xlUp).Row
For Each cella In sh.Range("C1:C" & sh.Range("C" & Rows.Count).End(xlUp).Row) 'Dynamic range in column C
If cella >= inizio And cella <= fine Then
cella.EntireRow.Copy Destination:=Worksheets("Week 34").Range("A" & LastRow + 1)
End If
Next cella
'The number rows in the database changes each month hence the variable lastCode.
'The code runs without problems when I manually put last row in i.e. $M$22510 and $O$22510 however when I put the variable & lastCode in the SUMIFS I receive the error
Run-time '1004'application-defined or object defined.
Please tell me what is wrong with my SUMIFS in the code below.
Sub SumGroups()
Worksheets("Database").Activate
Dim lastCode, lastFiltCode As Integer
Dim Formula As String
'Determine Last Row in Column O (Unfiltered Codes)
lastCode = Range("O" & Rows.Count).End(xlUp).Row
'Filter Unique Codes into Column A Sheet2
Range("O1:O" & lastCode).AdvancedFilter Action:=xlFilterCopy, _
CopyToRange:=Sheet2.Range("A1"), Unique:=True
'Determine last Row in Column A (Filtered Codes)
Worksheets("Sheet2").Activate
lastFiltCode = Sheet2.Range("A" & Rows.Count).End(xlUp).Row
'Place SUMIF Formula in Column B Sheet2
Worksheets("Sheet2").Range("B2:B" & lastFiltCode).Formula = _
"=SUMIFS(Database!$M$2:$M$ & lastCode,Database!$O$2:$O$ & lastCode,A2)"
End Sub
What you are looking for is:
"=SUMIFS(Database!$M$2:$M$" & lastCode & ",Database!$O$2:$O$" & lastCode & ",A2)"
The reason for this is when you put something into "quotation marks" VBA will read it as just text, you can use Debug.Print "=SUMIFS(Database!$M$2:$M$ & lastCode,Database!$O$2:$O$ & lastCode,A2)" and either press Ctrl + G or in VBA select "View" > "Immidiate Window" and it will show you what exactly is going into Excel
I get an error on this line in my code, any ideas what the issue may be?
Intersect(.UsedRange, .UsedRange.Offset(1)).SpecialCells(12).EntireRow.Delete
Here is the rest of the code:
Sub DefineDL_IDL()
Dim wbTHMacro As Workbook, wsRegulares As Worksheet, wsRegularesDemitidos As Worksheet, wsTempActivos As Worksheet, _
wsTempJA As Worksheet, wsTempFit As Worksheet, wsTempDemitidos As Worksheet, wsPS As Worksheet, wsResultados As Worksheet, _
wsDLList As Worksheet, wssheet As Worksheet, count_DL As Integer, count_IDL As Integer
Dim x&, r As Long
'*************REGULARES***********
Sheets("Regulares").Select
'Debug.Print xlToRight
'Sheets("Raw").Copy before:=Sheets(2)
With Sheets("Regulares")
'.Name = "Final2"
.UsedRange.AutoFilter 9, "INATIVE"
Intersect(.UsedRange, .UsedRange.Offset(1)).SpecialCells(12).EntireRow.Delete
r = WorksheetFunction.CountA(.Range("A:A"))
.UsedRange.AutoFilter
.Range("J:J").Insert xlToRight
.Range("J1").Value = "Real MO"
.Range("K:K").Cut
.Range("I:I").Insert xlToRight
.Range("Q:Q").Cut
.Range("I:I").Insert xlToRight
.Range("L2:L" & r).FormulaR1C1 = "=VLOOKUP(RC[-3],'DL List'!C[-11]:C[-10],2,0)"
.Range("L2:L" & r).Value = .Range("L2:L" & r).Value
For x = 2 To r
If Range("L" & x).Text = "#N/A" Then
'If Range("K" & x).Value = "DL" Then
' Range("L" & x).Value = "DL"
'Else: Range("L" & x).Value = "IDL": End If
Range("L" & x).Value = "IDL"
End If
Next x
End With
count_DL = Application.WorksheetFunction.CountIf(ActiveSheet.Range("L:L"), "DL")
count_IDL = Application.WorksheetFunction.CountIf(ActiveSheet.Range("L:L"), "IDL")
Worksheets("Resultados").Range("B17") = count_DL
Worksheets("Resultados").Range("C17") = count_IDL
Your expression works on my test worksheet so the problem must be something about your data.
I do not like stringing properties together like this because the objective becomes very unclear. Worse, if it fails, you do not know where is fails.
Try replacing the statement with this:
Dim rng As Range
Debug.Print .UsedRange.Address
Debug.Print .UsedRange.Offset(1).Address
Set rng = Intersect(.UsedRange, .UsedRange.Offset(1))
Debug.Print rng.Address
Debug.Print rng.SpecialCells(12).Address
Debug.Print rng.SpecialCells(12).EntireRow.Address
rng.SpecialCells(12).EntireRow.Delete
Step through this code to make sure each range is as you expect.
My guess that that there are no visible cells in the range so you are attempting to delete Nothing.
Edit Extra information about finding last row of worksheet.
There are a variety of methods of finding the last used row or column of a worksheet. None work in every situation but UsedRange is the method least likely to give the result you expect.
The most popular method of finding the last row, judging by answers here, is:
RowLast = .Cells(Rows.Count,9).End(xlUp).Row
This is the VBA equivalent of placing the cursor in the bottom cell of column 9 and clicking Ctrl+Up. RowLast will be set to the last row with a value in column 9 unless you have a value in the bottom cell. For this method to be of any use, there must be a value in the specified column of the last used row.
Find is a reliable method of finding the last value by either row or column.
SpecialCells is another useful method.
This answer of mine VBA Dynamic Ranges includea a macro, FindFinal, which demonstrates how these methods can fail to give the result you might expect. If you wish to fully understand the issues, step through this macro studying what happens.
I am currently working on a spreadsheet to help track individuals who attend a weekly meeting conducted by my department. I am trying to automate the process of tracking by using a macro to copy values from a list/form that a member of my department will enter the attendees email and the date. The email and date will then be added together (=a&b) to generate a value and that value will be used to mark whether the individual is present or not at that particular meeting. View Image of form/table
A report is generated after the meeting to tell which individuals have attended and how long they were on the call for. Before I was taking this report and pasting it onto the bottom of the original list but this has become inefficient as the columns and table length have changed. What I would like to do is take the emails, dates, and value on spreadsheet from the calculate tab and have those values append onto the bottom of the list on the reports tab without altering any of the previous information. View Image of reports tab
After the values have been appended to the bottom of the report, I have another tab called meeting dates. This contains a formula that will determine whether the individual was present or not by marking it with either āYā or āNā. Forgot to mention that every week it is the same 17 individuals that are attending these meetings. Eventually I would like to have it so that if the date entered on the calculate tab is not present on the meeting dates tab, add the date to the meeting dates tab.
I am still very new to Excel VB and macros however do have some programming experience. Just not in excel. If somebody could help me, that would be awesome!
This answer is an attempt to get your started.
If you search the internet for "Excel VBA Tutorial" you will get many hits. Try a few because they are all different and pick the one you like best. Work through that tutorial to get a general feel for Excel. I do not believe you will be successful finding bits of relevant code without that general feel.
Do not try to describe your entire problem because I doubt anyone will respond. Instead try to break your problem down into little steps and seek help with those steps.
For example, you will need to determine the number of rows in the post-meeting report so you can access that data. You then want to add that data to the bottom of the previous list. In both cases you need to determine the last used row in a worksheet. "Excel VBA: How to find last row of worksheet?" is a simple question and you will be able to find multiple answers. I give my response to that question below.
I assume the post-meeting report and the list you are creating are in different workbooks. Your macro could be in the same workbook as the list or it could be in a different workbook. Macros can access their own workbooks, any other workbook that happens to be open or they can open as many other workbooks as required. Again "Excel VBA: How do I work with several workbooks?" should result in plenty of hits.
I have not tried either of my questions. I find "Excel VBA:" helps but you may require several attempts before you find the just the right question to get the answer you seek. But if your question is small and precise you should always be able to find an answer.
Let's return to the first question. An irritating feature of Excel VBA is that they are almost always several ways of achieving a similar effect. Create a new workbook, create a module and copy the code below to it. Run the macro FindFinal().
This macro demonstrates several methods of finding the last row and column. Every method has its problems and I have tried to show how how each method can fail. There is a lot of worksheet access within this macro which I believe will repay study. It should help you decide which method is appropriate for each of your requirements.
Option Explicit
Sub FindFinal()
Dim Col As Long
Dim Rng As Range
Dim Row As Long
' Try the various techniques on an empty worksheet
Debug.Print "***** Empty worksheet"
Debug.Print ""
With Worksheets("Sheet1")
.Cells.EntireRow.Delete
Set Rng = .UsedRange
If Rng Is Nothing Then
Debug.Print "Used range is Nothing"
Else
Debug.Print "Top row of used range is: " & Rng.Row
Debug.Print "Left column row of used range is: " & Rng.Column
Debug.Print "Number of rows in used range is: " & Rng.Rows.Count
Debug.Print "Number of columns in used range is: " & Rng.Columns.Count
Debug.Print "!!! Notice that the worksheet is empty but the user range is not."
End If
Debug.Print ""
Set Rng = .Cells.Find("*", .Range("A1"), xlFormulas, , xlByRows, xlPrevious)
If Rng Is Nothing Then
Debug.Print "According to Find the worksheet is empty"
Else
Debug.Print "According to Find the last row containing a value is: " & Rng.Row
End If
Debug.Print ""
Set Rng = .Cells.SpecialCells(xlCellTypeLastCell)
If Rng Is Nothing Then
Debug.Print "According to SpecialCells the worksheet is empty"
Else
Debug.Print "According to SpecialCells the last row is: " & Rng.Row
Debug.Print "According to SpecialCells the last column is: " & Rng.Column
End If
Debug.Print ""
Row = .Cells(1, 1).End(xlDown).Row
Debug.Print "Down from A1 goes to: A" & Row
Row = .Cells(Rows.Count, 1).End(xlUp).Row
Debug.Print "up from A" & Rows.Count & " goes to: A" & Row
Col = .Cells(1, 1).End(xlToRight).Column
Debug.Print "Right from A1 goes to: " & ColNumToCode(Col) & "1"
Col = .Cells(1, Columns.Count).End(xlToLeft).Column
Debug.Print "Left from " & Columns.Count & _
"1 goes to: " & ColNumToCode(Col) & "1"
' Add some values and formatting to worksheet
.Range("A1").Value = "A1"
.Range("A2").Value = "A2"
For Row = 5 To 7
.Cells(Row, "A").Value = "A" & Row
Next
For Row = 12 To 15
.Cells(Row, 1).Value = "A" & Row
Next
.Range("B1").Value = "B1"
.Range("C2").Value = "C2"
.Range("B16").Value = "B6"
.Range("C17").Value = "C17"
.Columns("F").ColumnWidth = 5
.Cells(18, 4).Interior.Color = RGB(128, 128, 255)
.Rows(19).RowHeight = 5
Debug.Print ""
Debug.Print "***** Non-empty worksheet"
Debug.Print ""
Set Rng = .UsedRange
If Rng Is Nothing Then
Debug.Print "Used range is Nothing"
Else
Debug.Print "Top row of used range is: " & Rng.Row
Debug.Print "Left column row of used range is: " & Rng.Column
Debug.Print "Number of rows in used range is: " & Rng.Rows.Count
Debug.Print "Number of columns in used range is: " & Rng.Columns.Count
Debug.Print "!!! Notice that row 19 which is empty but has had its height changed is ""used""."
Debug.Print "!!! Notice that column 5 which is empty but has had its width changed is not ""used""."
Debug.Print "!!! Notice that column 4 which is empty but contains a coloured cell is ""used""."
End If
Debug.Print ""
Set Rng = .Cells.Find("*", .Range("A1"), xlFormulas, , xlByRows, xlPrevious)
If Rng Is Nothing Then
Debug.Print "According to Find the worksheet is empty"
Else
Debug.Print "According to Find the last row containing a formula is: " & Rng.Row
End If
' *** Note: search by columns not search by rows ***
Set Rng = .Cells.Find("*", .Range("A1"), xlFormulas, , xlByColumns, xlPrevious)
If Rng Is Nothing Then
Debug.Print "According to Find the worksheet is empty"
Else
Debug.Print "According to Find the last column containing a formula is: " & Rng.Column
End If
' *** Note: Find returns a single cell and the nature of the search
' affects what it find. Compare SpecialCells below.
Debug.Print ""
Set Rng = .Cells.SpecialCells(xlCellTypeLastCell)
If Rng Is Nothing Then
Debug.Print "According to SpecialCells the worksheet is empty"
Else
Debug.Print "According to SpecialCells the last row is: " & Rng.Row
Debug.Print "According to SpecialCells the last column is: " & Rng.Column
End If
Debug.Print ""
Row = 1
Do While True
Debug.Print "Down from A" & Row & " goes to: ";
Row = .Cells(Row, 1).End(xlDown).Row
Debug.Print "A" & Row
If Row = Rows.Count Then Exit Do
Loop
End With
With Worksheets("Sheet2")
.Cells.EntireRow.Delete
.Range("B2").Value = "B2"
.Range("C3").Value = "C3"
.Range("B7").Value = "B7"
.Range("B7:B8").Merge
.Range("F3").Value = "F3"
.Range("F3:G3").Merge
Debug.Print ""
Debug.Print "***** Try with merged cells"
Set Rng = .UsedRange
If Rng Is Nothing Then
Debug.Print "Used range is Nothing"
Else
Debug.Print "Used range is: " & Replace(Rng.Address, "$", "")
End If
Debug.Print ""
Set Rng = .Cells.Find("*", .Range("A1"), xlFormulas, , xlByRows, xlPrevious)
If Rng Is Nothing Then
Debug.Print "According to Find the worksheet is empty"
Else
Debug.Print "According to Find the last cell by row is: " & Replace(Rng.Address, "$", "")
End If
Set Rng = .Cells.Find("*", .Range("A1"), xlFormulas, , xlByColumns, xlPrevious)
If Rng Is Nothing Then
Debug.Print "According to Find the worksheet is empty"
Else
Debug.Print "According to Find the last cell by column is: " & Replace(Rng.Address, "$", "")
End If
Debug.Print "!!! Notice that Find can ""see"" B7 but not F3."
Debug.Print ""
Set Rng = .Cells.SpecialCells(xlCellTypeLastCell)
If Rng Is Nothing Then
Debug.Print "According to SpecialCells the worksheet is empty"
Else
Debug.Print "According to SpecialCells the last row is: " & Rng.Row
Debug.Print "According to SpecialCells the last column is: " & Rng.Column
End If
End With
End Sub
Function ColNumToCode(ByVal ColNum As Long) As String
Dim Code As String
Dim PartNum As Long
' Last updated 3 Feb 12. Adapted to handle three character codes.
If ColNum = 0 Then
ColNumToCode = "0"
Else
Code = ""
Do While ColNum > 0
PartNum = (ColNum - 1) Mod 26
Code = Chr(65 + PartNum) & Code
ColNum = (ColNum - PartNum - 1) \ 26
Loop
End If
End Function
In the code above, I access worksheet cells directly with statements such as .Range("B2").Value = "B2". This can be slow particularly when you are moving data from one worksheet to another. An alternative approach is to use arrays.
Dim Rng As Range
Dim ShtValues as Variant
With Worksheets("Xxxx")
Set Rng = .Range(.Cells(Row1, Col1), .Cells(Row2, Col2))
End With
ShtValues = Rng.Value
A Variant is a variable that can hold anything including an array. ShtValues = Rng.Value converts ShtValues to a two-dimensional array hold all the values within Rng. Processing values within an array is much faster that accessing them in the worksheet.
.Range(.Cells(Row1, Col1), .Cells(Row2, Col2)) is perhaps the easiest way of creating a range specifying the worksheet area with Cells(Row1, Col1) as the top left cell and Cells(Row2, Col2) as the bottom right.
If I understand correctly, you want to move data from the post-meeting report to the list but the sequence of columns in the report and list are not the same. This suggests you need to move the data as columns. Using .Range(.Cells(Row1, Col1), .Cells(Row2, Col2)) and with Col1 = Col2, you can define a range that is a column.
Rng1.Copy Destination := Cell2
The above statement will copy the contents of Rng1 to the range starting at Cell2. A statement like this for each column of data in the report may be the easiest way of copying the data.
I hope the above gives you a start.