Форум: "Базы";
Текущий архив: 2004.06.06;
Скачать: [xml.tar.bz2];
ВнизОчень быстрое добавление изменение записи... Найти похожие ветки
← →
Rouse_ © (2004-05-16 11:08) [0]Имеется база (Access) более 150 тысяч записей...
Необходимо в таблице создать еще одну колонку и заполнить ее неким числом...
Колонку создаю вот так:SourceQuery.SQL.Text := "Alter Table " + sTitTable + " Add " + FIELD_REINDEX + " Char(50)";// CONSTRAINT " + S.Strings[0] + " PRIMARY KEY";
SourceQuery.ExecSQL;
где sTitTable - имя таблицы
FIELD_REINDEX - имя колонки
и начинаю модифицировать это поле в каждой записи:SourceDS.DataSet.First;
while not SourceDS.DataSet.Eof do
begin
SourceDS.DataSet.Edit;
try
SourceDS.DataSet.FieldValues[FIELD_REINDEX] := GenerateGUID;
finally
SourceDS.DataSet.Post;
end;
SourceDS.DataSet.Next;
end;
Проблема заключается в чем:
на довольно мощной мацине (Xeon 3.2 1Gb Dual Memory) этод код модифицирует около 4 записей в секунду, т.е. всю базу он пробежит за примерно 10 часов...
Вопрос: Как можно ускорить модификацию записей в базе?
← →
Nikolay M. © (2004-05-16 11:52) [1]Попробуй запросом
UPDATE mytab
SET my_field = :my_field_value
WHERE ID = :ID
Не знаю механизмов апдейта через датасет, возможно, что в твоем случае уходит запрос
UPDATE mytab
SET
field1 = value1,
field2 = value2,
...
fieldN = valueN
WHERE
ID = id_value
(т.е. апдейтятся ВСЕ поля вместо одного) что, возможно, приводит к перестройке всех существующих в таблице индексов.
Все сказанное - только предположения. Как оно происходит в аксессе на самом деле, утверждать не берусь.
← →
Rouse_ © (2004-05-16 16:12) [2]> WHERE ID = :ID
Ключевых полей в базе нет индексированных тоже (вот такая вот беда)...
Коль, а можно ли каким нибудь макаром сгенерировать все необходимые данные через запятую - а потом одним запросом перекинуть в базу?
К примеру:SourceQuery.SQL.Text := "UPDATE " + sTitTable + " SET "+ FIELD_REINDEX + " = "+QuotedStr(GenerateGUID);
SourceQuery.ExecSQL;
Обрабатывается очень быстро - но значение во все поля заносится одинаковое, а вот как сделать чтобы в каждое поле заносилось разное?
← →
Polevi © (2004-05-16 16:47) [3]ф-ию напиши на Access
в запросе полю присваивай значение ф-ии
← →
Rouse_ © (2004-05-16 17:09) [4]> [3] Polevi © (16.05.04 16:47)
В смысле? Т.е. необходимо в базу добавить некий макрос и вызывать уже его?
← →
Polevi © (2004-05-16 17:15) [5]не макрос
в Access можно писать ф-ии на васике, если ты не в курсе
затем эти ф-ии можно использовать в запросах
UPDATE ... SET Field1=MyGlobalFunctionFromModule()
там чтото вроде модудля данных надо создать а в нем public function
Точно не могу сказать давно не работал с Access и сейчас не установлен у меня
← →
Rouse_ © (2004-05-16 17:18) [6]Угу - попробую, если что получится - сообщу :)
Спасибо :)
← →
Rouse_ © (2004-05-16 17:55) [7]Такс...
Функцию написал, вот теперь вопрос, как ее правильно вызвать?
Вот так:SourceQuery.SQL.Text := "UPDATE " + sTitTable + " SET "+ FIELD_REINDEX + " = Module1.CoGenerateGUID()";
Не проходит, пишет неопределенная функция Module1.CoGenerateGUID()
Где про это почитать можно?
если интересно то вот как выглядит сама функция...Option Compare Database
Public Function CoGenerateGUID() As String
Dim Counter, Dat As Integer
Dim Result As String
Dim HexCode
HexCode = Array("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", F)
Result = ""
Counter = 0
Randomize
Do While Counter < 36
Dat = (15 * Rnd) + 1
If Dat > 15 Then Dat = 15
Result = Result + HexCode(Dat)
Counter = Counter + 1
Loop
CoGenerateGUID = Result
End Function
это так - набросок, главное чтоб возвращала какое нибудь уникальное значение...
← →
Polevi © (2004-05-16 18:03) [8]из Access вызывал ?
← →
Mike Kouzmine © (2004-05-16 18:11) [9]Построй запрос в аксессе, там используй эту функцию, а из делфи работай с запросом, как с хранимой процедурой.
← →
Rouse_ © (2004-05-16 18:15) [10]> [8] Polevi © (16.05.04 18:03)
В Access проверял ее, а вызывал уже из программы, сейчас попробую совет Mike Kouzmine © :)
← →
Polevi © (2004-05-16 18:17) [11]что значит проверял
запрос отработал ?
← →
Rouse_ © (2004-05-16 18:25) [12]Хех, вот запрос: UPDATE REG21 SET ID = CoGenerateGUID();
Результат, как и в предыдущем варианте - у всех записей одинаковый GUID
5546D6C3124561B347965CA919372AD1842
Это уже в самом Access - е проверил, без Дельфи...
Беда :(
← →
Polevi © (2004-05-16 18:27) [13]попробуй
UPDATE REG21 SET ID = CoGenerateGUID(SomeFieldValue);
← →
Polevi © (2004-05-16 18:27) [14]и у ф-ии параметр, соотв :)
← →
Rouse_ © (2004-05-16 18:31) [15]> [13] Polevi © (16.05.04 18:27)
Урррра, зарработала!!! :))) © Кот Матроскин
Всем кто принял участие большое спасибо :))
Сергею в часности :)
← →
Polevi © (2004-05-16 18:36) [16]будь здоров :)
← →
sniknik © (2004-05-16 21:09) [17]> Очень быстрое добавление изменение записи...
> Ключевых полей в базе нет индексированных тоже (вот такая вот беда)...
самый быстрый способ, в такой ситуации (имхо).
добавляеш таблице поле типа "Счетчик" (делать надо в конструкторе самого аксеса), ставиш ему размерность (в свойствах) "Код репликации" и назначакш его ключем. все. выходиш с подтверждением сохранения макета таблицы. гуиды проставятся автоматом. очень быстро.
то же самое можно через ADOX реализовать (в SQL почемуто не нашол установку типов полей, возможно нет этого), только неохота искать в хелпе, если нужно найдеш (???).
← →
Rouse_ © (2004-05-17 10:29) [18]> добавляеш таблице поле типа "Счетчик"
Ругается зараза на MaxLockPerFile - мол превышено кол-во блокировок файла...
← →
sniknik © (2004-05-17 10:36) [19]> Ругается зараза на MaxLockPerFile - мол превышено кол-во блокировок файла...
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Jet\4.0\Engines\Jet 4.0\MaxLockPerFile
увеличь, по умолчанию там значение 9500, на одиночной таблице полное обновление я на 170 000 записей ставил 19500 (приблизительно также поставь, после взад верни :).
← →
Rouse_ © (2004-05-17 10:41) [20]> [19] sniknik © (17.05.04 10:36)
Это сделал сразуже - всеравно ругается...
Ладно попробуем другим методом:
Вопрос: Существует ли возможность добавить из программы в физическую базу сам код генерации идентификатора, потом также новый запрос?
А потом я уже из программы буду запрос вызывать...
Просто баз много, около 80 и ежемесячно будет еще столько же приходить, хочется автоматизировать этот процесс...
ЗЫ: Nikolay M. посоветовал непосредственно к тебе обратиться, мол ты решал уже такую задачу :)
← →
Vlad © (2004-05-17 11:11) [21]
> Rouse_ © (17.05.04 10:41) [20]
Очень странно что код, приведенный тобой в начальном посте, работает так медленно. Должен на несколько порядков быстрее.
Для ускорения могу посоветовать:
1) Отвязать все DataSource, связанные с этим DataSet
2) Не делать Post каждый раз в цикле, сделать его один раз после цикла
← →
sniknik © (2004-05-17 11:37) [22]> Это сделал сразуже - всеравно ругается...
странно, может число больше поставить (на связанных таблицах блокировок много больше чем на одиночной, и если у тебя есть внешние ключи...), а может ты не отовсюду вышел, это же свойства COM обьекта, если он висит в памяти то вряд ли параметры меняются.
> Вопрос: Существует ли возможность добавить из программы в физическую базу сам код генерации идентификатора, потом также новый запрос?
в смысле? GUID формировать? если функцию ищеш то я ее не нашол (хотя чуство такое что должно быть чтото вроде NewID() как в MSSQL)
а если просто в поле значение занести, то уже сказал как автоматом вносится, можеш изначально таблицу создать со счетчиком - кодом репликации (GUID) остальную структуру копию твоей таблицы и делать insert into ... , аналогично все заполнится.
или имееш ввиду счетчик такой программно сделать? запросом у меня не получалось, а вот для ADOX примеры видел, легко найдеш в инете, или могу на бейсике из хелпа положить. разберешся? на паскаль не переводил.
кстати действительно можно и твой изначальный код ускорить,
+ к > Vlad © (17.05.04 11:11) [21]
поставь DataSet-у серверный курсор, и свойство tabledirect. ускорение должно быть значительным.
GenerateGUID это твоя функция? не боишся, что неуникальным будет? почему не CreateGUID/CoCreateGuid из SysUtils?
← →
Rouse_ © (2004-05-17 11:40) [23]> [21] Vlad © (17.05.04 11:11)
Не помогло - я как понял при SourceDS.DataSet.Next; автоматически делается Post
> Отвязать все DataSource, связанные с этим DataSet
В смысле отвязать?
У меня идет связка
TADOConnection -> TADOTable -> TDataSource -> TDBGridEh
Ну TDBGridEh.DataSource := nil; я конечно делаю, или я не про то понял?
← →
Vlad © (2004-05-17 11:46) [24]
> Rouse_ © (17.05.04 11:40) [23]
> Не помогло - я как понял при SourceDS.DataSet.Next; автоматически
> делается Post
Да, Post делается автоматически, поэтому явно его делать смысла нет, только после цикла, чтобы запостить последнюю запись.
> TADOConnection -> TADOTable -> TDataSource -> TDBGridEh
Ну эта связка вызывает значительные тормоза.
Можно перед циклом сделать ADOTable.DisableControls, тогда быстрее будет, но все равно, лучше на время цикла занилить DataSource.DataSet
← →
Rouse_ © (2004-05-17 11:52) [25]> или могу на бейсике из хелпа положить.
По идее должен разобраться :)
> не боишся, что неуникальным будет?
Это просто пример - в действительности используется более сложная функция на основе CoCreateGuid
С курсором получилось - выставил Серверный и все летает :)
Правда почемуто не дает динамически создавать новую колонку - но с этим я сейчас разберусь...
← →
Rouse_ © (2004-05-17 11:57) [26]> [24] Vlad © (17.05.04 11:46)
Погоди, но я же через этот датасет и добавляю записи, зачем его нилить?
Или я просто не понял о чем ты говоришь?
← →
Vlad © (2004-05-17 12:10) [27]
> Rouse_ © (17.05.04 11:57) [26]
Нилить нужно не сам датасет, а проперть DataSource.DataSet
То есть таким образом отвязываешь DataSource от AdoTable
и цикл будет такой
while not ADOTable.Eof() т.е. без всяких DataSource и связанных контролов, которые обычно тормозят цикл
← →
sniknik © (2004-05-17 12:18) [28]> По идее должен разобраться :)
чегойто в хелпе не находится ;), видел точно есть..
это тоже самое, даже лутше
http://support.microsoft.com/default.aspx?scid=http://support.microsoft.com:80/support/kb/Articles/q297/9/80.asp&NoWebCo ntent=1
← →
Rouse_ © (2004-05-17 20:41) [29]Оффтоп:
Так как в большинстве своем раньше я занимался исключительно сетью, а сейчас приходится работать с базами хочу заполнить пробелы в своих знаниях.
(Вы только представьте какой объем новой информации приходится впитывать :)
А именно:
Меня очень насторожил момент с изменением типа курсора...
Я походил по хелпам и коду - основу я для себя выяснил, но для меня остался неочевидным один момент.
К примеру что я понял:
Серверный тип курсора применим для работы с большими наборами данных, в отличия от клиентского курсора, который дает большую гибкость с исполнением различных запросов...
(Если я ошибаюсь - поправьте)
Вопрос:
Если можно, обьясните в чем принцип работы курсора, за что он отвечает, в каких случаях лучше применять один тип, а в каких другой...
Если есть ссылки на материалы - это даже лучше...
← →
Vlad © (2004-05-17 21:07) [30]
> Вопрос:
> Если можно, обьясните в чем принцип работы курсора, за что
> он отвечает, в каких случаях лучше применять один тип, а
> в каких другой...
Клиентский курсор - это что-то вроде локального буфера. Туда попадают сразу все записи, удовлетворяющие твоему запросу. Удобен тем, что перемещения по нему достаточно быстрые, в отличие от серверного, но есть недостаток - возрастает нагрузка на сеть, поскольку приходится передавать полный набор данных.
Ну и естественно при больших объемах данных фетч записей с сервера будет происходить довольно долго.
Серверный курсор дает обычно выигрыш когда сеть перегружена, так как в клиентский набор данных не попадают сразу все записи, а только те, с которыми работает пользователь. Т.е. fetch записей с сервера происходит по мере необходимости(напр. по мере скроллинга DataSet). Удобно использовать когда делаешь Insert, Update, Delete. Ну, естественно, недостаток - бОльшая нагрузка на сервер.
Вот вкратце где-то так :-)
← →
Rouse_ © (2004-05-17 21:30) [31]> [30] Vlad © (17.05.04 21:07)
Понял, спасибо...
С точки зрения нагрузки на сеть - понял великолепно, но вот сразу возникает такой вопрос:
Как я себе это представляю - помоему намного проще сразу получить целиковый набор данных, отключится от сервера, произвести необходимые изменения и отправить их обратно...
Как я понял, для этого существует TClientDataSet...
Такой вариант имеет право на жизнь и каковы его возможности?
И второе:
Мне немного не понятно именно с нагрузкой на сеть - если база у меня локальна, т.е. я загружаю ее из той же папки что и сам EXE - почему происходят такие тормоза при клиентском курсоре, ведь сетевая карточка адсолютно не используется?
Что в данный момент является сервером? JET драйвер?
← →
Vlad © (2004-05-17 21:48) [32]
> помоему намного проще сразу получить целиковый набор данных,
> отключится от сервера, произвести необходимые изменения
> и отправить их обратно...
Ну да, многие так и делают, напр. в трехзвенных приложениях, или просто когда сетка слабая или коннект отваливается постоянно. Тут конечно удобнее целиком набор данных получить на клиента, и с ним работать. Не обязательно TClientDataSet, можно и ADODataSet использовать, насколько я знаю. Вполне ходовой вариант.
Но опять же, если наборы данных очень большие, как в твоем случае, то невыгодно их целиком тащить на клиента.
> я загружаю ее из той же папки что и сам EXE - почему происходят
> такие тормоза при клиентском курсоре, ведь сетевая карточка
> адсолютно не используется?
Ну во-первых у тебя открытие самого набора данных(ADOTable.Open) при клиентском курсоре и таком количестве записей должно занимать минут пять, а то и более. Что касается перемещения по набору данных.... тут вопрос интересный, затрудняюсь ответить. Теоретически оно должно проходить быстрее чем при серверном курсоре. А практически, видимо влияет то, что довольно большой объем данных сразу загружается в виртуальную память.... хотя не буду фантазировать, т.к. не уверен, что в этом дело
← →
Vlad © (2004-05-17 21:58) [33]Да, забыл добавить: по поводу, что является сервером, а что клиентом - думаю, для Access это условная терминология, поскольку Access сам по себе не является клиент-серверной СУБД, это файл-серверная СУБД.
Хотя по вопросам ADO лучше всего расскажет тебе sniknik ©, потому что я не так много с ним работал
и еще
> помоему намного проще сразу получить целиковый набор данных,
> отключится от сервера, произвести необходимые изменения
> и отправить их обратно...
одним из удобств TClientDataSet является то, что он получает локальный набор данных, а при отсылке изменений на сервер он не отсылает весь набор, а только дельту, т.е. только то, что было изменено.
← →
Rouse_ © (2004-05-17 22:20) [34]> [33] Vlad © (17.05.04 21:58)
Спасибо, большинство вопросов отпало...
По мере возникновения буду спрашивать :)
← →
sniknik © (2004-05-18 00:37) [35]Vlad © (17.05.04 21:07) [30]
> возрастает нагрузка на сеть, поскольку приходится передавать полный набор данных.
не всегда, все от задачи зависит. если предполагаются многократные "прогоны" по данным то клиентсский выгоднее. и даже однократный прогон по всем данным (если заранее знаеш что все что запрос возвращает нужно) клиентский будет менее "напряжным" т.к. пакет передается легче чем позаписьно, а если учитывать, что есть проверочные данные, то проверок на каждую запись и по обьему будет больше, коственно это можно подтвердить (как это работает внутри я нигде не читал, только предположения) делаеш выборку с клиентским курсором засекаеш время после с темже запросом серверный и делаеш last чтобы выкачать все (или фетч олл) и тоже засекаеш, во втором случае на порядок больше, причем различается настолько что я даже предполагал что пакет для клиентского, когда все вместе идет, архивируется. разница получалась, порядка, 2-3 сек. против примерно 18ти. (вот трафик когда тестил померять не догадался :(, а теперь уже нет желания повторять)
> Да, забыл добавить: по поводу, что является сервером, а что клиентом - думаю, для Access это условная терминология,
это точно, для аксесс(jet) это просто другой режим работы, практически прямой табличный доступ как (в BDE родных движках), действуют индексы, поиск по индексу, что в любых других движках не работает (индекс в них нужно создавать на "лету" в рекордсете, а accecc используется из базы). это во всяком случае пока (может кто еще повторит, потом), в хелпе этот особый режим jet специально упоминается.
> одним из удобств TClientDataSet является то, что он получает локальный набор данных, а при отсылке изменений на сервер он не отсылает
> весь набор, а только дельту, т.е. только то, что было изменено.
штука конечно хорошая, но придется еще мидас включать, что в общемто не нужно, есть аналогичный(похожий) механизм в самом ADO - UpdateBatch, включается установкой LockType = ltBatchOptimistic. там еще куча всего почитай "Using batch updates" в хелпе. большинством из этого и еще многого, многого я не пользовался... хотя с ADO работаю и долго, но там действительно дофига всего, не все пригождается в непосредственной в работе.
← →
sniknik © (2004-05-18 00:51) [36]> Ну во-первых у тебя открытие самого набора данных(ADOTable.Open) при клиентском курсоре и таком количестве записей должно занимать минут пять, а то и более.
обижаеш ;о) пять минут... 171714 открывает 3.054 сек, при серверном (с директтабле) а также с асинхронным фетчем время открытия гораздо меньше.
(но вообщето сильно зависит от размера записи, и если там куча текстовых полей... (до 255 символов, если больше это уже мемо и сам текст не передается), тогда да, но и то для 5мин это надо крутую запись иметь, длинную)
← →
Vlad © (2004-05-18 10:20) [37]
> sniknik © (18.05.04 00:51) [36]
> обижаеш ;о) пять минут... 171714 открывает 3.054 сек
это при клиентском курсоре ?
Тогда возможно у тебя всего 2-3 поля, и те числовые :-)
Я пробовал на 80 тыс. записей, правда полей штук 15 и все символьные. При клиентском курсоре открывал 2-3 минуты.
← →
sniknik © (2004-05-18 10:50) [38]> это при клиентском курсоре ?
ага
> Тогда возможно у тебя всего 2-3 поля, и те числовые :-)
не не так все плохо ;о) 3 поля char (не varchar) размеры 20,22,50 и 8 полей числа, double и int.
> Я пробовал на 80 тыс. записей, правда полей штук 15 и все символьные. При клиентском курсоре открывал 2-3 минуты.
может машина? памяти мало к примеру, своп подключается... база старая(не уверен в скорости базы от 97 офиса, почти не приходилось работать), да сам jet (версия, не устарела?), ну или если действительно 15 символьных полей ... запись обьемная. (но это все равно много 3мин, имхо)
могу прогу прислать для теста, быстрее чем там мне не удавалось запрос выполнить. посмотриш в ней сравниш (если актуально конечно). (размер ~400кб архив)
← →
Vlad © (2004-05-18 11:15) [39]
> sniknik © (18.05.04 10:50) [38]
Оперативки 256, хотя своп при открытии использовался на полную катушку. Версию jet не помню, давно это было, кажется год назад. Но база была Access 97, операционка win-2000.
Использовал Дельфи 5 без всяких апдейтов (может, кстати в этом дело)
Страницы: 1 вся ветка
Форум: "Базы";
Текущий архив: 2004.06.06;
Скачать: [xml.tar.bz2];
Память: 0.58 MB
Время: 0.041 c