I have an issue where I'm currently using several queries to achieve the outcome I want, but when run on larger spreadsheets it slows down substantially. Some info:
The Excel workbook in question has the .xlsx extension;
The two sheets in the Excel file are Matched and `Unmatched';
Connection type can use either ODBC or OleDB;
All data that needs to be matched is located on one sheet in Excel;
Matched data needs to be in pairs so must equal 2;
The 2 columns to be matched are Receipt and Amount where the Receipt on the two rows must match exactly and the Amount value for both rows must balance or equal 0. One Amount value will have a negative sign (-) at the beginning of it so the amounts can be added together to balance out, but they are on separate rows;
The number of rows can range from a couple hundred to several thousand (~60,000); and
If the two rows match/balance by Receipt and Amount, then they need to be moved into the Matched sheet in the same Excel file.
Scripts are not out of the question, VBScript and Powershell are welcome.
Example:
As you can see in the Excel example, I need to match 2 rows where the Receipt value matches exactly, and the Amount values balance. If both conditions are met, then I need to insert said rows into the Matched sheet. So in this example, both rows with 101010 in the Receipt column (rows 2 and 3) have amounts that balance in the Amount column, so they would be moved to Matched as both conditions are met. The remaining two values in Receipt match, but their Amount values do not balance, so the conditions would not be met for them.
The limitations are the statements available to the ODBC and OleDB connections. I am currently using several queries to match and return the row count of the above conditions and if it equals two, then I know to insert all rows of that Receipt number into the Matched sheet; however, like I said, I need it to be more efficient and to perform faster.
Here's a VBA suggestion - it expects the original data to be in a tab called 'Original' and will sort on Receipt, then do a couple of formulas to determine receipt#'s that tally to zero then creates a new tab called 'Match' with matched receipt rows and another called 'Unmatch' with unmatched receipts.
Sub TallySplit()
vLR = Cells(Rows.Count, 1).End(xlUp).Row
ActiveWorkbook.Worksheets("Original").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("Original").Sort.SortFields.Add Key:=Range("A2:A" & vLR), _
SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("Original").Sort
.SetRange Range("A1:D" & vLR)
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
Range("E2").Select
ActiveCell.FormulaR1C1 = "=IF(R[-1]C[-4]=RC[-4],R[-1]C[-3] + RC[-3],RC[-3])"
Range("E2").Select
Selection.AutoFill Destination:=Range("E2:E" & vLR)
Range("E2:E" & vLR).Select
Range("F2").Select
ActiveCell.FormulaR1C1 = "=IF(RC[-5]=R[1]C[-5],R[1]C,RC[-1])"
Range("F2").Select
Selection.AutoFill Destination:=Range("F2:F" & vLR)
Range("F2:F" & vLR).Select
Range("A1").Select
Selection.AutoFilter
ActiveSheet.Range("$A$1:$F$" & vLR).AutoFilter Field:=6, Criteria1:="<>0", _
Operator:=xlAnd
Range("A1:D" & vLR).Select
Selection.Copy
Sheets.Add.Name = "Unmatch"
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Sheets("Original").Select
ActiveSheet.Range("$A$1:$F$" & vLR).AutoFilter Field:=6, Criteria1:="=0", _
Operator:=xlAnd
Range("A1:D" & vLR).Select
Selection.Copy
Sheets.Add.Name = "Match"
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
End Sub
Related
hope I can get any help on this.. I have tried counting records which are meeting two dates criteria (date criteria are dynamic so will keep changing, I have put it on next sheet from the data here is code I am trying to run which is bringing me blank result here is my code which I have gathered so far using macro:
Worksheets("Data").Select
Range("A1").Select
Range(Selection, Selection.End(xlToRight)).Select
Selection.AutoFilter
ActiveSheet.Range("$A$1:$BN$235").AutoFilter Field:=4, Criteria1:="=COUNTIFS(DOB," >= "&R[1]C,DOB,""<=""&R[1]C[1])"
ActiveWindow.SmallScroll Down:=-9
Range("A1").Select
Range(Selection, Selection.End(xlDown)).Select
Range(Selection, Selection.End(xlToRight)).Select
Selection.Copy
Set NewSheet = Sheets.Add(After:=Worksheets("error"))
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks:=False, Transpose:=False
NewSheet.Name = "DOBrange"
NewSheet.Range("A1").Select
Application.CutCopyMode = False
Worksheets("Data").Select
Worksheets("Data").Range("A1").Select
You appear to be trying to filter rows by selecting values between two dates, however your criteria is saying you want to select values equal to something (which seems to be attempting to say all values equal to the count of values that have dates between two other dates - so if five dates were within the range, you are attempting to say filter to show all values that are 5).
The correct way to select values between two dates would be by saying:
ActiveSheet.Range("$A$1:$BN$235").AutoFilter _
Field:=4, _
Criteria1:=">=" & Worksheets("Error").Range("A20").Value2, _
Operator:=xlAnd, _
Criteria2:="<=" & Worksheets("Error").Range("B20").Value2
(assuming the two dates are in cells A20 and B20 of worksheet "error")
You have problem at the criteria1 statement
Criteria1:="=COUNTIFS(DOB," >= "&R[1]C,DOB,""<=""&R[1]C[1])"
You can see that text color of >= is black while other is brown. this mean vba will compare between "=COUNTIFS(DOB," and "&R[1]C,DOB,""<=""&R[1]C[1])" then criteria1 will be True or False. so you will not get the result vary by your condition
The code below may fix this problem
Criteria1:="=COUNTIFS(DOB, "">=""" & R[1]C & ",DOB,""<=""" & R[1]C[1] & ")"
I need help with my code. I am new to VBA so not quite understand to do this right. Already search answer from another thread but it too advance I cannot follow it.
Here the situation I want.
at column S I use vlookup to find match data from sheet3 and filter with N/A value. then I want to fill down visible data in column Q with "Fulfilled" but I what get is it fill my Q2 with header value and do not fill to last row.
Sheets("Sheet1").Select
Range("S2").Select
Range(Selection, Selection.End(xlDown)).Select
Selection.ClearContents
' vlookup from sheet3
Range("S2").Select
ActiveCell.FormulaR1C1 = "=VLOOKUP(RC[-14],Sheet3!C[-16],1,0)"
Selection.AutoFill Destination:=Range("S2:S" & Range("E" & Rows.Count).End(xlUp).Row)
Range("S2").Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Copy
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Application.CutCopyMode = False
Range("S2").Select
ActiveSheet.Range("$A$1:$S$20000").AutoFilter Field:=19, Criteria1:="#N/A"
' here it don't fill to last row
Range("Q2").Select
ActiveCell.FillDown
Many thanks for you help.
Autofill needs to know the range to autofill, not only the top cell. Supposing the only problem you have is in the last part, try this instead:
With ActiveSheet.UsedRange
.Resize(.Rows.Count - 1).Offset(1).Columns("B"). _
SpecialCells(xlCellTypeVisible).FillDown
End With
Or it could be .value = "Fulfilled" instead of .Filldown, depending on what you want to do.
That said, think of refactoring your code to get rid of the Select, ActiveSheet and Copy/Paste stuff` and work with qualified ranges.
I'm working on creating a scheduling tool that looks up departure and arrival times and creates a visual tool. I have 1440 columns, one for each minute of the day, and I've written a formula to compare the Staging time, departure time and arrival time to the minute of that column, which I've indicated in row 1. The formula looks like this:
=IFERROR(IF(AND(W$1<[Depart],W$1>=[stage]),"STAGE",IF(AND(W$1>=[Depart],W$1<=[Arrival]),"IN SERVICE","")),"")
Then I used conditional formatting to highlight the terms "Stage" and "In service" in different colours. When the column width is reduced to a few pixels, this provides a great visual scheduling toll.
The problem I hear from end users is that this formula, 720,000 times, takes a fair chunk of ram. I'm wondering if there is a way to reduce the file size by writing a vba that will remove the formulas when not in use and put them back when needed.
Thanks!
You should copy the cells with the formula & then with that same range use .PasteSpecial xlPasteValues. This will replace the formulas with the values they produce. You should then put in some logic on the worksheet's code so that when the values for [Depart], [Stage], &/or [Arrival] change, the formulas are reapplied, calculated, & then removed again. This way A) you save the formula in code, where it can't accidentally be deleted; B) the user doesn't have to do anything extra to initiate the update.
I'm going to assume the Depart/Arrival/Stage times start in B2 and are in a single column and that the 1440 columns start in column D. Given that, you'd want something like the following:
Private Sub Worksheet_Change(ByVal Target As Range)
'
'Confirm the target is for the Depart/Arrival/Stage values & we didn't just empty the cell
If Target.Column = "B" AND Target.Value <> "" THEN
Dim r as long
r = Target.Row
'Remove old values to be sure we don't carry over anything from the row's prior value
Range("D" & r & ":BCM" & r).ClearContents
Range("D" & r).Formula= "[insert your formula here]"
'Paste the formulas to the rest of the range
Range("E" & r & ":BCM" & r).PasteSpecial xlPasteFormulas
'Calculate so we have correct values
Range("D" & r & ":BCM" & r).Calculate
'Replace the formulas with values
Range("D" & r & ":BCM" & r).Copy
Range("D" & r & ":BCM" & r).PasteSpecial xlPasteValues
End If 'else do nothing, not in the correct range (prevents infinite loops)
End Sub
You'll probably need to do some tweaking to account for what data is where & how the updates are done (for example, if you add/remove multiple times at once, you'll want to apply the formulas to the first cell like I did, but then apply it to the whole affected range, rows & columns, in one step rather than one row # a time.
Thanks for your input all,
Sometimes the best solution is the simplest. I used the record macro function and rebuilt my chart. I added a couple of screen update commands and it works great. Code below if you're interested.
Thanks.
Sub ActiveGantt()
Application.ScreenUpdating = False
If Range("H1") = "OFF" Then
Sheets("Sheet1").Select
Range("W4").Select
ActiveCell.FormulaR1C1 = _
"=IFERROR(IF(AND(R1C<RC21,R1C>=RC20),""STAGE"",IF(AND(R1C>=RC21,R1C<=RC22),""IN SERVICE"","""")),"""")"
Range("W4").Select
Selection.AutoFill Destination:=Range("W4:W717"), Type:=xlFillDefault
Range("W4:W717").Select
Selection.AutoFill Destination:=Range("W4:BDF717"), Type:=xlFillDefault
Range("W4:BDF717").Select
Selection.End(xlUp).Select
Range("W4").Select
Range(Selection, Selection.End(xlDown)).Select
Range(Selection, Selection.End(xlToRight)).Select
Selection.Copy
Range("W4").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Range("h1").Value = "ON"
Else
Sheets("Sheet1").Select
Range("W4").Select
Range(Selection, Selection.End(xlDown)).Select
Range(Selection, Selection.End(xlToRight)).Select
Selection.ClearContents
Selection.End(xlUp).Select
Range("U3").Select
Selection.End(xlToLeft).Select
Range("h1").Value = "OFF"
Application.ScreenUpdating = True
End If
End Sub
I have a column named "Re-Align Midpoint" whose result is the date of another column (named "Final Execution Date") minus 100 days. Right now, the "Re-Align Midpoint" date is in column AZ while "Final Execution Date" is in column BR. These columns are subject to be moved around, so it is impractical for me to build a macro based on column indexes. What I would like to do instead is subtract one from the other based on column names. The header rows are on row 2, not row 1
This is what I have for my recorded macro:
Sub Re_Align_Midpoint_Date
Range("AZ3").Select
ActiveCell.FormulaR1C1 = "=RC[18] - 100"
Range("AZ3").Select
Selection.AutoFill Destination:=Range("AZ3:AZ142"), Type:=xlFillDefault
Range("AZ3:AZ142").Select
ActiveWindow.SmallScroll Down:=-132
Selection.Copy
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Application.CutCopyMode = False
End Sub
As long as the column headings won't change you can use an hLookup to do this:
=HLOOKUP("Final Execution Date",$A$2:$ZZ3,ROW(A2),FALSE)-100
Just paste this formula at the top of your "Midpoint" column and fill down.
I am trying to automate a monthly report that I run. Each month, I run a query (embedded into a separate tab) and then use vlookup to move the values into the column for the current month.
I want to create a macro to move the values for the current month, but what I need to figure out, is how to modify the macro to check if a column for a month has been filled out, and if it is, then to put the monthly numbers in the column to the right (next month). I have been researching this for a while, but I have very little VBA experience and get stuck when I run into errors. I know I can use the "IsEmpty" function, but I'm not sure of the best way to use it.
Here is a copy of the Macro:
Range("N6").Select
ActiveCell.FormulaR1C1 = "=VLOOKUP(RC[-10],'ENT - Query Totals'!C2:C4,3,FALSE)"
Range("N6").Select
ActiveWindow.SmallScroll Down:=-9
Range("N6").Select
Selection.AutoFill Destination:=Range("N6:N82"), Type:=xlFillDefault
Range("N6:N82").Select
ActiveWindow.SmallScroll Down:=-60
Range("N6:N82").Select
Selection.Copy
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Range("O64").Select
Cells.Replace What:="#N/A", Replacement:="0", LookAt:=xlPart, _
SearchOrder:=xlByRows, MatchCase:=False, SearchFormat:=False, _
ReplaceFormat:=False
End Sub
So basically, I want to check if "N6" is empty. If it is, the macro would use vlookup to move the data from the query tab into the column "N". If it is not empty, I want it to copy the data from the query tab into column "O", etc.
Any help or guidance will be greatly appreciated while I continue to learn VBA
Dim copyRange as Range
Dim rowNum As Long
Range("N6").FormulaR1C1 = "=VLOOKUP(RC[-10],'ENT - Query Totals'!C2:C4,3,FALSE)"
Range("N6").AutoFill Destination:=Range("N6:N82"), Type:=xlFillDefault
Set copyRange = Range("N6:N82").Select
rowNum = 64
For each cell in copyRange
If Not IsEmpty(Range("O" & rowNum) Then
cell.copy destination:=Range("O" & rowNum)
rowNum = rowNum + 1
Else
cell.copy Destination:=Range("N" & rowNums)
rowNum = rowNum + 1
End if
Next cell
i feel like this is somewhere in the neighborhood of what youre asking but the way you worded your problem is a little confusing to me