Вопрос по IBX

Вопросы программирования и использования среды Lazarus.

Модератор: Модераторы

Re: Вопрос по IBX

Сообщение Mikhail » 30.03.2015 20:12:29

Я правильно понимаю, что переменные в EXECUTE BLOCK нужно передавать явно, через заголовок?

Добавлено спустя 19 минут 31 секунду:
Пишет
invalid request BLR at offset 58 bad parameter number

Код: Выделить всё
execute block (
name1 varchar(16) = :NAME,
surname1 varchar(32) = :SURNAME,
birthday1 date = :BIRTHDAY
)
as
begin
insert into PERSONS (NAME, SURNAME, BIRTHDAY, SNILS)
values (:name1, :surname1, :birthday1, '16712398734');
end

Где я неправ? :?
Последний раз редактировалось Mikhail 30.03.2015 20:37:40, всего редактировалось 1 раз.
Mikhail
энтузиаст
 
Сообщения: 562
Зарегистрирован: 24.10.2013 16:06:47

Re: Вопрос по IBX

Сообщение *Rik* » 30.03.2015 20:34:06

Не совсем понял вопрос. Если поместить такой запрос в InsertSQL,
Код: Выделить всё
EXECUTE Block (
    CHKSTAT integer = :chkstat,
    KOMMENT varchar(30) = :komment)
RETURNS (
    DOCOTK_ID BIGINT)
AS
BEGIN
 
END

то chkstat и komment будут доступны как параметры
Код: Выделить всё
TIBDataSet.QInsert.PN('chkstat').asInteger := AValue;

Значения полей, имена которых совпадают с именами параметров, перед выполнением запроса будут подставлены в параметры автоматически, т.е. работаем с набором данных как обычно:
Код: Выделить всё
DOCOTK.Append;
DOCOTK.FN('chkstat').AsInteger := 10;
DOCOTK.FN('komment').AsString := 'ыаываы';
DOCOTK.Post;


Добавлено спустя 3 минуты 39 секунд:
Mikhail писал(а):Пишет
invalid request BLR at offset 58 bad parameter number

Код: Выделить всё
execute block (
name1 varchar(16) = :NAME,
surname1 varchar(32) = :SURNAME,
birthday1 date = :DATE
)
as
begin
insert into PERSONS (NAME, SURNAME, BIRTHDAY, SNILS)
values (:name1, :surname1, :birthday1, '16712398734');
end

Где я неправ? :?

Попробуйте
Код: Выделить всё
birthday1 date = :DATE

заменить имя параметра DATE на другое, возможно имя параметра совпало с именем типа.
Аватара пользователя
*Rik*
постоялец
 
Сообщения: 426
Зарегистрирован: 19.04.2011 12:18:51
Откуда: Урал

Re: Вопрос по IBX

Сообщение Mikhail » 30.03.2015 20:43:08

*Rik* писал(а):заменить имя параметра DATE на другое, возможно имя параметра совпало с именем типа.


Это у меня очепятка была, там другое имя birthday.
*Rik* писал(а):Не совсем понял вопрос.

т.е. так не верно?
Код: Выделить всё
execute block
as
begin
insert into PERSONS (NAME, SURNAME, BIRTHDAY, SNILS)
values (:NAME, :SURNAME, :BIRTHDAY, '16712398734');
end

?
Mikhail
энтузиаст
 
Сообщения: 562
Зарегистрирован: 24.10.2013 16:06:47

Re: Вопрос по IBX

Сообщение *Rik* » 30.03.2015 20:47:57

Mikhail писал(а):
*Rik* писал(а):заменить имя параметра DATE на другое, возможно имя параметра совпало с именем типа.


Это у меня очепятка была, там другое имя birthday.
*Rik* писал(а):Не совсем понял вопрос.

т.е. так не верно?
Код: Выделить всё
execute block
as
begin
insert into PERSONS (NAME, SURNAME, BIRTHDAY, SNILS)
values (:NAME, :SURNAME, :BIRTHDAY, '16712398734');
end

?

Нет так не правильно, в документации FireBird все указано четко. В TIBDataSet для EXECUTE BLOCK параметры парсятся до ключевого слова BEGIN.
EXECUTE BLOCK был введен в версии FireBird 2. У Вас случайно FireBird не 1.*?
Аватара пользователя
*Rik*
постоялец
 
Сообщения: 426
Зарегистрирован: 19.04.2011 12:18:51
Откуда: Урал

Re: Вопрос по IBX

Сообщение Mikhail » 30.03.2015 20:53:42

*Rik* писал(а):EXECUTE BLOCK был введен в версии FireBird 2. У Вас случайно FireBird не 1.*?

Firebird 2.5.3 32bit

Добавлено спустя 4 минуты 22 секунды:
Код: Выделить всё
execute block (
name1 varchar(16) NOT NULL = :NAME,
surname1 varchar(32) NOT NULL = :SURNAME,
birthday1 date NOT NULL = :BIRTHDAY
)
as
begin
insert into PERSONS (NAME, SURNAME, BIRTHDAY, SNILS)
values ('gxcvhcjgx', 'dfdfjhdjfhkj', '01.01.1991', '16712398734');
end

если на место переменных поместить константы, то не выдает ошибок :?
Mikhail
энтузиаст
 
Сообщения: 562
Зарегистрирован: 24.10.2013 16:06:47

Re: Вопрос по IBX

Сообщение *Rik* » 30.03.2015 21:01:16

Mikhail писал(а):
*Rik* писал(а):EXECUTE BLOCK был введен в версии FireBird 2. У Вас случайно FireBird не 1.*?

Firebird 2.5.3 32bit

Должно работать. У меня та-же версия сервера, я привел свои примеры взятые из рабочей программы..
Самый лучший вариант сначала сделать хранимую процедуру (IBExpert к тому-же поможет написать весь PSQL), отладить в виде процедуры, затем перенести её на клиента заменив заголовок на EXECUTE BLOCK.

Добавлено спустя 4 минуты 58 секунд:
Вот ещё, руководство на русском, EXECUTE BLOCK на 212 странице.
http://www.firebirdsql.org/file/documentation/reference_manuals/Firebird-Language-Reference-Russian.pdf

Добавлено спустя 58 минут 30 секунд:
Mikhail
Я сделал таблицу с такими-же полями как у Вас, работает, сравните с Вашим, может что-то прояснится..
https://youtu.be/_tuenS8P_go
Только на видео на совсем Lazarus..
Аватара пользователя
*Rik*
постоялец
 
Сообщения: 426
Зарегистрирован: 19.04.2011 12:18:51
Откуда: Урал

Re: Вопрос по IBX

Сообщение Mikhail » 01.04.2015 15:58:25

Rik, пробую с рекомендуемыми Вами компонентами. Не ругается, все собирается, но изменения в таблице не сохраняются. :?

Rik, Вы автор этих компонентов?
Mikhail
энтузиаст
 
Сообщения: 562
Зарегистрирован: 24.10.2013 16:06:47

Re: Вопрос по IBX

Сообщение *Rik* » 01.04.2015 17:51:07

Mikhail писал(а):Rik, пробую с рекомендуемыми Вами компонентами. Не ругается, все собирается, но изменения в таблице не сохраняются. :?

Мне надо видеть что у Вас там, как компоненты связаны, как транзакции стартуют.. Можете свой проект вместе с базой скинуть, ну или наверно я для примера лучше свой изготовлю..
Mikhail писал(а):Rik, Вы автор этих компонентов?

Я не автор, но получилось так, что веду свою ветку развития.. Когда-то на Delphi я пользовался FIBPlus, но разработчики напрочь отказались портировать FIBPlus на Lazarus, хотя конкуренты к тому времени уже давно портировали на Lazarus и AnyDac и UniDac ,. Сперва хотел купить IBDac но моя жаба долго возмущалась ценой, пока я договаривался с жабой, обнаружил что есть IBX for Lazarus, я стал расширять их функционал до уровня к которому привык...

Добавлено спустя 1 час 27 минут 51 секунду:
Простейшее тестовое приложение, в котором для вставки и редактирования записи в запросах используется EXECUTE BLOCK, проверка корректности вводимых значений так-же вынесены в EXECUTE BLOCK.
Запрос c EXECUTE BLOCK на добавления записи использует выражение RETURNING для возврата значения первичного ключа полученного в триггере, которое автоматически, сразу после выполнения запроса подставляется в локальный кэш IBDataset. В примере показано как использовать свойство TIBDataSet.OrderFields для задания сортировки (метод сортировки не работает с локальным кэшем набора данных, а парсит и изменяет/добаляет инструкцию ORDER BY в SELECT).
Кроме этого пример показывает, как перехватить обработку исключений в приложении для замены дурацкого окна сообщений об ошибках на свое собственное, одновременно в обработчике исключений перехватываются сообщения об ошибках FireBird, сгенерированные в хранимых процедурах, триггерах, для вывода их в нормальном виде (В FIBPlus этим занимался отдельный компонент).
http://visual-t.ru/files/IBTest2.7z

Для работы приложения требуется установленный SQL сервер FireBird версии 2.5 либо библиотека локального доступа fbembed.* версии 2.5.
Перед сборкой тестового приложения в редакторе компонента IBDatabase1 требуется прописать путь к базе TESTDB.FDB (лежит в папке с проектом) и путь к клиентской библиотеке FireBird.
Клиентская библиотека в комплекте поставки отсутствует.
Аватара пользователя
*Rik*
постоялец
 
Сообщения: 426
Зарегистрирован: 19.04.2011 12:18:51
Откуда: Урал

Re: Вопрос по IBX

Сообщение Mikhail » 02.04.2015 10:12:12

*Rik* писал(а):Простейшее тестовое приложение, в котором для вставки и редактирования записи в запросах используется EXECUTE BLOCK, проверка корректности вводимых значений так-же вынесены в EXECUTE BLOCK.
Запрос c EXECUTE BLOCK на добавления записи использует выражение RETURNING для возврата значения первичного ключа полученного в триггере, которое автоматически, сразу после выполнения запроса подставляется в локальный кэш IBDataset. В примере показано как использовать свойство TIBDataSet.OrderFields для задания сортировки (метод сортировки не работает с локальным кэшем набора данных, а парсит и изменяет/добаляет инструкцию ORDER BY в SELECT).
Кроме этого пример показывает, как перехватить обработку исключений в приложении для замены дурацкого окна сообщений об ошибках на свое собственное, одновременно в обработчике исключений перехватываются сообщения об ошибках FireBird, сгенерированные в хранимых процедурах, триггерах, для вывода их в нормальном виде (В FIBPlus этим занимался отдельный компонент).


Спасибо за пример, запустил - все работает. Буду разбираться дальше. Осталось еще несколько вопросов:
- Назначение поля GeneratorField, зачем оно нужно?
- как лучше сделать master-detail, использовать свойство datasource TIBDataset, менять запросы "налету"?
- можно ли завести свои переменные (параметры) в запросе и менять их из Lazarus?
- компоненты вроде TDBEdit закрывают транзакцию на запись сразу же после потери фокуса? А читающая транзакция транзакция когда закрывается, в конце работы программы? TDataSet.Edit открывает транзакцию на запись?
- лучше использовать компоненты TDBEditXXX или использовать обычные и добавлять все результаты в следующем коде
Код: Выделить всё
trWrite.StartTransaction; // это нужно?
DB.Edit;
Db.FieldByName('name').AsString:='skdjfksdjl';
...
Db.Post;
trWrite.Commit;
Mikhail
энтузиаст
 
Сообщения: 562
Зарегистрирован: 24.10.2013 16:06:47

Re: Вопрос по IBX

Сообщение *Rik* » 02.04.2015 12:02:37

Mikhail писал(а): - Назначение поля GeneratorField, зачем оно нужно?

GenerartorField - используется для получения первичного ключа на клиенте. В своем примере я показал как обойтись без GenerartorField, воспользовавшись возможностями сервера используя выражение RERURNING для передачи первичного ключа клиенту. В свойстве Generator указывается имя генератора (последовательности). В свойстве ApplyOnEvent - оно задает когда компонента будет запрашивать значение ключа для новой записи. Если значение gaeOnNewRecord - то GenerartorField запросит значения с сервера в момент добавления новой записи, недостаток - если вы отмените запись - значение пропадет. Если gaeOnPostRecord - значение первичного ключа будет запрошено в момент Post новой записи, для того чтобы GeneratorField работал, все свойства GenerartorField должны быть заполнены. У GenerartorField есть ещё одно назначение - вернее у его свойство Field (где указывается имя поля первичного ключа) - оно используется для FullRefresh и OrderFields, чтобы курсор всегда оставался на выбранной записи и не убегал в начало после обновления всего набора данных. Даже если вы захотите использовать RETURNING в SQL запросах для получения первичного ключа и не будете использовать GenerartorField, его свойство Field все равно лучше заполнить, указав имя поля первичного ключа, т.к. может понадобиться для FullRefresh или OrderFields.
Mikhail писал(а): - как лучше сделать master-detail, использовать свойство datasource TIBDataset, менять запросы "налету"?

Лучше использовать свойство DataSource, механизм master-detail существенно переработан и жизнь облегчает значительно. Обратите внимание на DetailCondition - используется для управления detail в связке с мастер. В нем если включен dcForceOpenClose - открывает detail автоматически при открытии master и автоматически закрывает его при закрытии master. Если включен dcForceMasterRefresh - вызывает у master.Refresh после detail.Post или detail.Delete. Если включен dcForceMasterPost - то в момент detail.BeforeInsert или detail.BeforeEdit проверяет, если master.State in [dsInsert, dsEdit] то делает ему master.Post.
Mikhail писал(а): - можно ли завести свои переменные (параметры) в запросе и менять их из Lazarus?

Не совсем понял вопрос.
Код: Выделить всё
IBDataSet.SelectSQL.Text := 'SELECT FROM MYTABLE WHERE MYFIELD = :MYPARAM';
IBDataSet.PN('MYPARAM').AsString := Value;
IBDataSet.Open;
....
IBDataSet.Close;
IBDataSet.PN('MYPARAM').AsString := NewValue;
IBDataSet.Open;


Mikhail писал(а): - компоненты вроде TDBEdit закрывают транзакцию на запись сразу же после потери фокуса? А читающая транзакция транзакция когда закрывается, в конце работы программы? TDataSet.Edit открывает транзакцию на запись?
- лучше использовать компоненты TDBEditXXX или использовать обычные и добавлять все результаты в следующем коде
trWrite.StartTransaction; // это нужно?
DB.Edit;
Db.FieldByName('name').AsString:='skdjfksdjl';
...
Db.Post;
trWrite.Commit;
[/code]

Лучше конечно использовать компоненты для работы с данными.
По поводу транзакций.
В тестовом приложении вы можете увидеть 2 TIBTransaction - IBRead и IBWrite.
IBRead - читающая транзакция, ей заданы параметры:
Код: Выделить всё
read
read_commited
rec_varsion
nowait

С такими параметрами транзакция стартует в режиме только чтение, не создает версии записей, соответственно не оказывает ни какого влияния на работу сервера и поэтому может жить сколь угодно долго, месяцами, возможно годами. Сделайте такую-же в своем приложении и все что нужно читать, читайте через неё, т.е. у всех TIBDataSet.Transaction должно быть IBRead. У меня эта транзакция всегда открыта, я стартую её руками после подключения к базе данных, и завершаю руками при закрытии приложения. В противном случае, если читающую транзакцию не держать всё время открытой, это буде приводить к тому, что если понадобится повторно открыть Select запрос, который уже ранее открывался, то у него буде всё время вызываться метод Prepare перед каждым открытием запроса Select, что приведет к сбросу списка параметров и их значения каждый раз нужно будет устанавливать по новой.
Пример:
Код: Выделить всё
IBDataSet.PN('GOD').AsInteger := 2015;
IBDataSet.Open;
IBDataSet.Close;
//Если после Close читающая транзакция остается открытой, то параметр GOD ни куда не денется
//Можно смело открывать запрос повторно, значение 2015 на месте
IBDataSet.Open;

Код: Выделить всё
IBDataSet.PN('GOD').AsInteger := 2015;
IBDataSet.Open;
IBDataSet.Close;
//Если после Close читающая транзакция закрылась, то значение параметра GOD тоже ликвидируется
//Поэтому потребуется
IBDataSet.PN('GOD').AsInteger := 2015;
IBDataSet.Open;


IBWrite - пишущая транзакция, ей заданы параметры:
Код: Выделить всё
write
wait
no_rec_version
read_commited
lock_timeout = 10

Я использую такую транзакцию для всех коротких пишущих запросов, для TIBDataSet, TIBStoredProc, TIBQuery с опцией AutoCommit := True. Т.е. для всех TIBDataSet в своем приложении, у которых AutoCommit = True (включено по умолчанию) назначьте в свойстве UpdateTransaction - IBWrite. Пишущая транзакция будет сама стартовать и завершаться во время метода Post или Delete. Пока вы редактируете запись, ни чего не происходит. Когда вы вызываете метод Post или Delete, стартует транзакция, данные пересылаются на сервер, транзакция подтверждается.

Смысл работы в рамках 2х транзакций в том, чтобы снизить "затыки" в многопользовательской работе и снизить возможность появления deadlock. Эти параметры позволят "писателям" не блокировать "читателей", в случае если "писатель" нарвется на запись заблокированную другим "писателем", сработает режим ожидания, пока запись не освободится и вы избежите ошибку блокировки. Короткие пишущие транзакции выполняются быстро, обычно пользователи даже не догадываются что там какие-то конфликты транзакций происходят.

Частные случаи.
Иногда нужно в рамках одной транзакции записать блок записей в одну или несколько таблиц, и в случае ошибки, для сохранения логической целостности данных, откатить транзакцию, вернув всё в исходное состояние. Для таких случаев, стоит завести отдельную пишущую TIBTransaction. У всех компонент, которые будут её использовать поставить AutoCommit = False. Запускать транзакцию и подтверждать (откатывать) её нужно будет вручную.
Код: Выделить всё
IBDataSet1.UpdateTransaction := DocWriteTransaction;
IBDataSet2.UpdateTransaction := DocWriteTransaction;
MyProc.Transaction := DocWriteTransaction;
IBDataSet1.Autocommit := False;
IBDataSet2.Autocommit := False;
MyProc.AutoCommit := False;
DocWriteTransaction.StartTransaction;
try

IBDataSet1.Append;
....................
IBDataSet1.Post;

IBDataSet2.Append;
...............
IBDataSet2.Post;

IBDataSet2.Append;
...............
IBDataSet2.Post;

IBDataSet2.Append;
...............
IBDataSet2.Post;

MyProc.ExecProc;

DocWriteTransaction.commit;

finally
  //Если на этом этапе DocWriteTransaction активна, значит произошла ошибка, откатываем изменения.
  if DocWriteTransaction.InTransaction then DocWriteTransaction.RollBack;
end;


С транзакциями нужно быть очень осторожным, чтобы избежать застревание Oldest Active Transaction (OAT), описанный подход поможет в этом, я постарался сделать так чтобы избежать застревание OAT. В любом случае почаще заглядывайте в монитор транзакций, что-бы увидеть не создают ли какие-либо операции проблем с OAT.
Аватара пользователя
*Rik*
постоялец
 
Сообщения: 426
Зарегистрирован: 19.04.2011 12:18:51
Откуда: Урал

Re: Вопрос по IBX

Сообщение Mikhail » 04.04.2015 17:55:54

*Rik* писал(а):С транзакциями нужно быть очень осторожным, чтобы избежать застревание Oldest Active Transaction (OAT), описанный подход поможет в этом, я постарался сделать так чтобы избежать застревание OAT. В любом случае почаще заглядывайте в монитор транзакций, что-бы увидеть не создают ли какие-либо операции проблем с OAT.


Спасибо за развернутый ответ, буду разбираться и пробовать. :)
Mikhail
энтузиаст
 
Сообщения: 562
Зарегистрирован: 24.10.2013 16:06:47

Re: Вопрос по IBX

Сообщение *Rik* » 04.04.2015 21:46:55

Mikhail писал(а):
*Rik* писал(а):С транзакциями нужно быть очень осторожным, чтобы избежать застревание Oldest Active Transaction (OAT), описанный подход поможет в этом, я постарался сделать так чтобы избежать застревание OAT. В любом случае почаще заглядывайте в монитор транзакций, что-бы увидеть не создают ли какие-либо операции проблем с OAT.


Спасибо за развернутый ответ, буду разбираться и пробовать. :)


Есть ещё один нюанс, я про него не знал... Я описал методику своего использования, при которой читающую транзакцию держу всё время открытой, по определенным причинам. Оказывается, если использовать текстовые блобы, и если имеется конкатенация блобов между собой или блобов со строками, или используется оператор List в запросе для работы с блобами, это приводит к созданию временных блобов, которые удаляются только после завершения транзакции, даже если это транзакция Read Only. Поэтому, если собираетесь использовать блобы и будут операции конкатенации, их лучше в отдельную читающую транзакцию, которую не стоит долго держать открытой.
http://www.ibase.ru/devinfo/dbgrowth.html
Аватара пользователя
*Rik*
постоялец
 
Сообщения: 426
Зарегистрирован: 19.04.2011 12:18:51
Откуда: Урал

Пред.

Вернуться в Lazarus

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 28

Рейтинг@Mail.ru