Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Начинающим";
Текущий архив: 2013.03.22;
Скачать: [xml.tar.bz2];

Вниз

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

 
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;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.59 MB
Время: 0.14 c
15-1350304912
8903765 0706
2012-10-15 16:41
2013.03.22
Внимание! Ищут машину, которая сбила 15.09 ребенка на стоянке


15-1339946385
Artem
2012-06-17 19:19
2013.03.22
Откуда-то появился Norton Internet Security


2-1328618080
JohnKorsh
2012-02-07 16:34
2013.03.22
Работа с MediaPlayer.


15-1332489521
ProgRAMmer Dimonych
2012-03-23 11:58
2013.03.22
Уникальность ключа


15-1346255072
Artem
2012-08-29 19:44
2013.03.22
MessageDlg в Visual C++





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский