Страницы: 1
RSS
Сложный поиск VBA
 
Есть данные (прикреплен файл). В файле перечисляются а/м (по гос номеру) в столбце А.  
Нужно найти а/м у которых в течение дня не было данных по открытию дверей, т.е. не было записей в строке с R по АА.  
Имеется макрос перебора файлов (данных по каждуму дню) с автофльтром. В данном случае автофильтр не помогает (в примере видно почему автофильтр не поможет).  
Уважаемые форумчане, помогите адаптировать существующий макрос для такого поиска.  
 
Option Explicit  
 
Sub Collect()  
 
Dim BazaWb As Workbook    'текущая книга (общий файл)  
   Dim BazaSht As Worksheet    'лист Price-group в общем файле  
   Dim iTempFileName As String    'имя поочерёдно открываемого файла  
   Dim iPath As String    'путь к папке, где лежат все файлы  
   Dim iLastRowBaza As Long    'последняя заполненная строка в общем файле в столбце C  
   Dim iLastRowTempWb As Long    'последняя заполненная строка в по-очерёдно открываемом файле в столбце C  
   Dim iNumFiles As Long    'количество открываемых файлов  
 
   Dim lr&, rr As Range  
 
   With Application  
       .ScreenUpdating = False  
       .DisplayAlerts = False  
       .Calculation = xlManual  
       Set BazaWb = ThisWorkbook  
       Set BazaSht = BazaWb.Sheets("ДанныеGPS")  
       iPath = BazaWb.Path & "\Данные_по_GPS\"  
       iTempFileName = Dir(iPath & "*.xls")  
         
       Sheets("ДанныеGPS").Select 'обнуление данных gps  
       Range("A3:BA10000").Select  
       Selection.ClearContents  
         
       Do While iTempFileName <> ""  
           If iTempFileName <> BazaWb.Name Then  
               With .Workbooks.Open _  
                    (Filename:=iPath & iTempFileName, UpdateLinks:=False, ReadOnly:=True)  
                   iNumFiles = iNumFiles + 1  
                     
                   UserForm1.Label4.Caption = "Собирается информация по неисправностям МТ из файла:   " & iTempFileName  
                   UserForm1.Repaint  
                                       
                           'Рабочая книга не должна быть защищена паролем  
                   With .Worksheets(1)  
                       iLastRowTempWb = .Cells(Rows.Count, 3).End(xlUp).Row  
                       iLastRowBaza = BazaSht.Cells(Rows.Count, 4).End(xlUp).Row + 1 'тут заменил 3 на 4!!! - не работало!  
                       '.Range(.Cells(3, 1), .Cells(iLastRowTempWb, "AA")).Copy Destination:=BazaSht.Cells(iLastRowBaza, 1)  
                         
                       '=======================  
                       lr = .Cells(1, 1).SpecialCells(xlLastCell).Row  
                                 
                               If IsDate(Range("i8").Value) And Application.WorksheetFunction.IsText(Range("i8").Value) Then  
                               Range(.Cells(8, 9), .Cells(lr, 9)).TextToColumns  
                               End If  
                                 
                       With .Rows(7)  
                           '.AutoFilter Field:=4, Criteria1:="=нет данных"  
                           .AutoFilter Field:=9, Criteria1:=">23:00:00"  
                       End With  
                         
                       Set rr = Intersect(.UsedRange.SpecialCells(xlCellTypeVisible), _  
                                          .Range(.Cells(8, 1), .Cells(lr, "AA")))  
                       If Not rr Is Nothing Then rr.Copy Destination:=BazaSht.Cells(iLastRowBaza, 1)  
                       .Rows(7).AutoFilter ' а это и не обязательно - всё равно закрываем без сохранения  
                       '=======================  
                         
                   End With  
                   .Close saveChanges:=False  
               End With  
           End If  
           iTempFileName = Dir  
             
       Loop  
       .Calculation = xlAutomatic  
       .DisplayAlerts = True  
       .ScreenUpdating = True  
         
   End With  
   UserForm1.Label4.Caption = ""  
   UserForm1.Repaint  
   'MsgBox "Информация собрана из " & iNumFiles & " файлов!", vbInformation, "Конец"  
End Sub
 
Поставьте формулу  
=ЕСЛИ(И(R8="";S8="";T8="";U8="";V8="";W8="";X8="";Y8="";Z8="";AA8="");A8;"")  
в любой столбец 8 строки после таблицы, например AI. Затем протяните формулу вниз.  
Подойдёт?  
А то макрос лень под это писать.  
Или м.б. я не понял задачи?
 
самостоятельный макрос:  
Sub www()  
Dim rrr, x  
Application.ScreenUpdating = False  
Set rrr = Range(Cells(8, 18), Cells(94, 27)) '94 - последняя строка  
With rrr  
 For Each x In .Rows  
 If x.SpecialCells(xlCellTypeBlanks).Count <> x.Cells.Count Then x.Rows.Hidden = True  
 Next  
End With  
Application.ScreenUpdating = True  
'Rows.Hidden = False 'показать все строки  
End Sub
 
{quote}{login=}{date=21.09.2012 12:41}{thema=}{post}Поставьте формулу  
=ЕСЛИ(И(R8="";S8="";T8="";U8="";V8="";W8="";X8="";Y8="";Z8="";AA8="");A8;""){/post}{/quote}  
1) В этом случае форумула будет выводить пустые значения(когда в столбце а будет пусто).  
2) Нужен именно макрос.
 
k61, макрос скрывает не пустые значения, это можно сделать автофильтром.  
Задача стоит в том, чтобы просматривать изменение в столбце а и одновременно смотреть в столбцы R-AA на наличие значений, если там были значения, то проверять следующюю а/м в столбце А.  
Со столбцом "А" - наверно нужно перебирать строки: если значение в столбце "А" равно предыдущему или "" то считать, что это тот же а/м и проверять наличие значений в столбцах R-AA.  
Когда значение в столбце А изменилось и не равно "" и предыдущему, то считать что это уже другой а/м и смотреть на значения R-AA.
 
По идее нужен цикл, который перебирает названия а\м в столбце А (при этом, если встречается "", то это тот же а\м) и смотрит в столбцы R-AA на наличие значений, если там не пусто, то переход к следующей а\м. Если к примеру было перебрано 15 строк и не встретилось значений в R-AA, то взять последнюю строку по этой а\м и перейти к следующей.    
К сожалению в программировании пока не силен, такое пока не осилить своими руками.
 
Без посторонней помощи так ничего и не получилось.  
Нужна ваша помощь, уважаемые форумчане.
 
Подумал еще раз над алгоритмом. Можно упростить.  
1. Убрать автофильтром строки где в столбце А ячейки пустые.  
Остается    
2. Додумался вот до чего:  
  2.1) 1 скопировать из файла все строки во временный лист 1  
       2 скопировать из файла все строки во временный лист 2  
       3 отфильтровать временный лист 1 оставив строки где в ячейках R-АА есть значения  
       4 отфильтровать временный лист 1 оставив строки где в ячейках R-АА пусто  
       5 удалить дубли по ячейкам в столбце А во временном листе 1  
       6 удалить дубли по ячейкам в столбце А во временном листе 2  
       7 сравнить временный лист 1 с временным листом 2 на наличие совпадений, удалить совпадения  
при этом останутся те а/м у которых за день датчики не срабатывали  
Способ наипростейший, вот только сколько он будет переваривать файлы объемом в 6000 строк, а таких файлов 30???  
  2.2) Перебирать строки и искать значения в R-AA, при этом смотреть в ячейку в столбце А и проверять не та же самая это а/м. Если значения в R-AA были, то переходить в след строку.
 
У меня такое предложение:  
1. Открыть файл только для чтения  
2. Заполнить пустые ячейки в первом ст. формулой как описано тут: http://www.planetaexcel.ru/tip.php?aid=86  
3. Применить расширенный фильтр (см. файл) и получить уникальные номера, у которых ячейки R-AA пустые  
4. Забрать эти номера в сводную таблицу  
5. Закрыть файл  
 
Примечание: мне не удалось заставить работать формулу массива в условии автофильтра  
=И(R8:AA8="")  
 
Пришлось писать обычную формулу:  
=R8&S8&T8&U8&V8&W8&X8&Y8&Z8&AA8=""  
 
Кто-нибудь объяснит, почему?
 
Хотя в данном случае значения в ст. R-AA числовые и положительные, поэтому их можно просто суммировать для условия расширенного фильтра:  
 
=СУММ(R8:AA8)
 
Вернее так ;)  
 
=СУММ(R8:AA8)=0  
 
Почистите, пож.
 
Предложенный вариант интересный, но два значения не должны были высветиться С256КЕ, С278КЕ, исходя из условия. (если в течение дня у а/м датчик сработал-были значения в ячейках R-AA, то эту а/м не считать/пропустить)
 
ОК, тогда можно таким же макаром, но с противоположным условием, выделить номера, у которых БЫЛИ срабатывания, и удалить вторые значения из списка первых. Это легко делается на коллекции или словаре.  
Т.е. первый список отбирается по формуле  
=СУММ(R8:AA8)=0  
 
Второй список по формуле  
=СУММ(R8:AA8)  
 
В файле они в разных столбцах, но фактически можно в одном.  
Если в файле могут быть разные даты, то следует делать выборку номеров вместе с датами, и исключать уникальные по ключу Номер&Дата.
 
Вариант под один лист  
 
Private Function hasEmpty(ByVal inSigns As Variant, inRow As Long) As Boolean  
   Dim iCol As Long  
   hasEmpty = True  
   For iCol = 1 To UBound(inSigns, 2)  
       If Not IsEmpty(inSigns(inRow, iCol)) Then hasEmpty = False: Exit For  
   Next iCol  
End Function  
 
Public Sub HideRows(ByVal forSheet As Worksheet)  
   Dim LRow As Long, iRow As Long  
   Dim vNums As Variant, vSigns As Variant  
   Dim isFirst As Boolean, onlyEmpty As Boolean  
   Dim sLastKey As String, sCurKey As String  
   Dim startCol As New Collection, endCol As New Collection  
   Dim vStart As Long, vEnd As Long  
   LRow = forSheet.UsedRange.Rows.Count  
   vNums = forSheet.Range("A8:A" & CStr(LRow)).Value  
   vSigns = forSheet.Range("R8:AA" & CStr(LRow)).Value  
   isFirst = True  
   For iRow = 1 To UBound(vNums)  
       sCurKey = UCase$(Trim$(CStr(vNums(iRow, 1))))  
       If sCurKey <> "" Then  
           If sCurKey <> sLastKey Then  
               If isFirst Then  
                   vStart = iRow: isFirst = False: onlyEmpty = hasEmpty(vSigns, iRow): sLastKey = sCurKey  
               Else  
                   If Not onlyEmpty Then startCol.Add vStart + 7: endCol.Add iRow + 6  
                   vStart = iRow: onlyEmpty = hasEmpty(vSigns, iRow): sLastKey = sCurKey  
               End If  
           Else  
               onlyEmpty = onlyEmpty And hasEmpty(vSigns, iRow)  
           End If  
       Else  
           onlyEmpty = onlyEmpty And hasEmpty(vSigns, iRow)  
       End If  
   Next iRow  
   If Not onlyEmpty Then  
       startCol.Add vStart + 7: endCol.Add forSheet.UsedRange.Rows.Count  
   End If  
   If startCol.Count > 0 Then  
       For iRow = 1 To startCol.Count  
           forSheet.Rows(CStr(startCol(iRow)) & ":" & CStr(endCol(iRow))).Hidden = True  
       Next iRow  
   End If  
End Sub  
 
Public Sub TestFilter()  
   HideRows ActiveSheet  
End Sub
 
{quote}{login=Казанский}{date=24.09.2012 04:16}{thema=}{post}ОК, тогда можно таким же макаром, но с противоположным условием, выделить номера, у которых БЫЛИ срабатывания, и удалить вторые значения из списка первых. Это легко делается на коллекции или словаре.  
Т.е. первый список отбирается по формуле  
=СУММ(R8:AA8)=0  
 
Второй список по формуле  
=СУММ(R8:AA8)  
 
В файле они в разных столбцах, но фактически можно в одном.  
Если в файле могут быть разные даты, то следует делать выборку номеров вместе с датами, и исключать уникальные по ключу Номер&Дата.{/post}{/quote}  
 
Очень интересное решение, попробую внедрить в макрос. Спасибо большое за участие.
 
anvg, код замечательный, почистить дубли в конце и все. Только на 6000 строк 28 секунд многовато. Издержки перебора.  
Еще раз повторюсь, код замечательный. Спасибо большое, думаю что и остальным пользователям он будет полезный.
Страницы: 1
Наверх