Страницы: 1
RSS
Суммирование ежемесячно повторяющихся столбцов которые имеют нужное название и попадают в диапазон указанных месяцев при помощи VBA
 
Всем доброго времени суток, решил по чуть чуть уходить от своих формул.
Первым делом попала формула суммесли().
Есть БД договоров, каждая строка это отдельный договор,  необходимо посчитать для каждой строки (начиная с 5) сумму столбцов с названием "Реализация" в строке 3 и за период попадающий в выбранный диапазон месяцев включительно  (ячейки AM1 и AO1).
т.е. формулой я решал этот вопрос находил 1 и последний месяц участвующие в расчете из него делал ИНДКСОМ диапазон и дальше уже искал.
Решение я  получил как раз на оснвоании формулы, но как по мне оно громоздкое и что-то подсказывает мне, можно сделать все деликатнее.
Хочу узнать возможны ли корректировки макроса для его более удобного и максимально понятного упрощения.

в столбце AN приведено решение формулой.
ну и сам макрос:
Код
Sub суммесли()
Application.ScreenUpdating = False ' Отключаем обновление экрана
i = 5 'начало данных
o = 3 'шапка
Do Until Sheets("Отчет").Cells(i, 2) = ""
If Sheets("Отчет").Cells(i, 2) <> "" Then
    'начало диапазон для условия
NomStolbca1 = Rows(2).Find("Реализация", , xlValues, xlWhole).Column + 2 ' определяем № столбца со словом реализация,и № столбца назания 1 месяца участвующего в расчете
BukvaStolbca1 = Split(Cells(1, NomStolbca1).Address, "$")(1) 'определяем буквенное название столбца где указан № 1 месяц участвующий в расчете
Mesyac1 = Range(BukvaStolbca1 & 1) 'определяем название 1го месяца участвующего в расчете
DiapazonNachalo1 = Rows(2).Find(Mesyac1, , xlValues, xlWhole).Address 'адресс столбца где находится начало диапазона с 1 месяцем участвующем в расчет
BukvaDiapazonNachalo1 = Split(DiapazonNachalo1, "$")(1) ' буква столбца 1 месяца участвующего в расчет
Diapazon1 = Range(BukvaDiapazonNachalo1 & o).Address ' начало диапозона т.е. 1 месяц участвующий в расчете
    'конец диапазона для условия
NomStolbca2 = NomStolbca1 - 2 ' определяем № столбца со словом реализация, и № столбца названия последнего месяца участвующего в расчете
BukvaStolbca2 = Split(Cells(1, NomStolbca2).Address, "$")(1) 'определяем буквенное название столбца где указан последний месяц участвующий в расчете
Mesyac1 = Range(BukvaStolbca2 & 1) 'определяем название ПОСЛЕДНЕГО месяца участвующего в расчете
Stolbec2 = Rows(2).Find(Mesyac1, , xlValues, xlWhole).Column '
Diapazon2 = Cells(o, Stolbec2).Address
     'весь диапазон 1 для условия
    DiapazonYsloviya1 = Range(Diapazon1 & ":" & Diapazon2).Address ' первый диапазон для условия суммесли
    'диапазон для суммирования
Diapazon3 = Range(BukvaDiapazonNachalo1 & i).Address
Diapazon4 = Cells(i, Stolbec2).Address
DiapazonSummirovamiya = Range(Diapazon3 & ":" & Diapazon4).Address

Range(BukvaStolbca2 & i).Value = WorksheetFunction.SumIf(Range(DiapazonYsloviya1), "Реализация", Range(DiapazonSummirovamiya))
End If
i = i + 1
Loop
Application.ScreenUpdating = 1 ' включаем обновление экрана
End Sub
Изменено: Mershik - 26.06.2019 12:51:05
Не бойтесь совершенства. Вам его не достичь.
 
Вот мой вариант.

Косячёк нашёл - после сообщений об ошибках забыл написать Exit Sub.
Код
Option Explicit

Sub myWay()
Dim rngDate As Range 'Диапазон дат
Dim rngHeader As Range 'Диапазон заголовков
Dim c As Range 'Временная переменная для циклов
Dim sFirstMonth As String 'Первый месяц
Dim sLastMonth As String  'Последний месяц
Dim sColName As String    'Какой столбец считаем
Dim iFirstRow As Long     'Первый ряд с данными
Dim iResColumn As Long    'Столбец с результатом
Dim iFirstCol As Long     'Первый столбец нужного диапазона (вычисляеется)
Dim iLastCol As Long      'Последний столбец нужного диапазона (вычисляеется)
Dim arrCol() As Long      'Перечень колонок, которые нужно суммировать
Dim i As Long
Dim j As Long
Dim iTmp As Double          'Для подсчёта результата

'Ввод первичных переменных
    Set rngDate = Range("O2:AL2")
    Set rngHeader = Range("O3:AL3")
    sFirstMonth = "Май 2017"
    sLastMonth = "Июнь 2017"
    sColName = "Реализация"
    iResColumn = Range("AO1").Column 'Лениво считать номер столбца АО
    iFirstRow = 5
    
'Сама программа
'Ищем диапазон дат
    For Each c In rngDate
        If iFirstCol = 0 Then
            If c.Value = sFirstMonth Then iFirstCol = c.Column
        Else
            If iLastCol = 0 Then
                If c.Value = sLastMonth Then iLastCol = c.Column
            Else
                If c.Value <> "" Then
                    iLastCol = c.Column - 1
                    Exit For
                End If
            End If
        End If
        If iLastCol <> 0 Then iLastCol = rngDate.Column + rngDate.Columns.Count - 1 'На случай, если у нас выбран последний  месяц из диапазона
    Next c
    
    If iFirstCol = 0 Or iLastCol = 0 Then
        MsgBox "Не найден первый или последний месяц"
    End If

'Ищем колонки, которые нужно суммировать
    ReDim arrCol(0 To 0)
    For Each c In Intersect(rngHeader, Range(Cells(rngHeader.Row, iFirstCol), Cells(rngHeader.Row, iLastCol)))
        If c.Value = sColName Then
            If arrCol(0) <> 0 Then i = 1 'чтобы первую строчку обработать правильно (массив не менять)
            ReDim Preserve arrCol(0 To UBound(arrCol) + i)
            arrCol(UBound(arrCol)) = c.Column
        End If
    Next c
    
    If arrCol(0) = 0 Then
        MsgBox "Не найдены столбцы для суммирования"
    End If
    
'Cкладываем, наконец
    i = iFirstRow
    Do While Cells(i, 1).Value <> ""
        iTmp = 0
        For j = 0 To UBound(arrCol)
            iTmp = iTmp + Cells(i, arrCol(j)).Value
        Next j
        Cells(i, iResColumn).Value = iTmp
        i = i + 1
    Loop
End Sub
Изменено: Wiss - 26.06.2019 15:30:07
Я не волшебник, я только учусь.
 
Wiss,прошу прощения я забыл уточнить что месяцы всегда добавляются и думаю использовать  название диапазона типа O2:AL2 и  т.д. не целесообразно так как они меняются ежемесячно как минимум и как максимум по запрос...т.е. скажут показать данные за квартал или за последние только 2 месяца...т.п. и постоянно добавляются по 6 столбцов справа перед AM(в данном примере). да и название месяцев так же меняется...

но с Вашим макросом обязательно ознакомлюсь..
Изменено: Mershik - 26.06.2019 15:33:39
Не бойтесь совершенства. Вам его не достичь.
 
Mershik, тоже извиняюсь, не уточнил сразу про структуру документа...

Я правильно понимаю что:
1. Столбцы AM и далее будут смещаться вправо по мере поступления данных за новые периоды.
2. В первой строке так и будут заданы условия, которые нужно использовать в программе.
3. В качестве "якоря" можно брать слово "Реализация" строке с датами,
   над ней - условие для последнего месяца,
   над ней + 2 столбца - условие для первого месяца.
3. То есть в программу я "зашиваю":
   номер строки с датами;
   номер строки с заголовком;
   номер первой строки с данными;
   название колонки для суммирования.

Получилось как-то так. Должно работать стабильно.
Я не волшебник, я только учусь.
 
Wiss, да все верно, спасибо  огромное, я в макросах не силен изучаю опытным путем и с помощью гугла и планеты эксель, так понимаю Ваш вариант более гибкий чем мой...?
Не бойтесь совершенства. Вам его не достичь.
 
Mershik, честно говоря, в Вашем коде я досконально на разбирался, поэтому комментировать его не берусь + сам только учусь.

Что у меня есть:
1. Отлов каких-никаких ошибок (когда криво введены месяцы/слово "Реализация").
2. Слово "Реализация" вводится только 1 раз и если что, его можно заменить (просто привычка, у меня коллеги любят заголовки переименовывать туда-сюда).
3. Столбец "Реализация" не обязательно должен быть первым (Меняю "Реализация" на  "Оплачено потребителем за расчетный период" в переменных и в ячейка AM2 и получаю сумму по "Оплачено потребителем за расчетный период").
4. Не используются формулы листа (Вашей формулой листа можно всё закончить ещё перед комментарием "'Ищем колонки, которые нужно суммировать" следующим кодом)
Код
    i = iFirstRow
    Do While Cells(i, 1).Value <> ""
        Cells(i, iResColumn).Value = WorksheetFunction.SumIf(Range(Cells(iRowHeader, iFirstCol), Cells(iRowHeader, iLastCol)), sColName, Range(Cells(i, iFirstCol), Cells(i, iLastCol)))
        i = i + 1
    Loop
А с другой стороны, если можно использовать формулы листа, то зачем вообще было огород городить, если можно было вместо всего кода сразу написать:
Код
    i = 5
    Do While Cells(i, 1).Value <> ""
        Cells(i, iResColumn).FormulaR1C1 = "=ROUND(SUMIF(INDEX(R3C15:R3C39,MATCH(R1C41,R2C15:R2C39,)):INDEX(R3C15:R3C39,MATCH(R1C39,R2C15:R2C39,)),""Реализация"",INDEX(RC[-24]:RC,MATCH(R1C41,R2C15:R2C39,)):INDEX(RC[-24]:RC,MATCH(R1C39,R2C15:R2C39,))),2)"
        Cells(i, iResColumn).Value = Cells(i, iResColumn).Value
        i = i + 1
    Loop

P.S. Не могу сказать, что мой макрос существенно более гибкий. Просто его писал я и лично мне его читать проще, хотя 18 переменных  для такой казалось бы простой задачи, это как-то дофига. Вижу, как безболезненно убрать sFirstMonth, sLastMonth (встречаются всего 1 раз) и rngDate, rngHeader (остались от первой версии, их можно заменить на With range....)
Изменено: Wiss - 26.06.2019 17:07:17
Я не волшебник, я только учусь.
 
Wiss, эх...спасибо...я тоже только учусь и понимаю что знаний не хватает..нужно теория (хоть немного)...СПАСИБО еще раз.
Не бойтесь совершенства. Вам его не достичь.
Страницы: 1
Наверх