Страницы: 1 2 След.
RSS
// Как организовать наиболее быстрое нахождение уникальных значений при сравнении 2-х столбцов
 
Всем Доброго времени суток!  
Ситуация следущая:  
В Книгу 1 поступают данные из 2 других книг ("Pecom" и "База"). Соответственно, столбец А- данные из Pecom, столбец B - данные из имеющейся Базы (для примера сделаны листы Книга2 и Книга3).  
Далее в столбце А ищутся те "уникальные" значения (одно и то же значение/текст в столбце А может повторяться), которых нет в столбце B с назначением соответсвующего порядкового номера (столбцы C и D).  
В конце концов найденные новые значения приводятся в "человеческий" табличный вид (см. столбцы E и F).  
 
Вопрос - Как сделать выполнение данной процедуры наибыстрейшим?  
(поскольку комп конкретно подвисает с заполненными строками вплоть до максимальной для 2003 версии. Молчу уже про 2007 и выше..)
 
{quote}{login=АнЯ-2}{date=29.01.2012 08:05}{thema=// Как организовать наиболее быстрое нахождение уникальных значений при сравнении 2-х столбцов}{post}... комп конкретно подвисает с заполненными строками вплоть до максимальной для 2003 версии. Молчу уже про 2007 и выше..){/post}{/quote}  
Не отвергаю привязанности к XL, но почему бы, имхо, не воспользоваться Access?.. Тем более при больших объёмах данных и постоянной/регулярной однотипной обработке?.. Как вариант, разумеется... ;)  
-76503-
 
К сожалению, ни я, ни руководство - не умеем.. поэтому рассматриваются все варианты исключительно в старом добром Excel.. можно всякие dll и sql-запросы прикрутить.. но опять же - не умею..
 
Уж сколько было таких кодов...  
Буквально за секунду - B в массив, его в словарь, A в массив, создаём пустой массив под его размер, массив из A проверяем по словарю - отсутствующие складываем в созданный пустой массив.  
Заполненную верхушку выгружаем. Порядковые номера можно или сразу в массив заносить, или потом протянуть автозаполнением.
 
Вот аналогичное делал:  
 
 
Option Explicit  
 
Sub compareFull()  
   Dim a(), b(), c(), iLastrow As Long, ii As Long  
 
   '1.  
   With Sheets("сток")  
       iLastrow = .Cells(Rows.Count, 1).End(xlUp).Row  
       a = Range(.[e2], .Range("A" & iLastrow)).Value
   End With  
 
   With Sheets("инв")  
       iLastrow = .Cells(Rows.Count, 1).End(xlUp).Row  
       b = Range(.[e2], .Range("A" & iLastrow)).Value
   End With  
 
   '2.  
   ReDim c(1 To UBound(a) + UBound(b), 1 To 5)  
 
   'сверка по словарю, копирование данных в итоговый массив  
   dicdic a, b, c, ii  
   dicdic b, a, c, ii  
 
   '5.  
   With Sheets("результат")  
       .Columns(5).NumberFormat = "@"  
       .[A1].Resize(ii, 5) = c
       .Activate  
   End With  
 
End Sub  
 
Sub dicdic(a, b, c, ii)  
   Dim i&, temp$  
 
   With CreateObject("Scripting.Dictionary")  
 
       '3.  
       For i = 1 To UBound(a)  
           temp = a(i, 1) & "|" & a(i, 3) & "|" & a(i, 4)  
           .Item(temp) = i  
       Next  
 
       '4.  
       For i = 1 To UBound(b)  
           temp = b(i, 1) & "|" & b(i, 3) & "|" & b(i, 4)  
           If Not .exists(temp) Then  
               ii = ii + 1  
               c(ii, 1) = b(i, 1)  
               c(ii, 2) = b(i, 2)  
               c(ii, 3) = b(i, 3)  
               c(ii, 4) = b(i, 4)  
               c(ii, 5) = b(i, 5)  
           End If  
       Next  
   End With  
 
End Sub
 
Переделал. Выгрузка в [H2] для того, чтоб сравнить с Вашими данными.
 
 
Option Explicit  
 
Sub compareFull()  
   Dim a(), b(), c(), iLastrow As Long, ii As Long  
 
   '1.  
   With Sheets("Книга3-Лист1") 'baza  
       iLastrow = .Cells(Rows.Count, 7).End(xlUp).Row  
       a = Range(.[G5], .Range("G" & iLastrow)).Value
   End With  
 
   With Sheets("Книга2-Лист1") 'pecom  
       iLastrow = .Cells(Rows.Count, 2).End(xlUp).Row  
       b = Range(.[B3], .Range("B" & iLastrow)).Value
   End With  
 
   '2.  
   ReDim c(1 To UBound(b), 1 To 2)  
 
   'сверка по словарю, копирование данных в итоговый массив  
   dicdic a, b, c, ii  
 
   '5.  
   With Sheets("Книга1-Лист1")  
       .[H2].Resize(ii, 2) = c
   End With  
 
End Sub  
 
Sub dicdic(a, b, c, ii)  
   Dim i&, temp$  
 
   With CreateObject("Scripting.Dictionary")  
 
       '3.  
       For i = 1 To UBound(a)  
           .Item(a(i, 1)) = vbNullString  
       Next  
 
       '4.  
       For i = 1 To UBound(b)  
           If Not .exists(b(i, 1)) Then  
               ii = ii + 1  
               c(ii, 1) = ii  
               c(ii, 2) = b(i, 1)  
           End If  
       Next  
   End With  
 
End Sub
 
Забыл убрать уже ненужное объявление temp$...
 
С макросами дружите? Вариант.
Я сам - дурнее всякого примера! ...
 
Сергей, это скорее вариация :)  
Аня, все 3 кода - по сути одно и тоже. Только первый рассчитан на другие данные и для чуть другой работы...
 
Я пока елку от треноги отдирал, ты уже ответил. Ну не пропадать же:-)
Я сам - дурнее всякого примера! ...
 
Серёга, так 8-е марта ещё не скоро :)
 
К шкафу не подойти. Так бы до следующего года оставил.
Я сам - дурнее всякого примера! ...
 
:)  
У соседей на улице тоже ещё вся в игрушках стоит. Живая.  
А у нас за забором 5 лет в игрушках стояла. Пришлось с лестницей игрушки снимать - уже было не достать :)
 
Hugo, KukLP - большое спасибо за оказанную помощь.  
Только вариант Hugo что с удалением "temp", что без - не проходит.. выдаёт ошибку № "400".  
Потом в силу меньшего кол-ва букв вариант KukLP мне кажется наиболее симпатичным (опять же, если бы вариант Hugo не выдавал бы 400-ую ошибку, то можно было бы протестировать оба и впоследствии равновероятно получить, что по скорости он ничуть не уступает макросу KukLP).  
 
Тем не менее, ещё раз благодарю за оказанную помощь, и добавлю немножко off-топа, плавно вытекающего из вышеописанного:  
Уважаемые форумчане, как вы (=во мн.числе) смотрите, чтобы на сайте добавили кнопку "Спасибо!" или "+1", кол-во которого бы накапливалось помимо/вместо числа написанных сообщений?  
Ведь, согласитесь, эта инфа куда более содержательна вместо общей суммы написанных пользователем сообщений и соответствующих "простых человеческих спасиб" в каждой теме. И вам не сложно - и человеку приятно. А ещё хорошо бы добавить градацию оценок.  
Так, например, в моём случае:  
Пользователю KukLP - Спасибо на 10 баллов;  
Hugo - на 6 баллов;  
Z - 1-2 балла (за описание возможностей применительно к моей ситуации которые, несмотря на то, что я и знаю - могут быть полезны другим пользователям).  
 
Вобщем, как вам такая тема?  
 
С уважением,
 
{quote}{login=АнЯ-2}{date=30.01.2012 09:45}{thema=}{post}...и добавлю немножко off-топа... Вобщем, как вам такая тема?..{/post}{/quote} Off Нормальная... относительно. Почему? - Обжиться на Planet'e надоть-с, приглядеться... И тады предложения и дискуссию (если вообще они возникли бы) перенести сюды - http://www.planetaexcel.ru/forum.php?thread_id=11785... :) Но радует главное - нашему полку прибыло - еще на одного неравнодушного человека планетян стало больше... А далее - увидим! -33451-
 
Да, проверил код в файле Сергея - если код поместить в стандартный модуль, то всё работает.  
А вот если в модуль листа ниже кода Сергея - то не хватает двух точек (у меня ошибка 1004) :(  
Этой  
a = .R  
и этой  
b = .R  
 
У Сергея букв меньше потому, что он неленивый и код для задачи писал, а я ленивый и часто первый попавшийся переделываю :)  
А так они оба работают одинаково, и если и будет разница - то на доли секунды.
 
Мечта лентяя:  
Доживём ли мы до того времени, когда ёлки будут сами стряхивать игрушки и уходить из наших домов....
 
Неленивых программистов не бывает. В принципе. Неленивый вводил бы все руками и на калькуляторе считал. Я тоже свой с программерс форума переделал. Там где мы с тобой совпадения и разницу выводили:-)  
П.С. пока писал, еще один программист ответил:-)
Я сам - дурнее всякого примера! ...
 
я б так сделал(тоже лениво - поэтому переделываю код сергея)  
 
Public Sub www_slan()  
   Dim a, i&, lr&  
   a = Sheets("Книга3-Лист1").Range("g5").CurrentRegion  
   With CreateObject("scripting.dictionary")  
   On Error Resume Next  
       For i = 1 To UBound(a)  
           .Item(a(i, 1)) = 0  
       Next  
       a = Sheets("Книга2-Лист1").Range("b4").CurrentRegion  
       ReDim Preserve a(1 To UBound(a), 1 To 2)  
       For i = 2 To UBound(a)  
           If Not .exists(a(i, 1)) Then _  
               lr = lr + 1: a(lr, 2) = a(i, 1): a(lr, 1) = lr  
       Next  
   End With  
   Me.[a1].CurrentRegion.Offset(1).ClearContents: Me.[a2].Resize(lr, 2) = a
End Sub  
 
 
да, доли секунды.. но с другой стороны, в относительных мерах - около 20%
Живи и дай жить..
 
{quote}{login=k61}{date=30.01.2012 10:26}{thema=re: о ёлках.}{post}Мечта лентяя:  
Доживём ли мы до того времени, когда ёлки будут сами стряхивать игрушки и уходить из наших домов....{/post}{/quote}  
 
 
насчет стряхивать - несложно. вешаем на кончики веток - как только иголки осыпятся, упадут. сам пока жду
 
На моих ёлках иголки не осыпаются - одна искусственная, вторая живая :(  
А 20% - в чью пользу?  
Хотя вот, считайте (3/3, оба диапазона ~10k строк):  
 
0.171875    
0.171875    
0.1875    
0.171875    
0.1875    
0.1875    
 
Код Сергея чуть быстрее в общем.
 
у меня быстрее мой :)    
 
65Kстрок*9раз
Живи и дай жить..
 
теоретически:  
при записи значения item(..)=0  
 
точно так же работает код exists - оно же не знает, что вы в предыдущей строке уже его вызывали..  
 
во втором цикле только экономия памяти - пишем в тот же массив
Живи и дай жить..
 
Да не может быть! :)  
Тогда уже интересно - почему так?  
Вообще-то, мой должен был быть быстрее - за счёт того, что a() и .value :)  
Но вероятно из-за 2-х субов преимущество пропало...
 
Понял - iLastrow меня подвёл... Больше некому :)  
Уже и всё в один суб собрал - изменений 0...
 
Ниччего не понимаю - убрал () и .Value (iLastrow уже нет) - скорость выровнялась...  
А вроде наоборот должно было быть :(  
Потестил ещё - скорость воруют скобки - .Value не влияет.
 
Всем привет.  
В 1-ый раз задача была озвучена не полностью, дико изиняюсь, но в оправдание скажу, что второй день бьюсь пытаясь решить самостоятельно следующую задачу:  
 
Цель: Сопоставить источники по 2-м столбцам и на выходе получить "уникальные" значения Источника 1 с остальными показателями(=3 и более показателей.. целая таблица).  
 
Поиск номера строки (Cells(lr+2;5)=i) с "уникальным" значением явно не для большого диапазона данных..  
 
P.S. Это не помогло:  
http://www.planetaexcel.ru/forum.php?thread_id=28741  
http://www.planetaexcel.ru/forum.php/forum.php?thread_id=30666  
http://www.planetaexcel.ru/forum.php?thread_id=22229 - точно не для меня..  
 
Вобщем, всем, кто может.. Help me!.. (мне явно не хватает смайлов..)
 
От этого алгоритм не меняется.  
Мне тоже лениво писаать - проще переделать. Но и раз слено-kukipский вариант оказался быстрее и привлекательнее... :)  
 
Option Explicit  
 
Public Sub www_slanH()  
   Dim a, i&, lr&  
   a = Sheets("Книга3").Range("g5").CurrentRegion  
   With CreateObject("scripting.dictionary")  
       On Error Resume Next  
       For i = 1 To UBound(a)  
           .Item(a(i, 1) & "|" & a(i, 2)) = 0  
       Next  
       a = Sheets("Книга2").Range("b3").CurrentRegion  
       ReDim Preserve a(1 To UBound(a), 1 To 4)  
       For i = 2 To UBound(a)  
           If Not .exists(a(i, 1) & "|" & a(i, 2)) Then  
               lr = lr + 1  
               a(lr, 1) = lr  
               a(lr, 2) = a(i, 1)  
               a(lr, 3) = a(i, 2)  
               a(lr, 4) = a(i, 3)  
           End If  
       Next  
   End With  
   Me.[a1].CurrentRegion.Offset(1).ClearContents: Me.[a2].Resize(lr, 4) = a
End Sub  
 
 
Код поместить в модуль листа, куда планируете вытягивать данные.
 
Сергей, извини, промахнулся... :(
 
Тебе можно:-)  
-=37137=-
Я сам - дурнее всякого примера! ...
Страницы: 1 2 След.
Читают тему
Наверх