MyTetra Share
Делитесь знаниями!
Как собрать данные с нескольких листов или книг?
Время создания: 16.03.2019 23:43
Текстовые метки: FileDialog, fso, Open, Workbooks.Open
Раздел: !Закладки - VBA - GetOpen
Запись: xintrea/mytetra_db_adgaver_new/master/base/15149917892b3npykthu/text.html на raw.githubusercontent.com

Как собрать данные с нескольких листов или книг?

Очень часто бывает необходимо собрать данные с нескольких листов одной книги или даже с листов нескольких книг. Например, каждую неделю мы получаем некие отчеты от отделов, которые необходимо собрать в одну общую таблицу для построения сводной таблицы. Или это могут быть некие книги прайсов по товарам от разных поставщиком, который опять же надо сначала объединить, а потом уже анализировать. Вручную делать это довольно муторно. И то, муторно это только для первых 20-ти листов/файлов, потом становится просто тошно. Поэтому решил поделиться решением, которое поможет собрать данные со всех листов книги, со всех листов всех указанных книг или только с указанных листов:

'--------------------------------------------------------------------------------------- ' Module : mConsolidated ' DateTime : 02.02.2010 17:06 ' Author : The_Prist(Щербаков Дмитрий) ' Purpose : http://www.excel-vba.ru/chto-umeet-excel/kak-sobrat-dannye-s-neskolkix-listov-ili-knig/ ' Процедура сбора данных с нескольких листов/книг '--------------------------------------------------------------------------------------- Option Explicit Sub Consolidated_Range_of_Books_and_Sheets() Dim iBeginRange As Object, lCalc As Long, lCol As Long Dim oAwb As String, sCopyAddress As String, sSheetName As String Dim lLastrow As Long, lLastRowMyBook As Long, li As Long, iLastColumn As Integer Dim wsSh As Object, wsDataSheet As Object, bPolyBooks As Boolean, avFiles Dim wbAct As Workbook Dim bPasteValues As Boolean On Error Resume Next 'Выбираем диапазон выборки с книг Set iBeginRange = Application.InputBox("Выберите диапазон сбора данных." & vbCrLf & _ "1. При выборе только одной ячейки данные будут собраны со всех листов начиная с этой ячейки. " & _ vbCrLf & "2. При выделении нескольких ячеек данные будут собраны только с указанного диапазона всех листов.", Type:=8) 'для указания диапазона без диалогового окна: 'Set iBeginRange = Range("A1:A10") 'диапазон указывается нужный 'Если диапазон не выбран - завершаем процедуру If iBeginRange Is Nothing Then Exit Sub 'Указываем имя листа 'Допустимо указывать в имени листа символы подставки ? и *. 'Если указать только * то данные будут собираться со всех листов sSheetName = InputBox("Введите имя листа, с которого собирать данные(если не указан, то данные собираются со всех листов)", "Параметр") 'Если имя листа не указано - данные будут собраны со вех листов If sSheetName = "" Then sSheetName = "*" On Error GoTo 0 'Запрос - вставлять на результирующий лист все данные 'или только значения ячеек (без формул и форматов) bPasteValues = (MsgBox("Вставлять только значения?", vbQuestion + vbYesNo, "Excel-VBA") = vbYes) 'Запрос сбора данных с книг(если Нет - то сбор идет с активной книги) If MsgBox("Собрать данные с нескольких книг?", vbInformation + vbYesNo, "Excel-VBA") = vbYes Then avFiles = Application.GetOpenFilename("Excel files(*.xls*),*.xls*", , "Выбор файлов", , True) If VarType(avFiles) = vbBoolean Then Exit Sub bPolyBooks = True lCol = 1 Else avFiles = Array(ThisWorkbook.FullName) End If 'отключаем обновление экрана, автопересчет формул и отслеживание событий 'для скорости выполнения кода и для избежания ошибок, если в книгах есть иные коды With Application lCalc = .Calculation .ScreenUpdating = False: .EnableEvents = False: .Calculation = xlManual End With 'создаем новый лист в книге для сбора Set wsDataSheet = ActiveWorkbook.Sheets.Add(After:=Sheets(Sheets.Count)) 'если нужно сделать сбор данных на новый лист книги с кодом 'Set wsDataSheet = ThisWorkbook.Sheets.Add(After:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count)) 'цикл по книгам For li = LBound(avFiles) To UBound(avFiles) If bPolyBooks Then Set wbAct = Workbooks.Open(Filename:=avFiles(li)) Else Set wbAct = ThisWorkbook End If oAwb = wbAct.Name 'цикл по листам For Each wsSh In wbAct.Sheets If wsSh.Name Like sSheetName Then 'Если имя листа совпадает с именем листа, в который собираем данные 'и сбор идет только с активной книги - то переходим к следующему листу If wsSh.Name = wsDataSheet.Name And bPolyBooks = False Then GoTo NEXT_ With wsSh Select Case iBeginRange.Count Case 1 'собираем данные начиная с указанной ячейки и до конца данных lLastrow = .Cells(1, 1).SpecialCells(xlLastCell).Row iLastColumn = .Cells.SpecialCells(xlLastCell).Column sCopyAddress = .Range(.Cells(iBeginRange.Row, iBeginRange.Column), .Cells(lLastrow, iLastColumn)).Address Case Else 'собираем данные с фиксированного диапазона sCopyAddress = iBeginRange.Address End Select lLastRowMyBook = wsDataSheet.Cells.SpecialCells(xlLastCell).Row + 1 'вставляем имя книги, с которой собраны данные If lCol Then wsDataSheet.Cells(lLastRowMyBook, 1).Resize(Range(sCopyAddress).Rows.Count).Value = oAwb If bPasteValues Then 'если вставляем только значения .Range(sCopyAddress).Copy wsDataSheet.Cells(lLastRowMyBook, 1).Offset(, lCol).PasteSpecial xlPasteValues Else .Range(sCopyAddress).Copy wsDataSheet.Cells(lLastRowMyBook, 1).Offset(, lCol) End If End With End If NEXT_: Next wsSh If bPolyBooks Then wbAct.Close False Next li With Application .ScreenUpdating = True: .EnableEvents = True: .Calculation = lCalc End With End Sub


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

'---------------------------------------------------------------------------------------

' Module    : mConsolidated

' DateTime  : 02.02.2010 17:06

' Author    : The_Prist(Щербаков Дмитрий)

' Purpose   : http://www.excel-vba.ru/chto-umeet-excel/kak-sobrat-dannye-s-neskolkix-listov-ili-knig/

'             Процедура сбора данных с нескольких листов/книг

'---------------------------------------------------------------------------------------

Option Explicit

 

Sub Consolidated_Range_of_Books_and_Sheets()

    Dim iBeginRange As Object, lCalc As Long, lCol As Long

    Dim oAwb As String, sCopyAddress As String, sSheetName As String

    Dim lLastrow As Long, lLastRowMyBook As Long, li As Long, iLastColumn As Integer

    Dim wsSh As Object, wsDataSheet As Object, bPolyBooks As Boolean, avFiles

    Dim wbAct As Workbook

    Dim bPasteValues As Boolean

    

    On Error Resume Next

    'Выбираем диапазон выборки с книг

    Set iBeginRange = Application.InputBox("Выберите диапазон сбора данных." & vbCrLf & _

                                           "1. При выборе только одной ячейки данные будут собраны со всех листов начиная с этой ячейки. " & _

                                           vbCrLf & "2. При выделении нескольких ячеек данные будут собраны только с указанного диапазона всех листов.", Type:=8)

    'для указания диапазона без диалогового окна:

    'Set iBeginRange = Range("A1:A10") 'диапазон указывается нужный

    'Если диапазон не выбран - завершаем процедуру

    If iBeginRange Is Nothing Then Exit Sub

    'Указываем имя листа

    'Допустимо указывать в имени листа символы подставки ? и *.

    'Если указать только * то данные будут собираться со всех листов

    sSheetName = InputBox("Введите имя листа, с которого собирать данные(если не указан, то данные собираются со всех листов)", "Параметр")

    'Если имя листа не указано - данные будут собраны со вех листов

    If sSheetName = "" Then sSheetName = "*"

    On Error GoTo 0

    'Запрос - вставлять на результирующий лист все данные

    'или только значения ячеек (без формул и форматов)

    bPasteValues = (MsgBox("Вставлять только значения?", vbQuestion + vbYesNo, "Excel-VBA") = vbYes)

    'Запрос сбора данных с книг(если Нет - то сбор идет с активной книги)

    If MsgBox("Собрать данные с нескольких книг?", vbInformation + vbYesNo, "Excel-VBA") = vbYes Then

        avFiles = Application.GetOpenFilename("Excel files(*.xls*),*.xls*", , "Выбор файлов", , True)

        If VarType(avFiles) = vbBoolean Then Exit Sub

        bPolyBooks = True

        lCol = 1

    Else

        avFiles = Array(ThisWorkbook.FullName)

    End If

    'отключаем обновление экрана, автопересчет формул и отслеживание событий

    'для скорости выполнения кода и для избежания ошибок, если в книгах есть иные коды

    With Application

        lCalc = .Calculation

        .ScreenUpdating = False: .EnableEvents = False: .Calculation = xlManual

    End With

    'создаем новый лист в книге для сбора

    Set wsDataSheet = ActiveWorkbook.Sheets.Add(After:=Sheets(Sheets.Count))

    'если нужно сделать сбор данных на новый лист книги с кодом

    'Set wsDataSheet = ThisWorkbook.Sheets.Add(After:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count))

    'цикл по книгам

    For li = LBound(avFiles) To UBound(avFiles)

        If bPolyBooks Then

            Set wbAct = Workbooks.Open(Filename:=avFiles(li))

        Else

            Set wbAct = ThisWorkbook

        End If

        oAwb = wbAct.Name

        'цикл по листам

        For Each wsSh In wbAct.Sheets

            If wsSh.Name Like sSheetName Then

                'Если имя листа совпадает с именем листа, в который собираем данные

                'и сбор идет только с активной книги - то переходим к следующему листу

                If wsSh.Name = wsDataSheet.Name And bPolyBooks = False Then GoTo NEXT_

                With wsSh

                    Select Case iBeginRange.Count

                    Case 1 'собираем данные начиная с указанной ячейки и до конца данных

                        lLastrow = .Cells(1, 1).SpecialCells(xlLastCell).Row

                        iLastColumn = .Cells.SpecialCells(xlLastCell).Column

                        sCopyAddress = .Range(.Cells(iBeginRange.Row, iBeginRange.Column), .Cells(lLastrow, iLastColumn)).Address

                    Case Else 'собираем данные с фиксированного диапазона

                        sCopyAddress = iBeginRange.Address

                    End Select

                    lLastRowMyBook = wsDataSheet.Cells.SpecialCells(xlLastCell).Row + 1

                    'вставляем имя книги, с которой собраны данные

                    If lCol Then wsDataSheet.Cells(lLastRowMyBook, 1).Resize(Range(sCopyAddress).Rows.Count).Value = oAwb

                    If bPasteValues Then 'если вставляем только значения

                        .Range(sCopyAddress).Copy

                        wsDataSheet.Cells(lLastRowMyBook, 1).Offset(, lCol).PasteSpecial xlPasteValues

                    Else

                        .Range(sCopyAddress).Copy wsDataSheet.Cells(lLastRowMyBook, 1).Offset(, lCol)

                    End If

                End With

            End If

NEXT_:

        Next wsSh

        If bPolyBooks Then wbAct.Close False

    Next li

    With Application

        .ScreenUpdating = True: .EnableEvents = True: .Calculation = lCalc

    End With

End Sub

Приведенный выше код необходимо вставить в стандартный модуль(Что такое модуль? Какие бывают модули?). Выполнить его можно будет из этой книги нажатием клавиш Alt+F8. В появившемся окне выбрать Consolidated_Range_of_Books_and_Sheets и нажать Выполнить. Так же можно создать на листе кнопку и назначить ей данный макрос. Так же, если впервые работаете с макросами настоятельно рекомендую прочитать статью: Что такое макрос и где его искать?, а так же Почему не работает макрос?

После вызова макроса поочередно будут появляется запросы, в которых надо будет указать исходные параметры:

  • Диапазон сбора данных - Если в окне выбора диапазона выбрать только одну ячейку, то данные будут собраны со всех листов книги/книг, начиная с этой ячейки и до последней ячейки листа.
    Если выбрать несколько ячеек, данные будут собраны только с указанного диапазона всех листов книги/книг.
  • Имя листа - Необязателен для указания. Если не указан - данные будут собраны со всех листов. Указать можно как точное соответствие имени листа, так и с частичным соответствием. Например, если в книгах для сбора данных необходимо собрать данные только с листа "Январь", то следует так и указать - "Январь". Если требуется собрать данные только с листов, начинающихся с "Продажи"("Продажи ЮГ", "Продажи НН", "Продажи Запад" и т.д.), то следует применить символ подстановки звездочку - "Продажи*". Если надо собрать с листов, содержащих в имени "продажи"("Итоговые продажи ЮГ", "Продажи НН", "Сезонные продажи" и т.д.), то указываем "*продажи*". Если надо собрать только с листа "Сезонные продажи", но известно, что вместо пробела может быть нижнее подчеркивание или тире("Сезонные продажи", "Сезонные_продажи", "Сезонные-продажи") или иной символ, то можно также применить звездочку - "Сезонные*продажи". Но если среди листов могут встречаться и такие как "Сезонные разовые продажи", "Сезонные корпоративные продажи" и т.п., но информацию с них собирать не надо, то можно применить вопросительный знак - "Сезонные?продажи". Вопросительный знак заменяет любой один символ, звездочка - любое количество любых символов.
  • Далее появится запрос: Вставлять только значения? - если выбрать Да, то в результирующий лист с листов будут вставлены исключительно значения ячеек (без формул, форматов, заливки ячеек, цвета шрифта и т.п.). Может пригодится, если на листах для сбора записаны формулы, ссылающиеся на другие листы, книги, диапазоны. При обычном копировании может случиться так, что формула выдаст ошибку, т.к. в книге для вставки нет таких листов и диапазонов или данные расположены иначе. Если выбрать Нет, то все ячейки с листов на результирующий будут копироваться в точности как в исходных листах.
  • И последний запрос: Собрать данные с нескольких книг? - если выбрать Да, то появится диалоговое окно выбора файлов. Надо указать все файлы, данные с которых необходимо собрать. Если выбрать Нет, то данные будут собираться с листов только активной книги.

Данные будут собраны на новый лист книги с макросом. Если данные собирались с нескольких книг, то в первый столбец будут занесены имена книг, с которых собраны данные.

Если после сбора данных обнаружили, что после каждого файла/листа много пустых строк, то следует найти в коде строку:
lLastrow = .Cells(1, 1).SpecialCells(xlLastCell).Row
и заменить её на строку примерно следующего содержания:
lLastrow = .Cells(.Rows.Count, 1).End(xlUp).Row
где 1 - это номер столбца на листах данных, в котором искать последнюю заполненную ячейку.
Актуально это для файлов с одинаковой структурой. Например, если сбор идет с листов по продажам, то вполне может быть такое, что в столбце 1 может не быть данных. Поэтому следует определить номер столбца, в котором наполнение данных максимально. Например, это может быть столбец с наименованиями товара или с суммами. Если это столбец D, то следует строку записать так:
lLastrow = .Cells(.Rows.Count, 4).End(xlUp).Row 'ищем последнюю строку в 4-м столбце
Подробнее про определение последней строки можно прочитать в статье:
Как определить последнюю ячейку на листе через VBA?

Важное замечание: Если вы используете Excel 2007 и выше и файлы для сбора данных тоже в этом формате, то следует скачанный файл сначала сохранить в формат "Книга Excel с поддержкой макросов(.xlsm)", закрыть и открыть заново. Иначе есть шанс получить ошибку при сборе данных, т.к. Excel будет в режиме совместимости и не сможет поместить на результирующий лист более 65536 строк.

Скачать пример:

  Tips_Macro_Consolidated.xls (50,0 KiB, 26 836 скачиваний)

Прикрепленные файлы:
Так же в этом разделе:
 
MyTetra Share v.0.59
Яндекс индекс цитирования