Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Базы данных и репортинг > ODAC


Автор: AndreyZ53 13.1.2015, 19:08
Доброго времени суток. Для доступа к oraсle использую компоненты odaс.

Вопрос заключается в том, что когда делаю запрос, то приложение зависает, так как в базе миллионы записей и пока найдет нужную, то проходит порядка 12 минут. Это дело понятное. Данный вопрос я решил, установив свойство nonbloсking компонента TOraQuery в True и добавил анимацию. Теперь запрос выполняется в отдельном потоке, приложение не виснет, а при помощи анимации (бегунок), пользователь видит, что приложение не зависло и что-то ищет.

Но теперь вопрос вот в чем, если там из 10 миллиона записей нашло 100 тысяч, то в dbgrid (у меня грид с ehlib 3.6, бесплатной версии), то он отображает по 25-50 записей, начинаю прокручивать грид, приложение опять зависает, чтобы отобразить следующие 25-50 записей на минут 5, но уже именно зависает. А как мне можно сделать и тут, чтобы оно не зависало и появлялась анимация, что оно ищет остальные записи, может кто подскажет?

Автор: Vas 13.1.2015, 20:24
Цитата(AndreyZ53 @  13.1.2015,  19:08 Найти цитируемый пост)
Вопрос заключается в том, что когда делаю запрос, то приложение зависает, так как в базе миллионы записей и пока найдет нужную, то проходит порядка 12 минут.

Тут видимо все-таки надо с индексами на таблицу разобраться, миллион записей и 12 минут - это как-то очень много, тем более для оракуля. Такое чувство, что не TOraQuery используется, а TOraTable с включенным свойством filtered  и установленым фильтром.


Цитата(AndreyZ53 @  13.1.2015,  19:08 Найти цитируемый пост)
Но теперь вопрос вот в чем, если там из 10 миллиона записей нашло 100 тысяч

Нужны ли пользователю все 100 тысяч записей сразу не задавались таким вопросом?


Цитата(AndreyZ53 @  13.1.2015,  19:08 Найти цитируемый пост)
то он отображает по 25-50 записей, начинаю прокручивать грид, приложение опять зависает, чтобы отобразить следующие 25-50 записей на минут 5, но уже именно зависает.

А если в TOraQuery установить выбор не по 25 записей, а например по 1000 пусть пользователь минутку покрутит, а потом подумает и обратиться к фильтру с помощью которого отберет те нужные 10-100 записей. Если конечно фильтр у вас предусмотрен в программе.

Автор: AndreyZ53 13.1.2015, 20:34
Vas, спасибо за ответ. Насчет индексов ничего не могу сказать, но клиент sqldeveloper, через который я работаю с базой и таблицами, запрос примерно также делает.

База не моя, существует уже много лет, как пришел на работу, она уже стояла там с 2007 года, каждый месяц в нее добавляется по 350-400 тысяч записей.

Использую именно OraQuery, я с odaс не так давно работаю, еще мало про него знаю.

Индекса конечно есть в базе, но я с ораклом как-то не очень, база есть, подключился, просто хочу для себя написать приложение небольшое, чтобы оно выводило мне набор записей за день по определенному условию, а не каждый раз я бы подключался к oraсle через sqldeveloper и что-то там делал.

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

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

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

В OraQuery выставил 1000 записей чтобы выводило, но может можно как-то ускорить быстроту выполнения запроса в многомиллионной БД?

Автор: Vas 13.1.2015, 20:58
Цитата(AndreyZ53 @  13.1.2015,  20:34 Найти цитируемый пост)
В OraQuery выставил 1000 записей чтобы выводило, но может можно как-то ускорить быстроту выполнения запроса в многомиллионной БД?

Это вопрос в профильный раздел форума "Базы данных" там быстрее помогут.

Автор: superVad 14.1.2015, 10:41
AndreyZ53, выход из этого - расположить над гридом фильтр и по какой то кнопке отображать записи. Если условия не выбраны, то предупреждать, что это будет долго.
Было подобное у меня, пока вышли из положения таким образом, что при входе в программу показываются записи за последний месяц (фильтр так установлен).
Никому не надо 100 тыс. записей в гриде.

Автор: AndreyZ53 14.1.2015, 10:54
superVad, что-то я не могу сообразить, подскажите еще пожалуйста.

При открытии формы у меня стоит выполнение запроса.
Код

  OraQuery1.SQL.Clear;
  OraQuery1.SQL.Add('SELECT MAIL.*,KOMFORT.* FROM MAIL LEFT JOIN KOMFORT ON MAIL.ID_MAIL=KOMFORT.JARLIK');
  OraQuery1.Active:=True;
  OraQuery1.Filtered:=True;
  OraQuery1.Filter:='TYP=29';


Отображается 25 записей у меня сразу же, так как в свойстве FetchRows компонента TOraQuery стоит 25. Почему-то фильтр не срабатывает сразу, выводит все записи, то есть, ТИП 29 он игнорирует. Начинаю пролистывать грид, и чтобы он отобразил следующие 25 записей, приложение просто зависает, как этого избежать?

Точнее оно зависает, если я резко бегунок прокрутки грида опускаю вниз, если понемного опускаю, то появляются новые записи без зависания, но если резко, то приложение зависает и думают, через некоторое время отвисает и показывает следующие записи

Автор: superVad 14.1.2015, 13:58
AndreyZ53, на таких количествах записей, возможно стоит отказаться от родной фильтрации компонента.
У меня на основе установленных фильтров формируется соответствующий SQL-запрос.

Т.е.
Код

  OraQuery1.SQL.Clear;
  OraQuery1.SQL.Add('SELECT MAIL.*,KOMFORT.* FROM MAIL LEFT JOIN KOMFORT ON MAIL.ID_MAIL=KOMFORT.JARLIK');
  if true then //тут какое то свое условие
    OraQuery1.SQL.Add('WHERE TYP=29');
  OraQuery1.Active:=True;


Добавлено через 2 минуты и 12 секунд
Ну и каждый раз при изменении фильтрации набор данных закрывается, переформируется запрос и набор данных открывается. Это все можно оформить как одну функцию, что бы она срабатывала и при открытии программы и при изменении условий фильтрации.

Добавлено через 4 минуты
Ну и насчет того почему игнорирует TYP=29. Может попробовать так:
Код

  OraQuery1.Filter:='TYP=29';
  OraQuery1.Filtered:=True;
  OraQuery1.Active:=True;

Автор: Vas 14.1.2015, 20:11
Я в таких случаях открываю окно с пустым гридом, над гридом есть панелька с выбором каких то диапазонов или значений фильтров и кнопка выполнить.  Причем компоненты фильтрации пустые. так что если просто нажать кнопку выполнить то будет тянуться все из грида  smile  Но пользователи уже давно знают, что прежде чем нажать выполнить нужно выбрать критерий фильтра, иначе придется идти курить.

P.S.
Но из десятков миллионов записей запрос 12 минут для оракуля это не просто дофига, а ужас как дофига для простого запроса с условием и связыванием 1-2 таблиц. Так что сначала оптимизация БД, а потом уже реализация в программе, а то глядишь оно и так сразу залетает  у него  smile

Добавлено через 2 минуты и 33 секунды
Цитата(AndreyZ53 @  14.1.2015,  10:54 Найти цитируемый пост)
При открытии формы у меня стоит выполнение запроса.
Код

  OraQuery1.SQL.Clear;
  OraQuery1.SQL.Add('SELECT MAIL.*,KOMFORT.* FROM MAIL LEFT JOIN KOMFORT ON MAIL.ID_MAIL=KOMFORT.JARLIK');
  OraQuery1.Active:=True;
  OraQuery1.Filtered:=True;
  OraQuery1.Filter:='TYP=29';


В таком режиме у вас не TOraQuery, а TOraTable. Как я и думал у вас все миллионы тянутся по сети на ваш комп или пользователя, а потом в памяти фильтруется (ужас блин  smile ).
RTFM SQL для чайников причем срочно.

Автор: AndreyZ53 28.1.2015, 11:21
Vas, Так а пример небольшой можно, как правильно TOraquery пользоваться, либо ссылочку на литературку.

Автор: Vas 28.1.2015, 21:13
Вот же пример, который superVad привел в своем сообщении, осмелюсь продублировать
Код

 OraQuery1.SQL.Clear;
  OraQuery1.SQL.Add('SELECT MAIL.*,KOMFORT.* FROM MAIL LEFT JOIN KOMFORT ON MAIL.ID_MAIL=KOMFORT.JARLIK');
  if true then //тут какое то свое условие
    OraQuery1.SQL.Add('WHERE TYP=29');
  OraQuery1.Active:=True;

Если его выполнить, то выберутся только записи с TYP=29, причем их выберет сервер и передаст ТОЛЬКО ИХ клиенту, тем самым не будет по сети лететь вся таблица, что существенно сократит расходы времени на передачу данных по сети. 

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

P.S. Для начала надо изучить диалект SQL и тогда понимание запросов и работы с ними само придет.

Автор: AndreyZ53 29.1.2015, 08:41
Vas, Спасибо за разъяснения, у меня сейчас запрос выполняется именно с условием. Но допустим, пользователю прилетела табличка из 200 записей с условием WHERE TYP=29. А ему необходимо из 200, найти быстро запись, допустим по фамилии, тогда же уже необходимо будет применять локальный фильтр, правильно? без него же никак? Но вот с локальным фильтром не могу разобраться, что-то он у меня не работает.

Код

    if Trim(TextEditNP.Text)<>'' then
     begin
      OraQueryMAIL.Filter:='ID_MAIL='+TextEditNP.Text;
      OraQueryMAIL.Filtered:=True;
     end;


Поле ID_MAIL в базе имеет тип VARCHAR, может я фильтр не правильно указываю?

И еще такой вопросик в тему: вот запрос выполняется, пусть минуты 3, а можно ли мне его корректно завершить, чтобы не было никаких ошибок. Грубо говоря остановить его и например запустить опять, чтобы все было корректно?

Или как мне использовать этот запрос для нового SQL-оператора. Я сделал SELECT, мне сервер вернул записи, а потом я делаю SQL.Clear для этого запроса, пишу новый, но мне уже выкидывает ошибку:

Автор: ТоляМБА 29.1.2015, 15:28
Цитата(AndreyZ53 @  29.1.2015,  10:41 Найти цитируемый пост)
 можно ли мне его корректно завершить, чтобы не было никаких ошибок. Грубо говоря остановить его и например запустить опять, чтобы все было корректно?

Если запрос на Select, то сомневаюсь что его можно как-то завершить.
Если же запрос на Insert / Update / Delete почитайте о транзакциях.

Автор: AndreyZ53 29.1.2015, 15:45
ТоляМБА, Жаль конечно, может можно как-то на уровне OCI это сделать? Просто дело в том, что у меня есть OraQuery, я выполняю SELECT, мне сервер вернул записи, по сути запрос выполнился. Затем я хочу написать другой запрос, но в том же OraQuery, а мне выдает ошибку, которую я прикрепил выше!

Автор: ТоляМБА 29.1.2015, 16:11
Перед 
OraQuery1.SQL.Clear;
  OraQuery1.SQL.Add(...
неплохо бы сделать OraQuery1.Active:=false; то есть закрыть действующий запрос.

Автор: AndreyZ53 29.1.2015, 16:30
я делал:

Код

    OraQuery1.Active:=False;
    OraQuery1.SQL.Clear;
    OraQuery1.SQL.Add('SELECT MAIL.*,KOMFORT.* FROM MAIL LEFT JOIN KOMFORT ON MAIL.ID_MAIL=KOMFORT.JARLIK WHERE MAIL.dt_form between'+''''+DateToStr(myDateDec)+''' and '+''''+DateToStr(myDate)+''' and MAIL.TYP>=29 and MAIL.TYP<=34');
    OraQuery1.Active:=True;


И делал OraQuery.BreakExec, не помогает, не могу использовать тот же самый OraQuery для другого SELECT, не могу понять почему

Автор: ТоляМБА 29.1.2015, 16:54
AndreyZ53, попробуй этот же запрос выполнить на OraQuery2
Цитата(AndreyZ53 @  29.1.2015,  18:30 Найти цитируемый пост)
OraQuery1.SQL.Add('SELECT MAIL.*,KOMFORT.* FROM MAIL LEFT JOIN KOMFORT ON MAIL.ID_MAIL=KOMFORT.JARLIK WHERE MAIL.dt_form between'+''''+DateToStr(myDateDec)+''' and '+''''+DateToStr(myDate)+''' and MAIL.TYP>=29 and MAIL.TYP<=34');

И два совета:
1. Вместо +''''+DateToStr(myDate)+''' можно использовать +QuotedStr(DateToStr(myDate))+
2. Вместо and MAIL.TYP>=29 and MAIL.TYP<=34 можно тоже использовать Between

Автор: AndreyZ53 29.1.2015, 17:16
ТоляМБА, В другом OraQuery выполняется норм запрос, но у меня один Dbgrid, в котором и нужно отображать различные данные. Я думаю к DataSource подключать разные OraQuery не совсем правильно будет!

Автор: superVad 29.1.2015, 17:25
Цитата(ТоляМБА @  29.1.2015,  15:54 Найти цитируемый пост)
1. Вместо +''''+DateToStr(myDate)+''' можно использовать +QuotedStr(DateToStr(myDate))+2. Вместо and MAIL.TYP>=29 and MAIL.TYP<=34 можно тоже использовать Between

Еще можно использовать параметры.

Автор: AndreyZ53 30.1.2015, 10:36
superVad, 
ТоляМБА,

Спасибо за дополнения и разъяснения, но может знает, почему я не могу использовать один и тот же OraQuery для разных запросов. OraSession у меня одна, может там что с транзакциями надо делать, типа закрывать их. Не совсем все понятно!

Автор: Vas 31.1.2015, 16:09
Никогда проблем с выполнением разных запросов в одном OraQuery не было.
Вот разные процедуры используют один и тот же OraQuery для выполнения разных запросов
Код

Function TForm1.CheckRole(const Role:string):Boolean;
Begin
...
  with OraQueryUser do
    begin
      SQL.Clear;
      SQL.Add('select role as con from session_roles where role='+QuotedStr(Role));
      try
        Open;
        if RecordCount>0 then
          begin
            Result:=True;
            Exit;
          end;
      except
...
      end;
      Close;
    end;
End;

Procedure TForm1.CheckUserPrivs(const AUserName: string);
var i:integer;
Begin
...
  with OraQueryUser do
    begin
      SQL.Clear;
      SQL.Add('select up.* from user_privs up, users u where up.id_users=u.id and u.u_locked=''F'' and u.username=:USERNAME');
      Params.ParamByName('USERNAME').AsString:=AUserName;
      try
        Open;
      except
        on E:Exception do
...
      end;
      First;
      while not Eof do
        begin
...
           Next;
        end;
      Close;
    end;
End;

Очищаем, добавляем текст запроса, выполняем, делаем манипуляции, закрываем.
Код не весь, часть блоко выкинута и заменена "..."

Автор: AndreyZ53 11.2.2015, 11:17
Vas, я делаю почти тоже самое, только запрос у меня записан в самом компоненте. Вот:

Код

SELECT MAIL.*,KOMFORT.* FROM MAIL 
LEFT JOIN KOMFORT ON MAIL.ID_MAIL=KOMFORT.JARLIK 
WHERE MAIL.dt_form between :date1 and :date2 and MAIL.TYP>=29 and MAIL.TYP<=34


У компонента выставлены свойства AutoCommit и AutoClose в True, на одну кнопку стоит такой код:

Код

    OraQuery1.ParamByName('date1').Value:=FirstDay1;
    OraQuery1.ParamByName('date2').Value:=LastDay1;
    OraQuery1.Execute;


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

Автор: Vas 11.2.2015, 14:28
Open для конструкций SELECT
Execute для конструкций INSERT, UPDATE, DELETE

Закрывайте открытый запрос перед передачей других параметров
Код

    if OraQuery1.Active then OraQuery1.Close; //если запрос открыт, то закрываем 
    //а потом уже передаем новые параметры и выполняем
    OraQuery1.ParamByName('date1').Value:=FirstDay1;
    OraQuery1.ParamByName('date2').Value:=LastDay1;
    OraQuery1.Open;

Автор: AndreyZ53 17.2.2015, 09:08
Vas, Делаю как Вы написали, вот такой вот эррор:


Автор: salik 17.2.2015, 13:00
Цитата(AndreyZ53 @ 17.2.2015,  10:08)
Делаю как Вы написали, вот такой вот эррор:

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

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)