|
|
|
| условие (сильно упрощено, для понимания сути):
Есть 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 символов будет ОЧЕНЬ долго) | |
|
| |
|
10 Кб. |
|
| Посмотри запрос 2 и будет тебе счастье | |
|
| |
|
|
|
| спасибо.
Такое решение у меня уже есть :-)
На моем 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 минут, да и машинки у заказчика значительно слабее.
Возможно конечно это и есть самый быстрый способ. | |
|
| |
|
|
|
| Олег, я думаю, что это надо делать с помощью 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-ÿ]" Then
If s2 Like "[!A-ÿ]" 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-ÿ0-9]" Then
If s2 Like "[A-ÿ0-9]" Then s = Left$(s, i) & " " & Mid$(s, i + 1)
End If
Next
InsertSpaceBetweenGroupSimbols = s
End Function
|
| |
|
| |
|
|
|
|
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 | |
|
| |
|
|
|
| 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)
...
|
| |
|
| |
|
|
|
| Время вычисления, ms= 4046
Время вычисления, ms= 3531
Время вычисления, ms= 3094
Время вычисления, ms= 2375
Время вычисления, ms= 2907
Время вычисления, ms= 2313
Время вычисления, ms= 3234
Время вычисления, ms= 2906
Время вычисления, ms= 3515
Время вычисления, ms= 3000
Среднее: 3092
Хочу попробовать на рандомных строках. | |
|
| |
|
|
|
| На рандомных 30000 строках длиной 100 символов:
1. Asc : 3875-4110
2. arr : 3313-4688
У меня включена схема энергосбережения, потому частота процессора и напряжения на ядрах постоянно плавают.
Результаты только "на глаз".
Но похоже в одном диапазоне. | |
|
| |
|
|
|
| глянь в сторону регулярных выражений
http://www.sql.ru/forum/actualthread.aspx?bid=4&tid=534682&pg=2&hl=#5397127
в оракле щас с ними разбираюсь, а их оказывается и в Access можно притянуть | |
|
| |
|
|
|
|
| у меня от них уже мозги начинают пухнуть
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-мерный куб. Мало кому это удавалось, а народу с ума посходило немерянно. | |
|
| |
|
|
|
| Олег, а 8-9 секунд для родных таблиц на обе задачи это нормально?
Тест:
Записей: 30000
Длина Алиаса: 100 знаков
Групп замен: 2 (10+32 записей)
"На глаз" вроде работает как надо.
Проверить на 30000 рандомных строках глазом трудно.
Добавлено:
Тест2:
Время 13 сек.
Записей: 30000
Групп замен: 2 (52+32 записей)
В монопольном около 8 секунд. | |
|
| |
|
|
|
|
Олег, а 8-9 секунд для родных таблиц на обе задачи это нормально?
|
Нормально. вообще время не очень критично, но хотелось бы уложиться в 1-2 минуты, потому что иначе пользователь начинает нервничать :-)
ВСЕМ БОЛЬШОЕ СПАСИБО!!!
Все почитаю и попробую.
В регулярные выражения уже глянул, сейчас разбираюсь, но вот что быстрее это вопрос тестирования... а времени как всегда не хватает. | |
|
| |
|
18 Кб. |
|
| Мои "изыскания" : | |
|
| |
|
|
|
|
| Про саму замену там был использован Recordset и FindFirst .
Не быстрее будет использовать SHAPE метод. т.к. он работает на порядок быстрее если не больше. | |
|
| |