Excel/VBA - Extracting a range of rows from a selected sheet to a new book - vba

I'm trying to build a new VBA function for Excel. I've got a book of sheets with a front page that always loads first, on this page I've got a combo box that lists all the other sheets in the book and a nice extract button that will pull out the chosen sheet to a new book. (Thanks to those here who helped with that). Now I need a new function that will use the same combo box, but instead only extract a small subset of the chosen sheet.
Unfortunately, that subset isn't on the same rows for every sheet, nor is the number of rows the same (so one sheet, the subset might be 10 rows, on another it might be 12, on another it might be 20, etc etc etc).
On the plus side, there are merged rows (from column A to G) at the start and end of each subset - with specific text, which could be used to search for.
After some back and forth, I've got a better bit of code that I think is almost working:
Sub ZCPS_Extract()
Dim StartRow
Dim EndRow
Dim Zws As Worksheet
Dim wbkOriginal As Workbook
Set wbkOriginal = ActiveWorkbook
StartRow = 1
EndRow = 1
'sets site details into the header of the ZCPS checksheet
Worksheets(Sheet1.CmbSheet.Value).Range("B3").Value = Worksheets("front page").Range("E6")
Worksheets(Sheet1.CmbSheet.Value).Range("D3").Value = Worksheets("front page").Range("N6")
Worksheets(Sheet1.CmbSheet.Value).Range("F3").Value = Worksheets("front page").Range("K6")
Set Zws = Sheets(Sheet1.CmbSheet.Value)
'selects ZCPS block from select estate sheet
StartRow = (Zws.Cells.Find("**** ZCPS Installation").Row) + 1
EndRow = (Zws.Cells.Find("**** Aztec Hotfixes").Row) - 1
'copy above block and paste into Z-MISC starting at row 5
Worksheets(Sheet1.CmbSheet.Value).Range(Cells(StartRow, 1), Cells(EndRow, 7)).Copy Worksheets("Z-MISC").Range("A5")
With ActiveWorkbook.Sheets("Z-MISC")
.Copy
ActiveWorkbook.SaveAs _
"C:\temp\" _
& ActiveWorkbook.Sheets("Z-MISC").Cells(3, 2).Text _
& " ZCPS CheckSheet " _
& Format(Now(), "DD-MM-YY") _
& ".xlsm", _
xlOpenXMLWorkbookMacroEnabled, , , , False
End With
'code to close the original workbook to prevent accidental changes etc
Application.DisplayAlerts = False
wbkOriginal.Close
Application.DisplayAlerts = True
End Sub
It's error on the line for copying, I'm getting a runtime error of "Application-defined or object-defined error" which to my limited knowledge isn't helping me. Any assistance/pointers/suggestions are welcomed.

Sub ismerged()
Dim start As Integer, finish As Integer
For i = 1 To Range("A655").End(3).Row + 1
If Cells(i, "A").MergeCells = True Then
start = i
Exit For
End If
Next
For i = start To Range("A655").End(3).Row + 1
If Cells(i, "A").MergeCells = True Then
finish = i
End If
Next
MsgBox start
MsgBox finish
End Sub
Then I guess you can select your data as you wish.

I'm not sure about the way you reference your sheet. I will assume 'comboboxvalue' contains the name or the number of the sheet you are selecting. Your code should be something like the following.
Sub Z_Extract()
Dim StartRow
Dim EndRow
Dim ws As Worksheet
Set ws = Sheets(comboboxvalue)
StartRow = ws.Cells.Find("**** ZC").Row
EndRow = ws.Cells.Find("****").Row
'Im assuming you have values up to column G
ws.Range(ws.Cells(StartRow, 1), Cells(EndRow, 7)).Copy
'Now that you have the correct Range selected you can copy it to your new workbook
'SelectedRange.Copy Etc.....
'Cleanup
Set ws = Nothing
End Sub

Got it working.
Set Zws = Sheets(Sheet1.CmbSheet.Value)
'selects ZCPS block from selected estate sheet
StartRow = (Zws.Cells.Find("**** ZCPS Installation").Row)
EndRow = (Zws.Cells.Find("**** Aztec Hotfixes").Row) - 1
'copy above block and paste into Z-MISC starting at row 10
Sheets(Sheet1.CmbSheet.Value).Activate
ActiveSheet.Range(Cells(StartRow, 1), Cells(EndRow, 7)).Select
Selection.Copy
Sheets("Z-MISC").Select
Range("A10").Select
ActiveSheet.Paste

Related

macro: copy paste cell if condition met

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

Column and Row Indexing - VBA

I am trying to write something that does the following in excel macro (VBA):
For 'column X' of 'spreadsheet'
Copy range(row(1):row(5)
Paste to 'other spreadsheet' in range (row(1):row(5)) and column(Y)
And I want that to loop through the first spreadsheet for every column in the spreadsheet. This is what I have for 1 column:
Sheets("Info").Range("B3:B6").Value = Worksheets("Temp").Range("HK5:HK8").Value
Sheets("Info").Range("C3:C6").Value = Worksheets("Temp").Range("HK10:HK13").Value
This is what I want to do, however for every column within the first spreadsheet (there is 300 columns, manually would be tedious).
EDIT: This is another way i have found that may help explain the comments left below:
For i = 2 To 3
Worksheets("Info").Range(Cells(3, i), Cells(6, i)).Value = Worksheets("Temp").Range(Cells(5, i), Cells(8, i)).Value
Next i
I hoping this loops over the columns (2 - 290) currently its only from column 2 to 3 for testing purposes. I want the cells from TEMP worksheet from every column ('i') from row 5-8 and I want to put that into the INFO worksheet in column ('i') rows 3-6. Hope this helps!
Your description isn't consistent and I am not sure what is wrong with your final code. I have left a commented out debug statement so you can see what ranges are being worked with.
But
Use Option Explicit at the top of your code
Make sure to declare i as Long
Switch of ScreenUpdating to speed up performance
Use variables to hold the worksheets
Fully qualify Cells references with their worksheet
Public Sub test()
Dim i As Long
Dim wsInfo As Worksheet
Dim wsTemp As Worksheet
Set wsInfo = ThisWorkbook.Worksheets("Info")
Set wsTemp = ThisWorkbook.Worksheets("Temp")
Application.ScreenUpdating = False
For i = 2 To 290
' Debug.Print wsInfo.Name & "!" & wsInfo.Range(wsInfo.Cells(3, i), wsInfo.Cells(6, i)).Address & " = " & wsTemp.Name & "!" & wsTemp.Range(wsTemp.Cells(5, i), wsTemp.Cells(8, i)).Address
wsInfo.Range(wsInfo.Cells(3, i), wsInfo.Cells(6, i)).Value = wsTemp.Range(wsTemp.Cells(5, i), wsTemp.Cells(8, i)).Value
Next i
Application.ScreenUpdating = True
End Sub

Create loop index and copy data from index worksheets into Master

What I am trying to do is copy variable data ranges, but identical headers, from all sheets and paste into the Master sheet one after the other. The original code (CODE 1 below) renewed the data in the master whenever I clicked on another sheet and back onto the master. The problem now is that there are other sheets in the Workbook that I do not want included in the copy process.
I have edited the code I received below (CODE 2 below) to try and define start and end sheets for running a "loopindex" and also removing the "copy headers" line of code as the headers for each worksheet are appearing throughout the mastersheet. Obviously it does not work and I was wondering if someone could help.
Could you please help me correct the combined code or provide a more elegant solution? Thanks.
Original question here - Excel Forum post
Secondary code from here - Stack post LoopIndex
Original CODE 1
Private Sub Worksheet_Activate()
Dim ws As Worksheet
Application.ScreenUpdating = False
Me.UsedRange.Clear
For Each ws In ThisWorkbook.Worksheets
If ws.Name <> Me.Name Then
If Range("A1") = "" Then ws.Range("A1").EntireRow.Copy Me.Range("A1")'copy in the headers
ws.UsedRange.Offset(1).Copy Me.Range("A" & Rows.Count).End(xlUp).Offset(1)'copy data
End If
Next ws
Application.ScreenUpdating = True
End Sub
Edited CODE 2
Private Sub Worksheet_Activate()
Dim ws As Worksheet
Application.ScreenUpdating = False
Me.UsedRange.Clear
Dim StartIndex, EndIndex, LoopIndex As Integer
StartIndex = Sheets("Master sheet").Index + 1
EndIndex = Sheets("End").Index - 1
For LoopIndex = StartIndex To EndIndex
If Range("A1") = "" Then ws.Range("A1").Offset(1).Copy Me.Range("A" &Rows.Count).End(xlUp).Offset(1) 'copy data
Next LoopIndex
Application.ScreenUpdating = True
End Sub
I can just about understand why you had this as a Worksheet Activate event routine against worksheet "Master list" when there was only one source worksheet. I am having more difficulty in seeing this as convenient when you have multiple source worksheets. I am not asking you to justify your decision since I do not have a full understanding of workbook but you might like to reconsider your approach. I have coded the routine below as an normal macro but you can change this easily if you wish.
I do not like the approach of assuming the worksheets to be loaded are from Sheets("Master sheet").Index + 1 to Sheets("End").Index - 1. I would have thought that was unstable although I have never tried this approach.
I have created a hidden worksheet "Load List":
This lists the worksheets to be loaded in the sequence to be loaded.
I have filled worksheet "Sheet1" with data:
Not very imaginative data but it makes it easy to check that "Master list" is loaded with the correct data. Worksheets "Sheet2" to "Sheet5" have similar data except that the number of data rows vary and "S1" is replaced by "S2", "S3", "S4" and "S5".
After the macro has run, the top of "Master list" contains:
You can see I have loaded all rows from the first worksheet then data rows only from subsequent worksheets.
I do not say a great deal about the VBA I have used. Once you know a statement exists it is normally easy to look it up. Ask if necessary. I hope I have provided an adequate explanation of what the code does. Again ask if necessary.
Option Explicit
Sub CombinedSelected()
Dim ColSrcMax As Long
Dim LoadList As Variant
Dim RowListCrnt As Long
Dim RowListMax As Long
Dim RowMasterNext As Long
Dim RowSrcMax As Long
With Worksheets("Load List")
RowListMax = .Cells(Rows.Count, "A").End(xlUp).Row
' Load the values from column A of worksheet "Load List" to LoadList.
' The statement converts LoadList to a 2 dimensional array. It is the
' equivalent of Redim LoadList(1 To RowListMax, 1 to 1)
LoadList = .Range(.Cells(1, "A"), .Cells(RowListMax, "A")).Value
End With
RowMasterNext = 1
With Worksheets("Master sheet")
.Cells.EntireRow.Delete ' Delete existing contents
End With
For RowListCrnt = 2 To RowListMax
With Worksheets(LoadList(RowListCrnt, 1))
' Find last used row and column containing a value.
' Warning. These statements do not allow for any of the source worksheets being empty
RowSrcMax = .Cells.Find("*", .Range("A1"), xlFormulas, , xlByRows, xlPrevious).Row
ColSrcMax = .Cells.Find("*", .Range("A1"), xlFormulas, , xlByColumns, xlPrevious).Column
If RowListCrnt = 2 Then
' For first source worksheet only include header row
.Range(.Cells(1, 1), .Cells(RowSrcMax, ColSrcMax)).Copy _
Destination:=Worksheets("Master sheet").Cells(RowMasterNext, 1)
RowMasterNext = RowMasterNext + RowSrcMax
Else
' Data rows only to be copied
.Range(.Cells(2, 1), .Cells(RowSrcMax, ColSrcMax)).Copy _
Destination:=Worksheets("Master sheet").Cells(RowMasterNext, 1)
RowMasterNext = RowMasterNext + RowSrcMax - 1
End If
End With
Next
End Sub

Search for two values and copy everything in between in a loop

I have a worksheet which has many terms in Column A.I want to search for two terms for example
term A and term B and copy all rows between the two terms and paste it into a new sheet.These two terms may repeat in the column. The problem which I am basically facing the following problem : whenever I run my code it also copies rows between term B and term A which is unnecessary. Following is the code i am using for two terms term A and term B.
For example my column A is
Institute
Event
Job
Computer
Laptop
Figures
Event
figures
format
computer
and many more terms
I want to copy all the rows between term A: Event and term B: Laptop and paste it into a new sheet. What my code is doing is it is copying the rows between all combinations of Event and computer. Even the rows between computer and event are copied(in this case Figure and laptop).
Sub OpenHTMLpage_SearchIt()
Dim Cell As Range, Keyword$, N%, SearchAgain As VbMsgBoxResult
Dim ass As Variant
Dim Cellev As Range, prakash$, P%, SearchAgaina As VbMsgBoxResult
Dim asa As Variant
StartSearch:
N = 1
Keyword = "Event"
If Keyword = Empty Then GoTo StartSearch
For Each Cell In Range("A1:A500")
If Cell Like "*" & Keyword & "*" Then
ass = Cell.Address
P = 1
prakash = "Computer"
If prakash = Empty Then GoTo StartSearch
For Each Cellev In Range("A1:A500")
If Cellev Like "*" & prakash & "*" Then
asa = Cellev.Address
Range(asa, ass).Select
Selection.Copy
Sheets.Add After:=Sheets(Sheets.Count)
Range("B13").Select
ActiveSheet.Paste
Worksheets("sheet1").Select
P = P + 1
End If
Next Cellev
N = N + 1
End If
Next Cell
End Sub
Edit: code formatting.
The following is the code which is working for me.This copies everything in between Event and laptop and pastes it into a new sheet. Then again it searches for a second time and this time the search will start from the next row to the first search.I hope I am clear with this.
Sub Star123()
Dim rownum As Long
Dim colnum As Long
Dim startrow As Long
Dim endrow As Long
Dim lastrow As Long
rownum = 1
colnum = 1
lastrow = Worksheets("Startsheet").Range("A65536").End(xlUp).Row
With ActiveWorkbook.Worksheets("StartSheet").Range("a1:a" & lastrow)
For rownum = 1 To lastrow
Do
If .Cells(rownum, 1).Value = "Event" Then
startrow = rownum
End If
rownum = rownum + 1
If (rownum > lastrow) Then Exit For
Loop Until .Cells(rownum, 1).Value = "Laptop"
endrow = rownum
rownum = rownum + 1
Worksheets("StartSheet").Range(startrow & ":" & endrow).Copy
Sheets("Result").Select
Range("A1").Select
ActiveSheet.Paste
Next rownum
End With
End Sub
Try this:
Sub DoEeeeeet(sheetName, termA, termB)
Dim foundA As Range, _
foundB As Range
Dim newSht As Worksheet
With Sheets(sheetName).Columns(1)
Set foundA = .Find(termA)
If Not foundA Is Nothing Then
Set foundB = .Find(termB, after:=foundA, searchdirection:=xlPrevious)
End If
End With
If foundA Is Nothing Or foundB Is Nothing Then
MsgBox "Couldn't find " & IIf(foundA Is Nothing, termA, termB)
Else
Range(foundA, foundB).Copy
Set newSht = Sheets.Add
newSht.Range("B13").PasteSpecial
End If
End Sub
You can call it as follows:
DoEeeeeet "Sheet1","Event","Laptop"
It'll find the first instance of "Event" and the last instance of "Laptop" on the sheet named "Sheet1" and copy all of that data to B13 and subsequent cells in a new sheet.
Is that what you want? Or do you want each of the subranges beginning with "Event" and ending with "Laptop"?

Consolitate data from multible sheets ,re-arrange the data as per the column name

i want a macro to consolidate the data form multiple sheets to one sheet.. here i given the example ..
Sheet 1
a1:Name b1:Age
a2:sathish b2:22
a3:sarathi b3:24
.
sheet 2
a1:Age b1:Name c1:Dept
a2:60 b2:saran c2:Comp sce
a3:31 b3:rajan c3:B.com
the result should be like this
consolidate sheet
a1:Name b1:Age c1:Dept
a2:sathish b2:22
a3:sarathi b3:24
a4:saran b4:60 c4:Comp sce
a5:rajan b5:31 c5:B.com
Here is the code which i used for consolidate data-
Sub consolidate()
Dim sh As Worksheet
Dim DestSh As Worksheet
Dim Last As Long
Dim shLast As Long
Dim CopyRng As Range
Dim StartRow As Long
With Application
.ScreenUpdating = False
.EnableEvents = False
End With
Application.DisplayAlerts = False
On Error Resume Next
ActiveWorkbook.Worksheets("RDBMergeSheet").Delete
On Error GoTo 0
Application.DisplayAlerts = True
Set DestSh = ActiveWorkbook.Worksheets.Add
DestSh.Name = "RDBMergeSheet"
StartRow = 1
For Each sh In ActiveWorkbook.Worksheets
If sh.Name <> DestSh.Name Then
Last = LastRow(DestSh)
shLast = LastRow(sh)
If shLast > 0 And shLast >= StartRow Then
Set CopyRng = sh.Range(sh.Rows(StartRow), sh.Rows(shLast))
If Last + CopyRng.Rows.Count > DestSh.Rows.Count Then
MsgBox "There are not enough rows in the " & _
"summary worksheet to place the data."
GoTo ExitTheSub
End If
CopyRng.Copy
With DestSh.Cells(Last + 1, "A")
.PasteSpecial xlPasteValues
.PasteSpecial xlPasteFormats
Application.CutCopyMode = False
End With
End If
End If
Next
ExitTheSub:
Application.Goto DestSh.Cells(1)
DestSh.Columns.AutoFit
With Application
.ScreenUpdating = True
.EnableEvents = True
End With
End Sub
Function LastRow(sh As Worksheet)
On Error Resume Next
LastRow = sh.Cells.Find(What:="*", _
After:=sh.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
On Error GoTo 0
End Function
Function LastCol(sh As Worksheet)
On Error Resume Next
LastCol = sh.Cells.Find(What:="*", _
After:=sh.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Column
On Error GoTo 0
End Function
I can able consolidate the data but can't re-arrange as per the column title..
Please help me in this ..THanks in advance
First I identify some mistakes and bad practices in your code then I consider how to redesign your macro to achieve your objectives.
Issue 1
The primary purpose of On Error is to allow you to terminate tidily if an unexpected error occurs. You should not use it to avoid errors you expect and you should not ignore errors.
Consider the functions LastRow and LastCol. In both cases, if the Find fails, you ignore the error and carry on. But that means these functions return an incorrect value, so you get another error in the calling routine. If the Find fails you should investigate not ignore. This is true of any other error.
Issue 2
Find returns Nothing if the sheet is empty. You call functions LastRow and LastCol for worksheet "RDBMergeSheet" when it is empty. The code should be:
Set Rng = sh.Cells.Find( ...)
If Rng Is Nothing Then
' Sheet sh is empty
LastRow = 0
Else
LastRow = Rng.Row
End If
Here I have set LastRow to 0 if the worksheet is empty. This ceases to be a side effect of an error but a documented feature of the function: "Return value = 0 means the worksheet is empty." The calling routine must check for this value and skip any empty worksheets. There are other approaches but the key point is: provide code to handle expected or possible errors in a tidy manner. For function LastCol you need LastCol = Rng.Column.
Issue 3
The minimum syntax for a function statement is:
Function Name( ... parameters ...) As ReturnType
The two function statements should end: As Long.
Issue 4
Consider: "ActiveWorkbook.Worksheets("RDBMergeSheet")"
If you are working on multiple workbooks, ActiveWorkbook is not enough. If you are only working on one workbook, ActiveWorkbook is unnecessary. Please do not work with multiple workbooks until your understanding of Excel VBA is better.
Issue 5
You delete worksheet "RDBMergeSheet" and then recreate it which hurts my soul. More importantly, you have lost the column headings. I will discuss this matter further under Redesign.
Replace:
Application.DisplayAlerts = False
On Error Resume Next
ActiveWorkbook.Worksheets("RDBMergeSheet").Delete
On Error GoTo 0
Application.DisplayAlerts = True
Set DestSh = ActiveWorkbook.Worksheets.Add
DestSh.Name = "RDBMergeSheet"
with:
Set DestSh = Worksheets("RDBMergeSheet")
With DestSh
.Range(.Cells(2, 1), .Cells(Rows.Count, Columns.Count)).EntireRow.Delete
End With
You use Rows.Count, With and Cells in your code so I will not explain them.
.Range(.Cells(RowTop, ColLeft), .Cells(RowBottom, ColRight)) is an easy method of specifying a range with the top left and bottom right cells.
I have used .EntireRow so I do not need the column numbers. The following gives the same effect:
.Rows("2:" & Rows.Count).EntireRow.Delete
As far as I know ClearContents (which some people favour) has the same effect as Delete. It certainly takes the same number of micro-seconds. For the usages above, both remove any values or formatting from the second row to the last row of the worksheet.
The above change means that row 1 is unchanged and the column widths are not lost. I do not need AutoFit which you have used.
Issue 6
Please be systematic in the naming of your variables. You use StartRow as the first row and shLast as the last row of the source worksheet and Last as the last row of the destination worksheet. Will a colleague who takes over maintenance of your macro find this easy to understand? Will you remember it in six months when this macro needs some maintenance?
Develop a naming system that works for you. Better still, get together with colleagues and agree a single system so all your employer's macros look the same. Document this system for the benefit of future staff. I would name these variables: RowNumDestLast, RowNumSrcStart and RowNumSrcLast. That is: <purpose of variable> <worksheet> <purpose within worksheet>. This system works for me but your system could be completely different. The key feature of a good system is that you can look at your code in a year and immediately know what each statement is doing.
Issue 7
If shLast > 0 And shLast >= StartRow Then
You set StartRow to 1 and never change it so if shLast >= StartRow then shLast > 0. The following is enough:
If shLast >= StartRow Then
Issue 8
If Last + CopyRng.Rows.Count > DestSh.Rows.Count Then
MsgBox "There are not enough rows in the " & _
"summary worksheet to place the data."
GoTo ExitTheSub
End If
It is good that you are checking for conditions that will result in fatal errors but is this the most likely error? Even if you are using Excel 2003, you have room for 65,535 people and a heading line. You will break the size limit on a workbook before you exceed the maximum number of rows.
Issue 9
Set CopyRng = sh.Range(sh.Rows(StartRow), sh.Rows(shLast))
This includes the heading row in the range to be copied. Since I will suggest a totally different method later, I will not suggest a correction.
Issue 10
With DestSh.Cells(Last + 1, "A")
.PasteSpecial xlPasteValues
.PasteSpecial xlPasteFormats
Why are you pasting the values and formats separately?
Redesign
With the corrections above, the code sort of works. With your source data, it sets the destination sheet to:
Age Name Dept
Name Age
Sathish 22
Sarathi 24
Age Name Dept
60 Saran Comp sce
31 Rajan B.com
This is not what you seek. So the rest of this answer is about design: how do you achieve the appearance you seek? There are many approaches but I offer one and explain why I have picked it without discussing alternatives.
Key issues:
How do you determine which columns to consolidate and in which sequence?
If there is a column in a source worksheet that you are not expecting, what do you do? Is someone collecting information for which there is no central interest or is the column name misspelt?
I have decided to use the existing column names within worksheet "RDBMergeSheet" to determine the sequence. To prepare the macro for a new column name, just add that name to "RDBMergeSheet". If I discover a column name in a source sheet that is not in "RDBMergeSheet", I add it on the right. This second decision will highlight the error if a column name is misspelt but will not be a benefit if someone is collecting extra information in a source worksheet.
I do not copy formats to worksheet "RDBMergeSheet" since, if the source worksheets are formatted differently, each part of worksheet "RDBMergeSheet" would be different.
New statements and explanations
Const RowFirstData As Long = 2
Const WShtDestName As String = "RDBMergeSheet"
A constant means I use the name in the code and can change the value by changing the Const statement.
I assume the first row of every worksheet contains column names and the first data row is 2. I use a constant to make this assumption clear. It would be possible to use this to write code that would handle a different number of heading rows but I have not done so because it would complicate the code for little advantage.
ColNumDestLast = .Cells(1, Columns.Count).End(xlToLeft).Column
.Cells(1, Columns.Count) identifies the last column of row 1 which I assume is blank. .End(xlToLeft) is the VBA equivalent of the keyboard Ctrl+Left. If .Cells(1, Columns.Count) is blank, .Cells(1, Columns.Count).End(xlToLeft) returns the first cell to the left which is not blank. .Column gives the column number of that cell. That is, this statement sets ColNumDestStart to the column number of the last cell in row 1 with a value.
ColHeadDest = .Range(.Cells(1, 1), .Cells(1, ColNumDestLast)).Value
This copies the values from row 1 to the variant array ColHeadDest. ColHeadDest will be redimensioned by this statement to (1 to 1, 1 to ColNumDestLast). The first dimension is for the rows, of which there is only one, and the second dimension is for the columns.
Replacement consolidate
I hope I have added enought comments for the code to make sense. You still need the corrected LastRow and LastCol. I could have replaced LastRow and LastCol but I think I have provided enough new code to be getting on with.
Option Explicit
Sub consolidate()
Dim ColHeadCrnt As String
Dim ColHeadDest() As Variant
Dim ColNumDestCrnt As Long
Dim ColNumDestLast As Long
Dim ColNumSrcCrnt As Long
Dim ColNumSrcLast As Long
Dim Found As Boolean
Dim RowNumDestCrnt As Long
Dim RowNumDestStart As Long
Dim RowNumSrcCrnt As Long
Dim RowNumSrcLast As Long
Dim WShtDest As Worksheet
Dim WShtSrc As Worksheet
Dim WShtSrcData() As Variant
Const RowNumFirstData As Long = 2
Const WShtDestName As String = "RDBMergeSheet"
'With Application
' .ScreenUpdating = False ' Don't use these
' .EnableEvents = False ' during development
'End With
Set WShtDest = Worksheets(WShtDestName)
With WShtDest
' Clear existing data and load column headings to ColHeadDest
.Rows("2:" & Rows.Count).EntireRow.Delete
ColNumDestLast = .Cells(1, Columns.Count).End(xlToLeft).Column
ColHeadDest = .Range(.Cells(1, 1), _
.Cells(1, ColNumDestLast)).Value
End With
' Used during development to check array loaded correctly
'For ColNumDestCrnt = 1 To ColNumDestLast
' Debug.Print ColHeadDest(1, ColNumDestCrnt)
'Next
RowNumDestStart = RowNumFirstData ' Start for first source worksheet
For Each WShtSrc In Worksheets
ColNumSrcLast = LastCol(WShtSrc)
RowNumSrcLast = LastRow(WShtSrc)
If WShtSrc.Name <> WShtDestName And _
RowNumSrcLast <> 0 Then
' Source sheet is not destination sheet and it is not empty.
With WShtSrc
' Load entire worksheet to array
WShtSrcData = .Range(.Cells(1, 1), _
.Cells(RowNumSrcLast, ColNumSrcLast)).Value
End With
With WShtDest
For ColNumSrcCrnt = 1 To ColNumSrcLast
' For each column in source worksheet
Found = False
ColHeadCrnt = WShtSrcData(1, ColNumSrcCrnt)
' Find matching column in destination worksheet
For ColNumDestCrnt = 1 To ColNumDestLast
If ColHeadCrnt = ColHeadDest(1, ColNumDestCrnt) Then
Found = True
Exit For
End If
Next ColNumDestCrnt
If Not Found Then
' Current source column's name is not present in the
' destination sheet Add new column name to array and
' destination worksheet
ColNumDestLast = ColNumDestLast + 1
ReDim Preserve ColHeadDest(1 To 1, 1 To ColNumDestLast)
ColNumDestCrnt = ColNumDestLast
With .Cells(1, ColNumDestCrnt)
.Value = ColHeadCrnt
.Font.Color = RGB(255, 0, 0)
End With
ColHeadDest(1, ColNumDestCrnt) = ColHeadCrnt
End If
' I could extract data from WShtSrcData to another array
' suitable for downloading to a column of a worksheet but
' it is easier to move the data directly to the worksheet.
' Also, athought downloading via an array is marginally
' faster than direct access, loading the array will reduce,
' and perhaps eliminate, the time benefit of using an array.
RowNumDestCrnt = RowNumDestStart
For RowNumSrcCrnt = RowNumFirstData To RowNumSrcLast
' Copy value from array of source data to destination sheet
.Cells(RowNumDestCrnt, ColNumDestCrnt) = _
WShtSrcData(RowNumSrcCrnt, ColNumSrcCrnt)
RowNumDestCrnt = RowNumDestCrnt + 1
Next
Next ColNumSrcCrnt
End With ' WShtDest
' Adjust RowNumDestStart ready for next source worksheet
RowNumDestStart = RowNumDestStart + RowNumSrcLast - RowNumFirstData + 1
End If ' Not destination sheet and not empty source sheet
Next WShtSrc
With WShtDest
' Leave workbook with destination worksheet visible
.Activate
End With
'With Application
' .ScreenUpdating = True
' .EnableEvents = True
'End With
End Sub