Страницы: 1
RSS
Ошибка при использовании .SpecialCells(xlCellTypeBlanks)
 
Вот, нарвался на глюк. Может кому поможет. Беглый поиск по форуму явных аналогов не выявил, но если не так - не судите строго.

Ситуация достаточно типичная - после формирования силами VBA сводной таблицы и вставки на ее же место ее же значений возникает потребность заполнить пустоты полей области строк. Это делается заполнением всех пустот формулой, которую я называю "см.выше" (т.е. значение поля надо продублировать вниз на все строки этого поля - пока не встретится новое значение по данному полю). Это приводит наполнение сводной таблицы в нормализованный вид, пригодный для разного рода отчетов, ожидающих в каждой строке зоны данных увидеть заполненные данные в каждом столбце.

В общем вместо:

Код
Отдел А    Иванов
         Петров
         Сидоров
Отдел Б
          Власов
          Тарасов
          Кузьмин

Получаем:
Отдел А    Иванов
Отдел А    Петров
Отдел А    Сидоров
Отдел Б    Власов
Отдел Б    Тарасов
Отдел Б    Кузьмин


Код одолжен из книги, поэтому я был в нем уверен на все 100%:
Код
 
    FinalReportRow = WSD.Range("R1048576").End(xlUp).Row
    
    With Range("O2").Resize(FinalReportRow - 1, 1)
        With .SpecialCells(xlCellTypeBlanks)
            .FormulaR1C1 = "=R[-1]C"
        End With
        .Value = .Value
    End With




Всё бы хорошо, но при загрузке большого объема данных формула глючит и заполняет не только пустоты, но и все ячейки данного столбца. В итоге он полностью заполняется самым верхним значением (или даже пустотой).

Глюк имел плавающий характер - данных 400 тысяч строк, импортированных из текстового файла, что и усиливало желание найти "левый символ"  ;)  . В итоге методом половинного деления за 2,5 часа я его вычислил с точность до 10 строк. А потом - хвать, а демонов то и нет. Заполнение этих строк любыми другими (даже из уже прошедших проверку частей) также приводит к сбою. Потом я воспользовался гулглом, и нашел описание данного глюка.
http://support.microsoft.com/kb/832293/ru

Вывод - гугл круче клоуна с половинным делением  :)  , поэтому если ошибка не лечится за 20 минут - разумно потратить 2 минуты на погуглить. Ты понял, Леша? (это я себе  :)  )

ЗЫ. Больше всего понравился статус глюка в базе данных Microsoft - прям вспоминается программистская байка - "если в вашей программе обнаружили глюк, не спешите его исправлять - позиционируйте его как уникальную особенность вашего программного продукта"  :)
 
а так попробуйте
Код
 With Range("O2").Resize(FinalReportRow - 1, 1)
        .SpecialCells(xlCellTypeBlanks).FormulaR1C1 = "=R[-1]C"
        .Value = .Value
    End With
 
Володя,
Цитата
Excel поддерживает только до 8192 несмежных ячеек через макросы VBA.
На самом деле не ячеек, а областей. Этот факт неоднократно обсуждался на форуме. Лечится циклом, порциями. Или предварительной сортировкой, что для Alex-M не подходит.
Я сам - дурнее всякого примера! ...
 
Спасибо Сергей!
 
А вообще, проблему можно было решить гораздо проще

Достаточно считать Range("O2").Resize(FinalReportRow - 1, 1)  в массив,
пробежаться по нему в цикле, простейшей проверкой заполнив пустые позиции значениями «сверху»
И потом выгрузить массив на лист
Всего-то 5 строк кода...

А использовать SpecialCells при количестве строк более 20 тыс, - это уже рискованное занятие.
Цикл - почти всегда и быстрее, и проще.
 
EducatedFool, спасибо!

Я так и сделал - через цикл. Правда, сразу же после чтения и записывал "если ты пустая, получи значение из Темр, а если нет - пропишись в Темр". И так сверху вниз в 1 проход. В целом меня скорость устроила. Но, признаться,  не совсем догоняю в чем в данном случае выгода от массива - я ведь в нем ничего такого не обрабатываю и не вычисляю, а считать в каждую ячейку и записать в нее мне при любом раскладе придется.

Впрочем, я изучаю VBA только первые дни и откровенно говоря просто "не умею готовить" массивы - у меня счет идет на часы, поэтому я этот раздел в книге и на "практических тестах" особо детально не изучал, а тут, блин, еще эта сводная таблица чей-то на больших данных подвисает. Буду надеяться, что дело не в ней (вручную вроде делается), а в моем коде. Сейчас буду искать.

Еще раз спасибо за помощь!
 
Цитата
Но, признаться, не совсем догоняю в чем в данном случае выгода от массива
В любом случае будет быстрей, чем порциями SpecialCells.
У меня 418248 строк обработал за 1.6 сек. Это никак не часы. Код такой:
Код
Public Sub www()
    Dim a, i&, tmp, t!
    t = Timer
    a = ActiveSheet.UsedRange.Columns(1).Value
    For i = 1 To UBound(a)
        If a(i, 1) <> "" Then
            tmp = a(i, 1)
        Else
            a(i, 1) = tmp
        End If
    Next
    [a1].Resize(i - 1) = a
    MsgBox Timer - t
End Sub
Я сам - дурнее всякого примера! ...
 
Цитата
"не умею готовить" массивы
учитесь.
ничего сложного там нет.

операции с ячейками листа из VBA - очень долгая штука.
а у Вас их выполняется для каждой ячейки - по три.
сравните с кодом KuklP - всего два обращения (записать в массив значения и выгрузить значения на лист)
часы превратились в секунды.
фрилансер Excel, VBA - контакты в профиле
"Совершенствоваться не обязательно. Выживание — дело добровольное." Э.Деминг
 
ikki, я мельком их посмотрел, но эксперименты пока не делал. Просто действительно времени в обрез. Уже завтра, в край вторник, мне нужен работающий код. Поэтому дело не в сложно-просто, а в ресурсе времени.
Я не говорил и не говорю, что масивы это плохо. Видел, что они действительно в сотни раз ускоряют работу. Я лишь именно в моем случае так и не увидел выгоды, т.к. считал, что у меня тоже всего "2 касания". Если я чего-то не учитываю, подправьте пожалуйста. Вот мой код (заранее согласен что он не оптимален с каких-то других точек зрения, т.к. написал наспех).

Код
For i = 1 To finish - Range(start).Row
    If Range(start).Offset(i, 0).Value = nosing Then
            Range(start).Offset(i, 0).Value = temp
    Else
            temp = Range(start).Offset(i, 0).Value
    End If
Next i


В принципе, наверное,
temp = Range(start).Offset(i, 0).Value
можно было заменить на
temp = temp1, в которую изначально записывать Range(start).Offset(i, 0).Value.
Изменено: Alex-M - 08.09.2013 15:51:10
 
У Вас два "касания" к каждой ячейке (или может даже три - не знаю, для определения значения по сдвигу нужно ли "касаться" исходной ячейки...)
Умножьте количество строк на 2 - получите число "касаний".
Если использовать массив - будет всего 2 "касания", вся остальная работа в памяти с данными, не касаясь таблицы/ячеек.
Изменено: Hugo - 08.09.2013 15:52:12
 
Hugo, т.е. считывание всей колонки и считывание 1 ячейки  - одинаковы по времени?
Если так, то я понял, почему так долго упрямился - для меня время считывания диапазона всегда подразумевало сложение времени считывания каждой ячейки. И только сейчас я начинаю "подозревавать", что не учитываю некую инициализирующую составляющую для каждого обращения к листу.

ЗЫ. "Я все ловлю на лету, но теперь то мне ясно, что конкретно вы имели в виду" (с) из пародии на одну песню.
 
Ну не совсем одинаков - но можете замерить время считывания всего столбца - закомментируйте в коде KuklP цикл и выгрузку.

P.S. у меня на 100000 ячеек в среднем затрачивается 0,07 сек.
Изменено: Hugo - 08.09.2013 16:01:43
 
Цитата
If Range(start).Offset(i, 0).Value = nosing
Вот еще почему желательно использовать option explicit  :D  Еще дополню Игоря - запись в ячейку происходит гораздо медленнее, чем считывание из нее, поэтому в Вашем случае основное время(и весьма немалое) придется именно на запись в ячейки в цикле.
Изменено: KuklP - 08.09.2013 16:31:17
Я сам - дурнее всякого примера! ...
 
Цитата
option explicit
да, да, я что-то такое читал, и даже был душой "за", в смысле для себя решил "завтра обязательно поставлю".
И признаться, когда писал nosing, внутренний голос что-то такое пытался мне донести, но было полчетвертого утра и мы оба хотели спать, поэтому диалог не состоялся.

Вставлю прямо сейчас. Если из-за этого я убил пару часов, то я убьюсь об стенку.
Чувствую, на мне можно будет писать книжку "как я освоил VBA за Х дней и Y ляпов, про которые 120 раз написано на форуме" :)

Как однажды в сердцах воскликнул один "приверженец" тайм-менеджмента: "у меня нет времени, чтобы изучить как экономить время". Похоже это мой случай :).
 
option explicit заставит Вас объявлять все переменные - таких необъявленных скорее всего наберётся много, если код большой.
Но всё равно быстрее их все объявить, чем через неделю обнаружить, что что-то считается неправильно потому, что где-то переменная с, а где-то c  :(
Т.е. это ещё нужно суметь найти...
Изменено: Hugo - 08.09.2013 16:09:23
 
Сделал.

Hugo, а Вы не знаете обратного инструмента - т.е. нет ли каких-нибудь обработок, которые бы показывали в коде излишне объявленные переменные, рисовали бы графически структуру и т.п. "примочек" для удобства. Просто интересно - так как я подозреваю, что может быть я со злости за неделю я и рожу то, что наш отдел программирования не мог родить несколько лет, но как бывший программист я понимаю, какие у меня будут потом "ломки", когда я через 2 недели захочу разобраться в собственном коде. Поэтому хотелось бы все более-менее вылизать и откомментировать, чтобы потом легче жить.
 
От лишних объявленных переменных большого вреда не будет, а комментарии добавлять Вам никто не запретит. Еще - объявляйте переменные в столбик(а не в строку, как я) и тогда закомментировав по очереди, узнаете, какие лишние. Когда-то для ВБ существовала программа codecleaner. Аналогов для ВБА я не видел.
Я сам - дурнее всякого примера! ...
 
KuklP, спасибо!
Изменено: Alex-M - 08.09.2013 16:40:30
 
Цитата
Аналогов для ВБА я не видел
Сергей, а эта надстройка не подойдет
VBACodeCleaner.xla (205 312 байт), вроде скачивал ее вместе
с MZ Tools3 VBA
 
Володь, сейчас попробовал. Влепил в макрос из персонал пару левых переменных. Запустил кляйнер. Че-то он там чистил - не знаю. Отрапортовал, что все ОК. Переменные остались:( Мож я че не так делаю...
Я сам - дурнее всякого примера! ...
Страницы: 1
Читают тему
Наверх