Rambler's Top100
Форум: MS ACCESSVBVBA MS OfficeMS SQL server
Новые сообщения: 0000

Форум: MS ACCESS

Вопросы связанные с MS ACCESS

Обновить визитку
Участники «Online»
Все участники

 
 

Доброго времени суток, Посетитель!

вид форума:
Линейный форум Структурный форум

тема: множественная замена символов, оптимизация
 
 автор: osmor   (22.07.2009 в 09:04)   личное сообщение
 
 

условие (сильно упрощено, для понимания сути):
Есть 2 таблицы.
Таблица 1
Тип - Long
Алиас - String

Пример данных:

Код      | Алиас
1        | NF-kappaG
1        | ABC(45)
2        | NF-kappaG
2        | ABC(45)

Таблица 2
Тип (из таблицы 1) - Long
Старое значение
Новое значение

Пример данных:

Тип      |   Старое     |    Новое  
1        |  -           |     
1        |  (           |
1        |  (           |
1        |  G           |    @ 
2        |  -           |     +
2        |  kappa       |


Где в поле "Новое" ничего нет, это значит там пробел
Нужно заменить в исходном Алиасе старые символы на новые согласно типу. Т.е. после обработки в таблице 1 должно получиться

Код      | Алиас
1        | NF kappa@
1        | ABC 45 
2        | NF+G
2        | ABC(45)

Размер исходный таблицы разный от 2 тыс. до 30 тыс. записей
операция НЕ РАЗОВАЯ, т.е. время имеет значение
Вопрос:
Как бы все это "заоптимизировать"
У кого есть опыт?
Как лучше заменять каждый символ отдельно или написать функцию которая будет за один раз заменять все символы и только потом записывать и переходить к новой строке?
Что быстрее RegExp или Replace или MID?

В общем интересны любые предложения как, кто стал бы решать такую задачу.

Призовой вопрос.
Кроме всего прочего нужно разделить "группы типов" символов пробелом.
"группы типов" - это:
1 - все буквы
2 - все цифры
3 - все не буквенные символы

т.е. строка
привет123@123привет!?
должна превратиться в
привет 123 @ 123 привет !?

Тоже нужны идеи (пока есть только одна, проверять символ и следующий за ним на соответствие группе и вставлять пробел если группа изменилась, но думаю что на 30 тыс записей по 100 символов будет ОЧЕНЬ долго)

  Ответить  
 
 автор: ShadowOfSun   (22.07.2009 в 09:51)   личное сообщение
10 Кб.
 
 

Посмотри запрос 2 и будет тебе счастье

  Ответить  
 
 автор: osmor   (22.07.2009 в 11:19)   личное сообщение
 
 

спасибо.
Такое решение у меня уже есть :-)
На моем Intel Core2Duo 3Ghz + 4 гига памяти
Обработка таблицы с 8 тыс записями (по 4тыс в 2-х категориях) и 20 условиях замены (по 10 в каждой категории) заняла 2 минуты при локальном размещении таблиц.
и обработал 80 тыс записей
если условий в категории будет больше то каждая новая запись в условиях замены добавит в результирующий запрос столько записей сколько есть обрабатываемый строк в данной категории, т.е.
если я добавлю 1 условие для замены в категорию 1, то запрос будет обрабатывать на 4 тысячи строк больше.
т.е. при условии что категорий 5, записей в каждой категории 5 тыс (25 тыс записей) и условий для каждой категории 30, запрос будет обрабатывать 5000*30*5 = 750 тыс записей что 9 раз больше чем в тестовом примере, т.е. таблица в 15 тыс записей будет обрабатываться 2*9 = 18 минут, да и машинки у заказчика значительно слабее.

Возможно конечно это и есть самый быстрый способ.

  Ответить  
 
 автор: Анатолий (Киев)   (22.07.2009 в 13:06)   личное сообщение
 
 

Олег, я думаю, что это надо делать с помощью Recordset-ов. Типа так:

Do Until rsTabl1.EOF
    s = rsTabl1![Алиас]:  t = rsTabl1![Тип]
    rsTabl2.FindFirst "[Тип]=" & t   
 Do Until rsTabl2.NoMatch
    s = Replace(s, rsTabl2![Старое значение], NZ(rsTabl2![Новое значение], " "))
    rsTabl2.FindNext "[Тип]=" & t   
 Loop    
    s = InsertSpaceBetweenGroupSimbols(s)
 If rsTabl1![Алиас] <> s Then
    rsTabl1.Edit
    rsTabl1![Алиас] = s
    rsTabl1.Update
 End If
    rsTabl1.MoveNext
Loop

Каждая запись будет обрабатываться только 1 раз, причем обновление будет только там, где текст изменился.
Если применить ADODB.Recordset с UpdateBatch или транзакции, то процесс можно значительно ускорить.

Функцию InsertSpaceBetweenGroupSimbols я представляю так:
Проход по строке с предпоследнего по первый символ, анализ текущего и следующего символа с помощью Like и вставка пробела в строку при необходимости.

ДОБАВЛЕНО. А вот и функция:
Function InsertSpaceBetweenGroupSimbols(ByVal s$) As String
Dim i, s1 As String * 1, s2 As String * 1

 For i = Len(s) - 1 To 1 Step -1
    s1 = Mid$(s, i, 1): s2 = Mid$(s, i + 1, 1)
  If s1 = " " Or s2 = " " Then
  ElseIf s1 Like "[A-&#255;]" Then
   If s2 Like "[!A-&#255;]" Then s = Left$(s, i) & " " & Mid$(s, i + 1)
  ElseIf s1 Like "[0-9]" Then
   If s2 Like "[!0-9]" Then s = Left$(s, i) & " " & Mid$(s, i + 1)
  ElseIf s1 Like "[!A-&#255;0-9]" Then
   If s2 Like "[A-&#255;0-9]" Then s = Left$(s, i) & " " & Mid$(s, i + 1)
  End If
 Next
    InsertSpaceBetweenGroupSimbols = s
End Function

  Ответить  
 
 автор: Lukas   (22.07.2009 в 17:21)   личное сообщение
 
 


Function InsertSpaceBetweenGroupSimbols3(ByVal s$) As String
    Dim i As Long
    Dim grp As Long
    
    grp = 0
    For i = Len(s) To 1 Step -1
        Select Case Asc(Mid(s, i))
            Case 97 To 122, 65 To 90, 192 To 255 'буковки аглицкие, 192 To 255 'русские
                If grp = 2 Or grp = 3 Then s = Left$(s, i) & " " & Mid$(s, i + 1)
                grp = 1
            Case 48 To 57 'цифирки
                If grp = 1 Or grp = 3 Then s = Left$(s, i) & " " & Mid$(s, i + 1)
                grp = 2
            Case 32 'пробел
                grp = 0
            Case Else '33 To 47, 58 To 64, 91 To 96, 123 To 126, 127 To 191 ' все остальное
                If grp = 1 Or grp = 2 Then s = Left$(s, i) & " " & Mid$(s, i + 1)
                grp = 3
        End Select
    Next
    InsertSpaceBetweenGroupSimbols3 = s
End Function

Похоже в таком варианте будет работать быстрее, но думаю, и это не идеально.
Тест на моей машинке:

Public Function Test2()
    Dim i As Long
    Dim start As Long
    Dim finish As Long
    Dim str As String
    
    Const strText As String = "qwe 123ER FG$NNИТТИиреп.88*wert@^ 835rty FGHY^&*567@ апрГШвыЩО789=-=89 6578GHTY uiu561#@$  $4ft r678"
    Const lngCount As Long = 30000
    For i = 1 To lngCount
        'разгон процессора
        str = str
    Next i
    
    start = apiTimeGetTime
    For i = 1 To lngCount
        str = InsertSpaceBetweenGroupSimbols(strText)
    Next i
    finish = apiTimeGetTime - start
    Debug.Print "Вариант Анатолия"
    Debug.Print "Длина строки =", Len(strText)
    Debug.Print "Кол-во итераций =", lngCount
    Debug.Print "Время вычисления, ms=", finish
    Debug.Print "Result ="; str
    
    start = apiTimeGetTime
    For i = 1 To lngCount
        str = InsertSpaceBetweenGroupSimbols3(strText)
    Next i
    finish = apiTimeGetTime - start
    Debug.Print
    Debug.Print "Вариант Анатолия +Lukas"
    Debug.Print "Длина строки =", Len(strText)
    Debug.Print "Кол-во итераций =", lngCount
    Debug.Print "Время вычисления, ms=", finish
    Debug.Print "Result ="; str
End Function


Вариант Анатолия
Длина строки = 100
Кол-во итераций = 30000
Время вычисления, ms= 15422
Result =qwe 123 ER FG $ NNИТТИиреп . 88 * wert @^ 835 rty FGHY ^&* 567 @ апрГШвыЩО 789 =-= 89 6578 GHTY uiu 561 #@$ $ 4 ft r 678

Вариант Анатолия +Lukas
Длина строки = 100
Кол-во итераций = 30000
Время вычисления, ms= 2859
Result =qwe 123 ER FG $ NNИТТИиреп . 88 * wert @^ 835 rty FGHY ^&* 567 @ апрГШвыЩО 789 =-= 89 6578 GHTY uiu 561 #@$ $ 4 ft r 678

  Ответить  
 
 автор: Анатолий (Киев)   (22.07.2009 в 18:19)   личное сообщение
 
 

Lukas, ну тогда уж вместо:

   For i = Len(s) To 1 Step -1 
        Select Case Asc(Mid(s, i))
...

попробуйте:

Dim arr() As Byte
          arr = StrConv(s, vbFromUnicode)  'Получаем массив байтов.
   For i = Ubound(arr) To 1 Step -1 
        Select Case arr(i - 1)
...

  Ответить  
 
 автор: Lukas   (22.07.2009 в 18:33)   личное сообщение
 
 

Время вычисления, ms= 4046
Время вычисления, ms= 3531
Время вычисления, ms= 3094
Время вычисления, ms= 2375
Время вычисления, ms= 2907
Время вычисления, ms= 2313
Время вычисления, ms= 3234
Время вычисления, ms= 2906
Время вычисления, ms= 3515
Время вычисления, ms= 3000
Среднее: 3092

Хочу попробовать на рандомных строках.

  Ответить  
 
 автор: Lukas   (22.07.2009 в 18:49)   личное сообщение
 
 

На рандомных 30000 строках длиной 100 символов:
1. Asc : 3875-4110
2. arr : 3313-4688

У меня включена схема энергосбережения, потому частота процессора и напряжения на ядрах постоянно плавают.
Результаты только "на глаз".
Но похоже в одном диапазоне.

  Ответить  
 
 автор: ГлазастыйМышь   (22.07.2009 в 18:03)   личное сообщение
 
 

глянь в сторону регулярных выражений

http://www.sql.ru/forum/actualthread.aspx?bid=4&tid=534682&pg=2&hl=#5397127

в оракле щас с ними разбираюсь, а их оказывается и в Access можно притянуть

  Ответить  
 
 автор: Кабан   (22.07.2009 в 18:10)   личное сообщение
 
 

о, Мышь!
типа так? ;)

select 1 as res from dual where regexp_like(InputString,'^([-]?[]+(.[]+)?)$')

  Ответить  
 
 автор: ГлазастыйМышь   (23.07.2009 в 08:45)   личное сообщение
 
 

у меня от них уже мозги начинают пухнуть

select regexp_substr(lString,
'^((([]{3})?-([]{8})?([]{3})([]{3})/'||
         '[DMPST]([]{1,4})[KL]([.0123456789]{1,7})([]{2}[]{1,9})?([]{2}[]{1,2})?'||
         '([DMPST][]{1,4})?/([]{1,15})(/([]{3}){1,2})?)'||chr(10)||
         '(DIM/[]+'||chr(10)||')?'||
         '(OSI/[]{1,65}'||chr(10)||
            '(/[]{1,65}'||chr(10)||')?)?'||
         '(SCI/[]{0,15}(/[]{1,2})?'||chr(10)||')?'||
 ')((.+'||chr(10)||')+)'

from dual

проверка на соответствие определенной маске.

ЗЫ. Не пытайтесь себе представить N-мерный куб. Мало кому это удавалось, а народу с ума посходило немерянно.

  Ответить  
 
 автор: Lukas   (22.07.2009 в 21:13)   личное сообщение
 
 

Олег, а 8-9 секунд для родных таблиц на обе задачи это нормально?
Тест:
Записей: 30000
Длина Алиаса: 100 знаков
Групп замен: 2 (10+32 записей)

"На глаз" вроде работает как надо.
Проверить на 30000 рандомных строках глазом трудно.

Добавлено:
Тест2:
Время 13 сек.
Записей: 30000
Групп замен: 2 (52+32 записей)

В монопольном около 8 секунд.

  Ответить  
 
 автор: osmor   (23.07.2009 в 09:11)   личное сообщение
 
 


Олег, а 8-9 секунд для родных таблиц на обе задачи это нормально?



Нормально. вообще время не очень критично, но хотелось бы уложиться в 1-2 минуты, потому что иначе пользователь начинает нервничать :-)

ВСЕМ БОЛЬШОЕ СПАСИБО!!!


Все почитаю и попробую.
В регулярные выражения уже глянул, сейчас разбираюсь, но вот что быстрее это вопрос тестирования... а времени как всегда не хватает.

  Ответить  
 
 автор: Lukas   (23.07.2009 в 12:50)   личное сообщение
18 Кб.
 
 

Мои "изыскания" :

  Ответить  
 
 автор: osmor   (23.07.2009 в 13:11)   личное сообщение
 
 

Большое спасибо!

  Ответить  
 
 автор: kot_k_k   (28.08.2009 в 15:16)   личное сообщение
 
 

Про саму замену там был использован Recordset и FindFirst .
Не быстрее будет использовать SHAPE метод. т.к. он работает на порядок быстрее если не больше.

  Ответить  
HiProg.com - Технологии программирования
Rambler's Top100 TopList