简体   繁体   中英

VBA export only cells with data

I'm trying to go through many worksheets in a workbook and only export data from cells in column B that contain data.

Right now exporting is very slow since I'm selecting everything in column B and writing it to a text file.

I'm new to VBA and this macro was put together from online searches.

Sub Export()
Application.ScreenUpdating = False
Application.EnableEvents = False
'Remember original sheet
Set mySheet = ActiveSheet

For Each sht In ActiveWorkbook.Worksheets
    sht.Activate
    Columns("B").Select
Next sht

Dim myFile As String, cellValue As Variant, rng As Range, i As Long, j As Integer
Dim fso As Object
Set fso = CreateObject("Scripting.FileSystemObject")
myFile = fso.GetBaseName(ActiveWorkbook.Name) & ".txt"
Set rng = Selection
Open myFile For Output As #1
       For i = 1 To rng.Rows.Count
            For j = 1 To rng.Columns.Count
cellValue = rng.Cells(i, j).Value
If j = rng.Columns.Count Then
    Write #1, cellValue
Else
    Write #1, cellValue,
End If
    Next j
Next i
Close #1
'Remove extra quotes
Dim r As Range, c As Range
Dim sTemp As String
Open myFile For Output As #1
For Each r In Selection.Rows
    sTemp = ""
    For Each c In r.Cells
        sTemp = sTemp & c.Text & Chr(9)
    Next c
    'Get rid of trailing tabs
    While Right(sTemp, 1) = Chr(9)
        sTemp = Left(sTemp, Len(sTemp) - 1)
    Wend
    Print #1, sTemp
Next r
Close #1
'Return to original sheet
mySheet.Activate
Application.ScreenUpdating = True
Application.EnableEvents = True
MsgBox "Done"
End Sub

EDIT:

I can quickly export cells with a value on current worksheet. It will not cycle through all worksheets.

For Each ws In ThisWorkbook.Worksheets
    Range("B12:B1746").SpecialCells(xlCellTypeConstants, xlTextValues).Select
Next ws

EDIT 2:

This works, but I'm going to work on it more. Feel free to add suggestions.

Sub CopyRangeFromMultiWorksheets()
'Remember original sheet
Set mySheet = ThisWorkbook.ActiveSheet
Dim sh As Worksheet
Dim DestSh As Worksheet
Dim Last As Long
Dim CopyRng As Range

With Application
    .ScreenUpdating = False
    .EnableEvents = False
End With

'Delete the sheet "RDBMergeSheet" if it exist
Application.DisplayAlerts = False
On Error Resume Next
ThisWorkbook.Worksheets("RDBMergeSheet").Delete
On Error GoTo 0
Application.DisplayAlerts = True

'Add a worksheet with the name "RDBMergeSheet"
Set DestSh = ThisWorkbook.Worksheets.Add
DestSh.Name = "RDBMergeSheet"

'loop through all worksheets and copy the data to the DestSh
For Each sh In ThisWorkbook.Worksheets
    'Error if not unprotected first
    'ActiveSheet.Unprotect Password:=""
    If sh.Name <> DestSh.Name Then

        'Find the last row with data on the DestSh
        Last = LastRow(DestSh)

        'Fill in the range that you want to copy
        Set CopyRng = sh.Range("B12:B1746").SpecialCells(xlCellTypeConstants, xlTextValues)

        'Test if there enough rows in the DestSh to copy all the data
        If Last + CopyRng.Rows.Count > DestSh.Rows.Count Then
            MsgBox "There are not enough rows in the Destsh"
            GoTo ExitTheSub
        End If

        'This example copies values/formats, if you only want to copy the
        'values or want to copy everything look at the example below this macro
        CopyRng.Copy
        With DestSh.Cells(Last + 1, "A")
            .PasteSpecial xlPasteValues
            .PasteSpecial xlPasteFormats
            Application.CutCopyMode = False
        End With

        'Optional: This will copy the sheet name in the H column
        DestSh.Cells(Last + 1, "H").Resize(CopyRng.Rows.Count).Value = sh.Name

    End If
Next

ExitTheSub:

Application.Goto DestSh.Cells(1)

'AutoFit the column width in the DestSh sheet
DestSh.Columns.AutoFit

'Copy to txt
Dim iCntr
Dim myFile As String
Dim strFile_Path As String
Dim fso As Object
Set fso = CreateObject("Scripting.FileSystemObject")
myFile = fso.GetBaseName(ActiveWorkbook.Name) & ".txt"
Open myFile For Output As #1
For iCntr = 1 To LastRow(DestSh)
Print #1, Range("A" & iCntr)
Next iCntr
Close #1
'Remove helper sheet without alert
Application.DisplayAlerts = False
ThisWorkbook.Worksheets("RDBMergeSheet").Delete
Application.DisplayAlerts = True
'Return to original sheet
mySheet.Activate
Application.ScreenUpdating = True
Application.EnableEvents = True
MsgBox "Done"
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

You've got a bit of a multi-step problem here. I'll try to cover the biggest items at a high-level, to try to make it easier for you to tackle (or ask follow-up questions on) each of the individual issues in turn.

For looping through worksheets, you'll probably want something like this:

For Each ws In ThisWorkbook.Worksheets

    ' Insert your main actions within here, instead of after here

Next ws

Right now, your first loop isn't really doing anything. It's just unnecessarily "touching" each sheet, and then moving on to the rest of the code.

More than likely, you'll want to take each action you want to do and place them within the loop.

Also, use ThisWorkbook instead of ActiveWorkbook to avoid edge case issues when you have multiple books open.

Because you're having speed issues, it's best to try to avoid Select or Activate whenever you're copying columns. Try something like this:

...
Const RANGE_BASE As String = "B1:B"
Dim rangeToImport As String
Dim Items() As Variant

rangeToImport = RANGE_BASE & CStr(ReturnLastUsedRow(ws:=ws))
Items = ws.Range(rangeToImport)
...

Private Function ReturnLastUsedRow(ByVal ws As Worksheet) As Long

    Const CUTOFF_ROW As Long = 1000000
    Const SELECTED_COLUMN As String = "B"

    ReturnLastUsedRow = ws.Cells(CUTOFF_ROW, SELECTED_COLUMN).End(xlUp).Row

End Function

The above hard-codes the column (instead of relying simply on what's active). Then, it saves the contents of the given column into an array that you can use later on.

A separate helper function is provided to help determine the max length of your range. This is to ensure that you're not looping through EVERY row, just the ones with stuff in it.

I'm not sure if you need to export the columns individually, or if you need to export them as a whole? If the former, then you should be able to export within each iteration of the For Loop. If the latter, you may want to turn the array into a multi-dimensional array and increase its size on each iteration of the loop.

One you have this part cleaned up, you should be good with the export. It'll be a matter of looping through the array instead of looping through the rows, which should speed things up a little.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM