【问题标题】:VBA Move specific rows to unique new workbooksVBA 将特定行移动到唯一的新工作簿
【发布时间】:2019-11-29 11:58:53
【问题描述】:

对于比我更了解 VBA 的人来说,这可能真的很容易。如果是将行复制到新工作表中的情况,我可以看到如何执行此操作,但是在复制满足条件的第一个行后,我还没有弄清楚如何让它移动到下一行。我知道我需要告诉它寻找下一行,但我找不到正确的命令。

我希望让宏循环遍历我指定的数据范围,并将一次满足条件的一行复制到一个新的唯一工作簿中。例如,我有 10 条符合条件的记录,我想要输出 10 个工作簿,每个工作簿有一行数据。

目前的代码是:

Sub DPD()

Dim Ws As Worksheet
Dim Items As Range
Dim Item As Range

Set Ws = Worksheets("Out")
Set Items = Ws.Range("MyRange")

For Each Item In Items

Application.DisplayAlerts = False

'If value in column C > 0, copy row to new workbook and save
If Item.Value > 0 Then

    'Select row in active Sheet to copy
    Item.EntireRow.Copy

    'Paste row into new spreadsheet
    Workbooks.Add
    Selection.PasteSpecial Paste:=xlPasteValues
    Application.CutCopyMode = False
    ChDir "C:\DPD"
    ActiveWorkbook.SaveAs Filename:="C:\DPD\pf_" & Format(CStr(Now), "yyy_mm_dd_hh_mm") & ".csv", FileFormat:=xlCSV
    ActiveWindow.Close

    Application.DisplayAlerts = True


End If

Next Item

链接到示例sheet

【问题讨论】:

  • 问题可能出在您的命名...Format(CStr(Now), "yyy_mm_dd_hh_mm")... - 您每分钟只会获得一次新文件名,但宏运行得更快,因此您的每个具有唯一行的文件都会被简单地覆盖。尝试添加一些计数器:dim counter as long,然后在每次迭代中使用...Format(CStr(Now), "yyy_mm_dd_hh_mm") & counter...counter = counter +1 更新循环。
  • 将您的 For Each Item In Items 更改为 For Each Item In Items.Rows。然后引用您的单元格(即:If Item.Cells(1,1).Value > 0 Then

标签: excel vba


【解决方案1】:

正如我在评论中提到的,问题在于您的命名。这是为您重构的代码。检查一下,因为我删除了一些东西并移动了其他东西。

Sub DPD()

Dim Items As range
Dim Item As range

'Dim WS As Worksheet
Dim newWS As Worksheet
Dim counter As Long

'Set WS = ThisWorkbook.Sheets("sheet_name") ' try avoiding ActiveWorkbook/Sheet
'Set Items = WS.range("MyRange") ' - this is not necessary if you already have a named range

Application.DisplayAlerts = False ' thats right to switch off notifications
Application.ScreenUpdating = False ' but another good idea is to switch off screen update - this will allow vba to work much faster and you won't see blinking display

For Each Item In Range("MyRange") ' here's where you may use your named range directly

'--------------------------------------------------------------------------
' As per @Zac's comment:    
'For Each Item In Items.Rows ' should change the For loop condition
'If Item.Cells(1, 1).Value > 0 Then ' and also update an If statement
'--------------------------------------------------------------------------


'If value in column C > 0, copy row to new workbook and save
    If Item.Value > 0 Then
        Workbooks.Add
        Set newWS = ActiveSheet ' Here is the place where I can't avoid using "ActiveSheet"
        'Select row in active Sheet to copy
        Item.EntireRow.Copy

 'ChDir "C:\DPD" ' no need to change default directory, as you are providing full file name below ↓

        'Paste row into new spreadsheet
        With newWS
            .Cells(1, 1).PasteSpecial Paste:=xlPasteValues
            .Parent.SaveAs FileName:="C:\DPD\pf_" & Format(CStr(Now), "yyy_mm_dd_hh_mm") & counter & ".csv", FileFormat:=xlCSV
            .Parent.Close
        End With
        counter = counter + 1
    End If
Next Item

Application.DisplayAlerts = True
Application.ScreenUpdating = True

End Sub

更新

根据 Zac 的评论 - 范围内确实可能不止一列,因此我已将他的建议添加到我的答案中。

【讨论】:

  • 不确定这是否可行。您遇到的问题是,如果范围超过 1 列,则每次复制同一行的次数与新工作簿中的列数相同。因此,如果该范围内有 2 列和 3 行,那么您将在 2 个不同的工作簿中复制 row1 两次……依此类推。查看我的 cmets 以解决问题
  • @Zac 我知道,通过查看 OP 的代码,您可能会发现评论 'If value in column C > 0,... ,所以我假设范围设置为仅一列。但是,尽管如此,我一到我的电脑就会用你的观点更新我的答案:)
  • 谢谢两位,我会测试你的建议,看看会发生什么:)
  • 好的,我已经对此进行了测试。我认为问题如上所述,它没有复制以下行。我有大约 10 行,它可以更改为 1-100 之间的任何行数。 C 列包含一个数值,如果该行包含其他列中的数据,则该数值将始终被填充,这就是我使用“If Item.Value > 0 Then”的原因。上面的方法可以复制第一行,但是当它循环返回时,它没有向下选择第二行,它会再次将第一行复制到一个新的工作簿中。
  • 以上对我不起作用。循环数百次,直到我结束它并一次又一次地创建相同的一行记录。
【解决方案2】:

按照我的建议,试试下面的UDF

Sub DPD()

    ' Variable Declarations
    Dim rItem As Range
    Dim oNewWB As Workbook
    Dim oNewWS As Worksheet
    Dim iCounter As Long: iCounter = 0

    ' Switch updates off
    Application.DisplayAlerts = False
    Application.ScreenUpdating = False

    ' Loop through all rows in the range
    For Each rItem In Range("MyRange").Rows

        ' Check if column C has a value greater than 0. NOTE: if the cell has text in it (i.e. header column will have text)
        '                                                     excel counts the number of characters in the cell.. which will
        '                                                     be greater than 0 so that line will be copied in a new workbook
        If rItem.Cells(1, 3).Value > 0 Then

            ' Create new workbook and get the first sheet
            Set oNewWB = Workbooks.Add
            Set oNewWS = oNewWB.Sheets(1)

            ' Copy current row
            rItem.Cells(1, 1).EntireRow.Copy

            ' Paste row in new worksheet
            oNewWS.Cells(1, 1).PasteSpecial Paste:=xlPasteValues

            ' Save new workbook and close it
            iCounter = iCounter + 1
            oNewWB.SaveAs Filename:="C:\DPD\pf_" & Format(CStr(Now), "yyy_mm_dd_hh_mm") & counter & ".csv", FileFormat:=xlCSV
            oNewWB.Close

        End If
    Next

    ' Turn on updates
    Application.DisplayAlerts = True
    Application.ScreenUpdating = True

End Sub

【讨论】:

  • 谢谢,我已经对此进行了测试,它只为 10 行生成了两个工作簿。工作簿也是空白的。
  • 你能提供一份你的工作表样本吗?
  • 我已经上传了一份副本,其中的数据替换为此处的虚拟详细信息:gofile.io/?c=8jA3HM
  • 今晚我去看看
  • 我注意到我已更改的 VBA 中有一个错误,它现在正在生成文件,但当它到达最后一行数据时并没有停止,不幸的是它继续生成 100 个文件,这是我命名范围内的行。
【解决方案3】:

您的代码基本上可以工作,唯一的问题是文件创建速度如此之快,以至于它们都是在同一秒内创建的,并且

ActiveWorkbook.SaveAs Filename:="C:\DPD\pf_" & Format(CStr(Now), "yyy_mm_dd_hh_mm") & ".csv", FileFormat:=xlCSV

不允许您区分文件。因此,您的所有文件都将具有相同的时间戳并被覆盖。一个简单的解决方案是添加一个计数器

iCounter

每个新文件都会增加,就像这段编辑过的代码一样:

Sub DPD()

Dim Ws As Worksheet
Dim Items As Range
Dim Item As Range
Dim iCounter As Integer

Set Ws = Worksheets("Out")
Set Items = Ws.Range("MyRange")

For Each Item In Items

Application.DisplayAlerts = False

'If value in column C > 0, copy row to new workbook and save
If Item.Value > 0 Then

    'Select row in active Sheet to copy
    Item.EntireRow.Copy

    'Paste row into new spreadsheet
    Workbooks.Add
    Selection.PasteSpecial Paste:=xlPasteValues
    Application.CutCopyMode = False
    ChDir "C:\DPD"

    iCounter = iCounter + 1
    ActiveWorkbook.SaveAs Filename:="C:\DPD\pf_" & Format(CStr(Now), "yyy_mm_dd_hh_mm") & iCounter & ".csv", FileFormat:=xlCSV
    ActiveWindow.Close




End If

Next Item

Application.DisplayAlerts = True

End Sub

请注意我也移动了

Application.DisplayAlerts = True

退出循环。否则,屏幕更新将在第一次复制/粘贴后立即开启。

【讨论】:

    猜你喜欢
    • 2017-02-09
    • 2016-02-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多