Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2013.03.22;
Скачать: CL | DM;

Вниз

Работа с БД в отдельном треде.   Найти похожие ветки 

 
Alex_C   (2012-03-11 11:52) [0]

В продолжении темы о необходимости нескольких ADOConnection, если работа с базой происходит в отдельном треде.
Вот есть какая проблема.
Дано ADO+Access.
Есть ClientDataSet - база клиентов фирмы.
Есть SpravDataSet - общий справочник жителей города N.
После того, как в ClientDataSet вносится новый клиент нужно в справочной базе SpravDataSet проверить, есть ли такой человек, если есть - отметить, что он теперь клиент фирмы.
Делаем это на событие AfterPost для ClientDataSet.
Дабы не происходило зависания программы при поиске соответствующей записи в базе справочника SpravDataSet , было принято решение работу с ним вынести в отдельный тред.
В отдельном треде создаем свой ADOConnection и ADODataSet для соединения с базой справочника. Делаем необходимые корректировки в базе - тут все ок, корректировки проходят как надо.
Теперь нам нужно обновить содержание окна, которое содержит таблицу со SpravDataSet . Для этого перед завершением работы треда вызываем метод Synchronize, в котором делаем  

SpravDataSet.CommandText := "SELECT * FROM..."
SpravDataSet.Active := True;

И видим, что у нас отображаются "старые" данные по справочнику, без учета внесенных в треде изменений.
Если же затем мы вручную, по кнопке выполним код

SpravDataSet.CommandText := "SELECT * FROM..."
SpravDataSet.Active := True;

то видим, что все изменения внеслись.
Вопрос: как сделать так, чтоб изменения были видны из метода Synchronize?


 
sniknik ©   (2012-03-11 12:02) [1]

кончай херней маяться... обучись хоть чему то. проверка/запрос на 1 запись делается тысячную долю секунды, куда там поток? у тебя дольше? читай пост сначала.


 
Сергей М. ©   (2012-03-11 13:51) [2]


> После того, как в ClientDataSet вносится новый клиент нужно
> в справочной базе SpravDataSet проверить, есть ли такой
> человек


Хрень какая-то ...

в ClientDataSet нет и не должно быть никакого "человека" - ни такого ни другого - там должна быть ссылка на соотв.элемент SpravDataSet.

Если на момент подтверждения создания записи в ClientDataSet (событие AfterPost) в SpravDataSet  еще нет соотв.записи, то это ситуация сознательного и грубого нарушения ссылочной целостности, чреватая граблищами.


 
Alex_C   (2012-03-11 15:12) [3]


> проверка/запрос на 1 запись делается тысячную долю секунды,
>  куда там поток? у тебя дольше?


У меня дольше. Потому как я пытался тут максимально упростить объяснение задачи.
В реальной задаче приходится проверять более сложное условие. Выполнение запроса без отдельного треда получается порядка 2 сек - что для моего условия не приемлимо по скорости.


> в ClientDataSet нет


Прошу прощения - скажем так - имя его должно быть типа SpisokClientDataSet: TADODataSet. Неудачное название для примера подобрал, не обратя внимание , что оно совпадает с элементом TClientDataSet.


 
Alex_C   (2012-03-11 15:16) [4]

Да, саму работу в отдельном треде делал согласно этой рекоммендации.
http://delphi.about.com/od/kbthread/a/query_threading.htm


 
sniknik ©   (2012-03-11 15:30) [5]

> упростить объяснение задачи.
УПРОСТИТЬ??? а где ДЕЙСТВИЯ??? что ДЕЛАЕТСЯ? описания не связанной херни, выдуманной необходимости потоков, и других ДОМЫСЛОВ, "пугалок" (о сложных условиях), и приемлемости ничем не подтвержденного (и скорее ошибочного, из-за кривых рук результата) ничем не упрощают понимание того, что делается/в чем проблема.

проверка
запрос на обновление -
UPDATE n_kladr
SET Name = "Северный"
WHERE Code = "2301500003000"


в базе 204686 записей (кладр, больше чем у тебя?), поле code индексное (не ключ), по "плохому" строковому полю, т.е. не самое быстрое, выполнение идет - ... а не знаю сколько, у меня в тесте деление до тысячных секунд, показывает - 00:00.000.
ГДЕ НУЖЕН ПОТОК?


 
Ega23 ©   (2012-03-11 15:36) [6]


> проверка
> запрос на обновление -


Я не сильно вникал, но по-моему ему нужна конструкция Insert or Update. Которая есть далеко не везде.


 
Anatoly Podgoretsky ©   (2012-03-11 15:37) [7]


> Да, саму работу в отдельном треде делал согласно этой рекоммендации.

CoUninitialize не защищен секцией try, код опасный


 
Anatoly Podgoretsky ©   (2012-03-11 15:39) [8]

Кроме того никакой конкретики, код не приведен. Разговор ни о чем.


 
Alex_C   (2012-03-11 15:41) [9]

Послушай, я хочу не в данной конкретной задаче, а для себя понять, как работать в отдельном треде с БД. Работа с БД в отдельном треде не считается чем то плохим.
Вот сейчас у меня возник конкретный вопрос: я в отдельном треде изменил данные. Теперь эти измененные данные я хочу отобразить в таблице.
По твоему негативному к этому отношения, я так понимаю ты считаешь что работать с БД в отдельном треде не следует. Ок! Я твою точку зрения понял.
Теперь вопрос к другим: это все так считают, что следует избегать рабоать с БД в отдельном треде?


 
Alex_C   (2012-03-11 15:42) [10]

[9] пост относился к sniknik.


 
sniknik ©   (2012-03-11 15:46) [11]

> проверка
"усложнил"
UPDATE n_street
SET Name = "Межевая"
WHERE Code = "47010011003000600"

на более объемной таблице - 899615 записей, индекс "шире". время тоже - 00:00.000.
НА ХРЕНА ПОТОКИ?

> согласно этой рекоммендации.
в рекомендации может быть все идеально, но главный глюк не рекомендации/справка/система/объект/компонент, и т.д., а ТЫ. именно ты делаешь глюки, именно ТВОЙ код нужно проверять, пусть он и сделан по самой расчудесной рекомендации.

p.s. как-то читал про культуристов... их рекомендации. так основной и самой первой было - "встаньте перед зеркалом и перечислите все свои недостатки. честно, не лукавя. т.к. если вы будете кивать на другое, кривое зеркало там/неправильное мнение о вас окружающих... толку не будет".
а вот у начинающих "программистов" процветает самообман... типа у меня все правильно, остальное виновато. и если честно сказать такому глюк это ТЫ, начинаются "визги", ой меня оскорбили...
но это правда.


 
Alex_C   (2012-03-11 15:46) [12]

ок! вот реальный код.


procedure TRecalcColorThread.Execute;
var
 FDataSet: TADODataSet;
 FLogDataSet: TADODataSet;
 S: string;

 {$I GetCountryIndex.pas}

begin
 CoInitialize(nil);
 SpotMachineForm.SpotResLock.Lock;
 try
   FDataSet := TADODataSet.Create(nil);
   with FDataSet do
   begin
     ConnectionString := DataBaseConnectionString;
     CursorLocation := clUseServer;

     if FCountry = "" then
       CommandText := "SELECT * FROM SpotTable"
     else
       CommandText := "SELECT * FROM SpotTable WHERE Country = " + QuotedStr(FCountry);
     Active := True;
     if RecordCount > 0 then
     begin
       FLogDataSet := TADODataSet.Create(nil);
       with FLogDataSet do
       begin
         //Connection := FLogConnection;
         ConnectionString := LogConnectionString;
         CursorLocation := clUseServer;
         S := "";
         if PaperQSLR  then
           S := "QSLR";
         if LoTWQSLR then
         begin
           if S = "" then
             S := "LoTWR"
           else
             S := S + "+LoTWR";
         end;
         if eQSLR then
         begin
           if S = "" then
             S := "eQSLR"
           else
             S := S + "+eQSLR";
         end;
         if S = "" then
           S := "0";
         if FCountry = "" then
           CommandText := "SELECT Country, BandADIF, Mode, SUM(" + S + ") AS QSL " +
             "FROM " + MainForm.LogName + " WHERE Country <> " + QuotedStr("") +
             "GROUP BY Country, BandADIF, Mode ORDER BY Country "
         else
           CommandText := "SELECT Country, BandADIF, Mode, SUM(" + S + ") AS QSL " +
             "FROM " + MainForm.LogName + " WHERE Country = " + QuotedStr(FCountry) +
             "GROUP BY Country, BandADIF, Mode ORDER BY Country ";
         Active := True;
       end;
       //**********************************************************************
       First;
       while not Eof do
       begin
         Edit;
         FieldByName("IsNewCountry").AsInteger :=
           GetCountryStatus(FieldByName("DXCall").AsString,
               FieldByName("BandADIF").AsString,
               FieldByName("Mode").AsString);
         Post;
         Next;
       end;
       FreeAndNil(FLogDataSet);
     end;
   end;
 finally
   FreeAndNil(FDataSet);
   SpotMachineForm.SpotResLock.UnLock;
   CoUninitialize;
 end;
 Synchronize(RefreshWithSpotWindow);
end;


 
RWolf ©   (2012-03-11 15:47) [13]


> это все так считают, что следует избегать рабоать с БД в
> отдельном треде?

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


 
sniknik ©   (2012-03-11 15:48) [14]

> я так понимаю ты считаешь что работать с БД в отдельном треде не следует. Ок! Я твою точку зрения понял.
опять ДОМЫСЛЫ. ламер.


 
Alex_C   (2012-03-11 15:51) [15]

Небольшое объяснение к коду:
SpotTable - это собственно та таблица, которую нежно изменять. Как видно из кода, при некоторых условиях приходится изменять все данные в этой таблице. При некоторых - только часть.
Для каждой выбранной из SpotTable записи необходимо проверить/изменить ее статус.
В итоге время получается около 2 сек.


 
RWolf ©   (2012-03-11 15:51) [16]

       First;
       while not Eof do
       begin
         Edit;
         FieldByName("IsNewCountry").AsInteger :=
           GetCountryStatus(FieldByName("DXCall").AsString,
               FieldByName("BandADIF").AsString,
               FieldByName("Mode").AsString);
         Post;
         Next;
       end;


вот этот апдейт *всех* записей таблицы, он зачем?


 
Alex_C   (2012-03-11 15:52) [17]


> ламер.

Да. Ламер. И пишу в соответствующей теме. Если тебе это так не приятно - просто пройди мимо.


 
Anatoly Podgoretsky ©   (2012-03-11 15:54) [18]

> Alex_C  (11.03.2012 15:46:12)  [12]

Почему clUseServer?


 
Alex_C   (2012-03-11 15:56) [19]


> вот этот апдейт *всех* записей таблицы, он зачем?


Не всех, а в соответствии с условием

     if FCountry = "" then
       CommandText := "SELECT * FROM SpotTable"
     else
       CommandText := "SELECT * FROM SpotTable WHERE Country = " + QuotedStr(FCountry);
     Active := True;


Все записи обходятся только при открытии БД - в остальных случаях только те, что нужны.


 
sniknik ©   (2012-03-11 15:59) [20]

> Почему clUseServer?
он думает так не будет "закачки всего", правильно в общем то, для access, но для этого нужно соблюсти все условия...
к примеру не пользоваться  RecordCount, т.к. встречая его ADO начнет закачку чисто посчитать. (сработает как fetch al)l, насколько помню)

а вот почему прямые обращения из потока типа MainForm.LogName?


 
Alex_C   (2012-03-11 15:59) [21]


> Почему clUseServer


Пробовал и clUseClient - но разницы не было.
Кстати а как в данном случае правильно?


 
Юрий Зотов ©   (2012-03-11 15:59) [22]


> Alex_C   (11.03.12 15:41) [9]
> это все так считают, что следует избегать
> рабоать с БД в отдельном треде?

От задачи зависит. Например, если данные поступают от разных юзеров, то можно для каждого юзера создать отдельный поток и этот поток будет коннектиться к БД от имени этого юзера. Юзер разлогинился - его поток завершился и уничтожился.

Если же поток создается только лишь для того, чтобы не "подвисал" GUI, то надо прикинуть, а нужно ли это реально. Если, скажем, вся операция обновления длится доли секунды, то вряд ли для нее нужен отдельный поток. Тем более, если этот поток имеет свой собственный коннект - сама операция коннекта может длиться дольше, чем все остальное.

Ну а уж если мы решили, что отдельный поток со своим коннектом все-таки нужен, то надо внимательно отследить, где и когда этот поток коммитит свою транзакцию - ведь пока он ее не закоммитит, другие коннекты изменений не увидят.


 
Alex_C   (2012-03-11 16:02) [23]


> он думает так не будет "закачки всего", правильно в общем
> то, для access, но для этого нужно соблюсти все условия.
> ..
> к примеру не пользоваться  RecordCount, т.к. встречая его
> ADO начнет закачку чисто посчитать. (сработает как fetch
> al)l, насколько помню)


Совершенно верно так я и думаю.


> не пользоваться  RecordCount, т.к. встречая его ADO начнет
> закачку чисто посчитать.


Проверял по времени - вроде как даже при его использовании все равно существенно быстрее, если при большой бд делать clUseClient.


 
Юрий Зотов ©   (2012-03-11 16:02) [24]

> пока он ее не закоммитит, другие коннекты изменений не увидят

Если, конечно, не настроены соответствующим образом уровни изоляции.


 
sniknik ©   (2012-03-11 16:09) [25]

> Кстати а как в данном случае правильно?
в данном случае поток НЕ НУЖЕН, максимум в пару запросов все решается. если вообще есть что решать (цель все этого? нафига при старте менять все значения, а после "только те, что нужны.").  

> Если, конечно, не настроены соответствующим образом уровни изоляции.
в ADO + Jet, уровни изоляции это отдельные COM объекты на каждое подключение (о чем я ему говорил уже). т.е. сделал отдельный... и жди пока он данные в файл скинет чтобы другой мог перечитать...
а вот с асинхронностью, когда он сам потоки делает, там все в порядке, сам делает, сам разбирается, что где, может и из буфера прочитать.


 
Alex_C   (2012-03-11 16:14) [26]


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


Совершенно верно - это я и хочу понять!


> настроены соответствующим образом уровни изоляции.


ilCursorStability установлены.


 
RWolf ©   (2012-03-11 16:21) [27]

моё мнение — надо исключить цикл с апдейтами, перестроив логику программы соответствующим образом.
и вообще, модификация результатов агрегатного запроса выглядит как-то подозрительно.


 
знайка   (2012-03-11 16:21) [28]


> Для каждой выбранной из SpotTable записи необходимо проверить/изменить
> ее статус.
Одним запросом это никак?


 
sniknik ©   (2012-03-11 16:23) [29]

> ilCursorStability установлены.
пофигу. но как тест, где? код твой приведен выше, создание компонент динамическое, где присваивается ilCursorStability?


 
Ega23 ©   (2012-03-11 16:28) [30]

Зачем нужна многопоточность в БД-приложении:
1. Пишем сервер приложений. Например - бэкенд за http-сервером. Надеюсь, тут объяснять не нужно.
2. Двухзвенка. Тут вариант простой: нужно ли нам залипание гуя во время длительных операций? Нужно - внедряем. Так сойдёт - не внедряем. В каких случаях залипание нежелательно? Ну, например, формируем некий офигенный отчёт. Когда snapshot-транзакция и пошло-поехало. Или длительный поиск по Full-Text Search индексу. Больше, честно говоря, не могу придумать.

А вот сборная солянка, когда что-то в основном потоке, что-то в отдельном - это скорее какая-то фигня в архитектуре. Либо всё, либо ничего (не считая этих самых отчётов и поисков).


 
Сергей М. ©   (2012-03-11 16:28) [31]


> имя его должно быть типа SpisokClientDataSet: TADODataSet.
>  Неудачное название для примера подобрал


Да не суть важно какое у него название.

Важно что это подчиненный НД.


 
sniknik ©   (2012-03-11 16:30) [32]

> но как тест
это в общем то пример, отличия "описательной" части от реальной, и чем может помочь "кивание" на рекомендации - http://delphi.about.com/od/kbthread/a/query_threading.htm
а сколько еще скрыто в "запасниках"...

> 2. Двухзвенка. Тут вариант простой: нужно ли нам залипание гуя во время длительных операций? Нужно -
внедряем асинхронность и событийность... потоки тут тоже в последнюю очередь.


 
Anatoly Podgoretsky ©   (2012-03-11 17:00) [33]

> Alex_C  (11.03.2012 15:59:21)  [21]

Ну так никакого сервера нет и в помине.


 
Ega23 ©   (2012-03-11 17:20) [34]


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


Ну тут спорный вопрос. Можно по-разному извратиться. Но от DB-Aware отказаться придётся с большой долей вероятности.
А многие не хотят. Хотят и штоб DBGrid-ы мастре-детальные, и штоб событийность и асинхронность.


 
Alex_C   (2012-03-11 21:36) [35]


> а сколько еще скрыто в "запасниках"...
>


Хорошо. Приведу максимально полное описание проблемы.
Есть основная база (50-200тыс записей) проведенных радиосвязей.
Из интернета скачиваем базу сейчас работающих в эфире станций. Количество записей в ней от 1 до 1000.
Естественно делаем это в отдельном треде. Там же ее парсим, для каждой станции высчитываем индекс нужности (есть ли такая страна в основной базе, есть ли такая страна на таком диапазоне, виде модуляции, подтверждена. Если подтверждена, то способ подтверждения - индекс достаточно сложный и одним SQL-запросом его получить нельзя). И в этом же треде заносим полученные данные в базу данных, которая называется база спотов. Теперь эту базу нам нужно отобразить. Думаю в данном случае необходимость отдельного треда очевидна?
Но это еще не все. При изменении основной базы, в базе спотов необходимо пересчитывать индексы нужности.
Пример: в базе спотов у нас присутствует информация о 100 работающих станциях из USA. Мы в основную базу занесли новую связь с USA. Теперь для каждой записи в базе спотов нам необходимо пересчитать индекс нужности. А если мы пакетом добавили 100 связей в основную базу? Но естественно интекс нужности пересчитывать нужно для всех записей. И при этом основная программа не должна тормозиться.
Думаю, после этого подробного объяснения мой приведенный выше код стал более понятным.


 
sniknik ©   (2012-03-11 22:06) [36]

> Думаю, после этого подробного объяснения мой приведенный выше код стал более понятным.
ценность этого пояснения для понимания = 0 целых х...рен десятых. тут же вообще НИЧЕГО кроме слов.

тем более словам даны собственные значения (???)...
> Из интернета скачиваем базу
вот прямо таки базу? с таблицами, индексами и т.д... тогда скачивай только данные, и тут же все ускорится...

> Там же ее парсим
базы, и даже таблицы не парсят, это структурированные данные. парсят текст.  

> высчитываем индекс нужности
ну прямо офигеть как все понятно стало, это же известный и везде изучаемый алгоритм... известнее пузырька. (сарказм, как и во всем этом посе в принципе)

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

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

> Но это еще не все.
а все... не надо больше... нафиг. "глушняк" это. не выберешься ты из ламеризма с таким подходом.


 
sniknik ©   (2012-03-11 23:00) [37]

посмотри историю
http://www.delphimaster.ru/cgi-bin/forum.pl?n=1&user=Alex_C
у тебя до сих пор одни и те же проблемы, а там тебе на разные голоса твердили "так делать нельзя", ты же при внешнем "да да я слушаю и все все учитываю",  внутренне ничего не поменял/не усвоил "мне нужно именно так, а не иначе, у меня ТЗ. логика такая... сам придумал". приехали. сейчас ты на том же уровне что тогда, уже правда немного обучен "шаманским трюкам", но знания/понимания как не было так и нет.


 
знайка   (2012-03-11 23:20) [38]

А что такое индекс нужности и зачем его в базе держать (явно)? он так сложен в расчете?


 
Inovet ©   (2012-03-11 23:29) [39]

> [38] знайка   (11.03.12 23:20)
> А что такое индекс нужности и зачем его в базе держать (явно)
> ? он так сложен в расчете?

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


 
Alex_C   (2012-03-12 09:33) [40]


> динамически расчитывается хоть каждую миниту


Раньше я его динамически расчитывал при отрисовке таблицы. Но тогда я отдельно хранил статистику по каждой сработанной стране отдельно и естественно все расчитывалось практически мгновенно.
Если сейчас расчитывать индекс нужности для каждой станции из основной базы - получается задержка при отрисовке.


> он так сложен в расчете


Я описывал выше индекс нужности:

> есть ли такая страна в основной базе, есть ли такая страна
> на таком диапазоне, виде модуляции, подтверждена. Если подтверждена,
>  то способ подтверждения - индекс достаточно сложный и одним
> SQL-запросом его получить нельзя



Страницы: 1 2 вся ветка

Текущий архив: 2013.03.22;
Скачать: CL | DM;

Наверх




Память: 0.61 MB
Время: 0.502 c
15-1347955003
Kerk
2012-09-18 11:56
2013.03.22
Книжка "Выучи Delphi за 21 день" явно устарела.


2-1339482474
Abcdef123
2012-06-12 10:27
2013.03.22
Медленно работает TdxDBTreeView.


15-1341492118
Unknown user
2012-07-05 16:41
2013.03.22
Определить или открыто главное меню


4-1259572252
keymaster
2009-11-30 12:10
2013.03.22
Работа с POS-принтером.


15-1351157222
Empleado
2012-10-25 13:27
2013.03.22
DB с четырьмя таблицами