As the title says, It seems obvious but I can't seem to find a one-liner to it without looping. Is there a method able to do this task with a fast execution time ?
Here is my failed attempt (It crashes my excel file) :
Worksheets("Iso_Journal").Range("A:P").EntireRow.SpecialCells(xlBlanks).EntireRow.Delete
Any knowledge would be appreciated !
Edit:
Found this method but it filters instead of deleting (much faster), it does the job but it still not what I truly need
Worksheets("Iso_Journal").Range("A:P").AutoFilter Field:=1, Criteria1:="<>"
Edit 2: (Response to #FaneDuru)
Please, try the next adapted code. If the target is to delete all empty ROWS, it is enough to use a single column. And also to limit the number of rows to be processed:
Dim wsJ As Worksheet
Set wsJ = Worksheets("Iso_Journal")
wsJ.Range("A1:A" & wsj.Range("A" & wsJ.rows.count).End(xlup).row).SpecialCells(xlBlanks).EntireRow.Delete
If the range to be processed is huge, and the workbook contains many formulas, you can make the code a little faster placing Application.Calculation = xlCalculationManual before the deletion line, followed by Application.Calculation = xlCalculationAutomatic. Even if the rows are deleted in block, Excel needs to update the formulas row by row, and no good to calculate after each...
Related
I'm trying to get some details copied in Excel from Sheet 1 columns 1-5 to Sheet 2 columns 1-4, but only for lines that include text or values on sheet one under a specific column (in this case, Column 2). There are other columns in between, so I need to be able to use exact columns rather than A:D for example.
Example of what I'm trying to achieve:
I have tried using a simple IF function with A:A<>"" so it would include any rows that have any data in them, however this does not seem to copy as I need and occasionally based on my attempts i also get circular reference errors. Additionally, I’m not sure how to make sure this gets pasted at the bottom of a table that will expand with each addition.
I realize a probably easier option would be to simply copy Sheet 1 entirely and use a filter on row 1 to deselect Blanks on A:A, but the sheet has so much more info that it would be a waste, and additionally info is constantly added so I need something scale-able. It also occurred to me now that by doing this i would include info from the "header" and "footer", basically a frozen pane - which I do not need.
Could this be done via a simple function, or would it require a Macro?
Please keep in mind I'm rubbish at programming, just trying to make my life easier and learn as I go. A lot of excel forums help but still I'm no coder. I can understand to a pretty big degree what the code does and can adjust accordingly though :)
As suggested, this cannot be done with formulas. There are different ways to achieve this.. below is one approach:
Sub CopyFilteredRows()
Dim oSourceSheet As Worksheet: Set oSourceSheet = ThisWorkbook.Worksheets("Sheet3") ' Set your source sheet here
Dim oRng As Range: Set oRng = oSourceSheet.Range("A2:E" & oSourceSheet.Range("C" & oSourceSheet.Rows.Count).End(xlUp).Row)
' Set filter on column B
oRng.AutoFilter
oRng.AutoFilter 2, "<>"
' Copy to specified sheet
oRng.SpecialCells(xlCellTypeVisible).EntireRow.Copy ThisWorkbook.Worksheets("Sheet4").Range("A2") ' Change your destination sheet here
' Clear objects
Set oRng = Nothing
Set oSourceSheet = Nothing
End Sub
Paste the above UDF in a Module and then run it whenever you want to perform the copy. I suspect you would have to modify it a bit so that you can cater for your particular scenario but it should give you a start
I'm currently working on a document that needs to autofill a selection of cells down an area that is changing, although it will regularly be upwards of 25000 or more rows. The cells that I am attempting to autofill downwards are filled with "complex" formulas, that are configured to work with autofill.
When I have more than 15000 rows, to run one instance of
Worksheets("sheet1").Range("A1:A4").AutoFill Destination:=Range("A1:A" & LastRow), Type:=xlFillDefault
this will take upwards of 20 minutes to completely execute this one line, so if I need to run this 5 times for an example, we're looking an estimated 100 minutes of run time for 5 lines of code.
I'm curious if there's a more efficient way to either use AutoFill, or execute the task I'm looking to achieve.
I haven't really found much that was helpful, I did change my range a few times to see if that increases or reduces times.
If I change LastRow to be equal to say, 50-1000 this line of code runs instantaneously. Once we creep above the 1000 rows, this starts to run incredibly slow.
Ideally, If I can find an additional way to run this that would significantly decrease my run times, that would be great.
There are some boilerplate code I use frequently to squeeze a better performance out of my VBA code:
Sub Test()
Application.EnableEvents = False
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
'Code here
....
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
Application.ScreenUpdating = False makes your spreadsheet not jump around. Can't provide you with hard data here, but from my personal experience updating the screen slows down your macro quite significantly.
Application.Calculation controls how calculation is handled, and is defaulted to xlCalculationAutomatic. Under this default setting, all formula field gets updated when there is a change in value of a referenced cell. Setting it to xlCalculationManual before running your VBA, then back to xlCalculationAutomatic will ensure that calculations are only performed one time, instead of throughout your script when numbers are changing all the time.
Application.EnableEvents controls codes that is triggered by event (like saving, or activating a worksheet). Just in case, I set it to False.
Remember to set everything back to their default values at the end of your vba, or under your ErrorHandler section of your VBA.
Just FYI, having these two application settings changed makes it a harder to debug your code if you rely on looking at your spreadsheet to debug. Best to completely remove them during debugging, and add them back once you are ready to push to production.
Without your specific formula, it is hard to further optimize. There is one other way you may try that MAY speed things up.
Sub MyAutoFill()
My_Formula = "SUM(1,3)" 'Replace this with your complex formula, remember to escape your inverted commas
Worksheets("sheet1").Range("A1:A" & LastRow) = "=" & My_Formula
End Sub
The above code sets the values of a range to a string the begins with "=", which becomes an excel formula on the worksheet. This MAY be more efficient than autofill, try it out and report back!
I've tried every suggestion on this site to make my macro faster.
Halt calculations
Disable the screen updating
Disable status bar updating
Telling the Excel to ignore events
and so much more. Even in loops, I've tried many methods of making my macro smooth.
And I saw an article that I can use colon to make a multiple lines of codes into single line.
From:
Dim x as Integer
x = 1
If x = 0 Then
Exit Sub
End If
To:
Dim x as Integer: x = 1: If x = 0 Then Exit Sub
Does making some codes in the same line makes the macro faster?
No, writing the code on the same line does not make the code run faster.
Code can be slow because of inefficient coding techniques, like looping through all cells in a column, reading each cell, calculating it, and writing the result back to another cell in each step of the loop.
Writing that code in one line is possible, but it won't make the code run faster.
What WILL make the code run faster is reading the data range into an array, then looping over the array, writing the output of any calculation into another array and then finally writing the result array back to the spreadsheet as one write operation.
This is simply a different line break and does not affect execution time.
You already noted a couple of tricks you applied:
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
Since you haven't posted full code and "Please optimize my code" is not a question, there's the following general tips to speed up Execution:
When referencing the same Object multiple times in a row, use With and End With instead. This is faster than specifying the full path to the object each time, since it prevents certain IO operations to access the right block in memory.
Use as little references to Worksheet / Range objects as possible, in general. A lot of times it's faster to use an Array than a Range. See Chip Pearson's article on this.
Looping is slow. Using native functions is faster. Make use of Autofilter instead of checking each cell, for example.
Do not use Copy and Paste, instead use the Destination parameter of the Copy method
Declare your objects as accurately as possible: Use Dim ws As Worksheet instead of Dim ws or Dim ws As Object.
Never use .Select and .Activate - See this Question
No, such changes does not boost Your code.
It also depends what Your macro do, but my case was that I needed to use sth similar to vlookup in my code, so at the first version I used ForEach loop and if statment - when I was to match 10 000 records with about 3k values it took about 15 minutes. So I decided to use dictionary (reference does not break acro on other machines) and it really boost my code. Now 20 k records with 6k values takes about 2 minutes.
I have a macro-enabled Excel workbook that opens a series of MSProject files, applies filters to a specific column, and copies portions of the visible range to Excel. The code for the copy action looks like this:
For Each Task In ActiveSelection.Tasks
If Not Task Is Nothing Then
TargetWS.Cells(Row, 3) = Task.PercentComplete / 100
TargetWS.Cells(Row, 4) = Task.Name
TargetWS.Cells(Row, 5) = Task.Start
TargetWS.Cells(Row, 6) = Task.Finish
TargetWS.Cells(Row, 7) = Task.BaselineFinish
Row = Row + 1
End If
Next Task
Essentially, I am looping through every row in the filtered range and copying each column one at a time. As you can imagine, this takes a long time.
My hope is to replace this iterative method with the standard set of actions I would use in Excel VBA: define first & last rows, then use one copy action for each column I want. This would greatly reduce the number of copy actions required to complete the task, which should provide a speed increase.
In Excel VBA, the code I want would look something like this, having defined the last row:
TargetWS.Range("A2:" & LastRow).Copy Destination:= (destination cells)
I know how to find the last visible task in Project, but am unfamiliar with range selection. Could someone fill in the gaps?
Thanks!
Your current method takes a sound approach, so instead of changing the method, try improving the performance.
The slowest part of your code right now isn't that you are looping through the tasks one-by-one, it's that you are writing to Excel cell-by-cell. The first step you can take is to write all data for a single task at one:
TargetWS.Range("C" & Row & ":G" & Row) = Array(Task.PercentComplete / 100, _
Task.Name, Task.Start, Task.Finish, _
Task.BaselineFinish)
Once you are comfortable with that, then you can move on to writing blocks of data at a time. To do this, store the data in a 2-dimensional array and only write it to Excel when you are done looping through the tasks. (Note, if you have many thousands of tasks, you may need to write the data in smaller chunks.)
Also, make sure you have turned calculation off in Excel. This will improve performance as can turning off screen updates. Just make sure you reset both application settings when your code is completed (even it if finishes with an error).
One last tip, avoid naming variables the same as objects (e.g a Task object named Task).
I have two different workbooks with approx 15 columns and 50k rows in one workbook and 10columns and 1000 rows in another workbook and only 2 columns(partnumber, changelevel)are in common. So I want to pull two reports from these two workbooks.
Records with common partnumber & changelevel in to a different workbook as one report.
I want to delete the common part number & changelevel records from first workbook and copy all the remaining records into a different workbook as another report.
Angiee . . .
You have a couple of questions that will need to answered before anyone can help you with this.
Is this a one time deal where you are trying to clean up data and come up with a new starting point and you won't need to run this process over and over again?
Can we assume that the data rows are not in the same Worksheet row in both Workbooks?
If the answer to both questions is YES then I would have to say that Excel is decidedly NOT the Office Application that you should be using. I would suggest that you import both Workbooks into an Access Database as separate tables. That way you can use SQL to perform the matches and lookups with little or no code needed. You can easily export the query results back to an Excel Workbook once you have the results you want. You could probably have your answer in an hour. If you go with this option you can also link the Worksheets into the Access DB and avoid importing them. It won't be as fast but it will work.
Otherwise, if you are stuck with Excel then you either have a significant amount of code to write that pretty much consists of looping through all of the records in one Workbook, looking up the values in the other Workbook then generating the output in still more Workbooks. . . or . . . you could try copying the Worksheet with the 1000 records into the other Workbook and then using the Worksheet Functions VLOOKUP and/or HLOOKUP to create a lot of formulas. (I can't in good conscience endorse this second approach but if you are not very experienced at VBA then it may be the better approach for you).
Either way you go with the Excel solution there'll be a lot of work invloved.
If you have any specific coding issues then you are in the right spot. But you will need to pick an approach first.
Good luck!
Doug
The question is an easy one to answer, the problem comes into two fold.
How fast is your computer?
How often do you need to run this code?
The reason I ask these questions are because to run any code on 50,000 lines no matter how small the code to actually make this work is... you need to have a computer that is rather robust, otherwise this code is going to stall your computer, or at least excel for a good minute to three minutes+ depending on how fast and how much memory you actually have.
Without seeing your workbook you need some very simple formulas, but what you are going to have to do is add another line into the workbook. In Column P, you need a verification formula. This formula is simple, but it will depend on how many points of reference you require.
=COUNTIFS('Sheet2'!$A:$A,$A3,'Sheet2'!$E:$E,$E3)
From there you can see what are duplicates or not. You can then have in column Q a formula like this:
=IF($P3,"SAME","")
And it will tell you if the data is the same or not. Basically it says if there is anything but 0 in the cell P3 it will say there is something the same, otherwise it's not.
From there you need a code sort of like this:
Sub Update_TNOOR()
Dim wsS1 As Worksheet
Dim wsS2 As Worksheet
Dim lastrow As Long, fstcell As Long
Set wsS1 = Sheets("Sheet1")
Set wsS2 = Sheets("Sheet2")
With Application
.ScreenUpdating = False
.DisplayAlerts = False
.EnableEvents = False
End With
With wsS1
wsS1.Columns("P:Q").ClearContents
ThisWorkbook.Sheets("Sheet1").Cells(1, 16).Value = “=COUNTIFS('Sheet2'!$A:$A,$A3,'Sheet2'!$E:$E,$E3)"
ThisWorkbook.Sheets("Sheet1").Cells(1, 17).Value = “=IF($P3,"Same",””””)"
wsS2.Columns("P:Q").ClearContents
ThisWorkbook.Sheets("Sheet2").Cells(1, 16).Value = “=COUNTIFS('Sheet1'!$A:$A,$A3,'Sheet1'!$E:$E,$E3)"
ThisWorkbook.Sheets("Sheet2").Cells(1, 17).Value = “=IF($P3,"Same",”Different”)"
End With
With Intersect(wsS1, wsS1.Columns("Q"))
.AutoFilter 1, "<>Same"
With Intersect(.Offset(2).EntireRow, .Parent.Range("B:Q"))
.EntireRow.Delete
End With
.AutoFilter
End With
'Blow away rows that are useless
lastrow = wsS2.Range("A2").End(xlDown).Row
wsS2.Range("P1:Q1").Copy wsS2.Range("P2:Q" & lastrow)
With Intersect(wsS2.UsedRange, wsS2.Columns("Q"))
wsS2.Range("P:Q").Calculate
.AutoFilter 1, "<>Different"
.SpecialCells(xlCellTypeVisible).EntireRow.Delete
End With
With wsS2
lastrow = wsS2.Range("A1").End(xlDown).Row
Intersect(.UsedRange, .Range("A1:N" & lastrow)).Copy wsS1.Cells(Rows.Count, "B").End(xlUp).Offset(1)
End With
With Application
.ScreenUpdating = True
.DisplayAlerts = True
.EnableEvents = True
End With
End Sub
this should get you on your way... if I read what you are attempting to do correctly.
As people have said though, what you want done can be done in excel, should it, I don't know... people here seem to think not, but if you need to use excel, this should get you on your way.
Again, I don't know what your workbook looks like, so I hope this helps. This compares data and merges it into the first sheet. IT won't do everything you want to do... but this should get you on your way.