21 октября 2021

FireDAC vs UniDAC. Получение значения первичного ключа новой строки

    Два года тому назад я писал о получении в программе значения первичного ключа, который сгенерирован СУБД при добавлении новой строки в таблицу. Мои примеры вызова INSERT с модификатором RETURNING (или OUTPUT в случае MS SQL Server) были с использованием библиотеки для доступа к базам данных UniDAC. Давайте посмотрим, как реализована эта возможность в библиотеке FireDAC, которая уже много лет входит в поставку Delphi и C++Builder.
    Что бы не повторяться я воспользуюсь тестовыми таблицами из предыдущей статьи, а в этой только адаптирую код, написанный с использованием TUniQuery из UniDAC к использованию с TFDQuery из FireDAC.

1. Oracle. Здесь у FireDAC мы имеем код аналогичный UniDAC – сгенерированный первичный ключ возвращается через параметр:

q := TFDQuery.Create(nil);
q.Connection := dbOra;
q.SQL.Text := 'insert into test(id, t) ' +
              'values (test_id.nextval, ''test'') ' +
              'returning id into :id';
q.Params[0].ParamType := ptResult;//ptOutput ptInputOutput
q.Params[0].DataType  := ftInteger;
q.Execute;
Writeln(q.Params[0].Name, ' ', q.Params[0].AsString);
Тип этого параметра может принимать три значения: ptResult, ptOutput и ptInputOutput.

2. PostgreSQL. Применить для FireDAC код аналогичный UniDAC не получилось. Вызов метода Execute приводит к ошибке:

[FireDAC][Phys][PG]-310. Cannot execute command returning result sets. Hint: use Open method for SELECT-like commands
FireDAC, в отличие от UniDAC, при вызове метода Execute не определяет вид запроса (у UniDAC методы Execute и ExecSQL можно использовать вместо метода Open для запроса данных) и не учитывает то, что RETURNING у PostgreSQL возвращает DataSet. Поэтому для подобного случая у TFDQuery необходимо использовать метод Open:
q := TFDQuery.Create(nil);
q.Connection := dbPG;
q.SQL.Text := 'insert into public.test(t) values (''test'') returning id';
q.Open;
Writeln(q.Fields[0].FieldName, ' ', q.Fields[0].AsString);
Для тех, кому не нравится вставлять данные вызывая метод Open, разработчики FireDAC предусмотрели возможность добавить в текст запроса с RETURNING псевдопараметр "INTO" экранированный фигурными скобками "{INTO :PARAM_NAME}":
q := TFDQuery.Create(nil);
q.Connection := dbPG;
q.SQL.Text := 'insert into public.test(t) values (''test'') returning id {into :id}';
q.Params[0].ParamType := ptOutput;//ptInputOutput
q.Params[0].DataType  := ftInteger;
q.Execute;
Writeln(q.Params[0].Name, ' ', q.Params[0].AsString);
Тип псевдопараметра "INTO" может принимать только два значения: ptOutput и ptInputOutput.

3. Firebird/InterBase. Работает аналогично как у PostgreSQL: или Open или Execute с псевдопараметром "INTO".

4. MS SQL Server. Про то, как у MS SQL Server обойти отсутствие RETURNING я писал прошлый раз. Учитывая мое предложение использовать для этого временную таблицу INSERTED, то для FireDAC сразу заменим метод Execute на Open:

q := TFDQuery.Create(nil);
q.Connection := dbMS;
q.SQL.Text := 'insert into test(t) output INSERTED.ID values (''test'')';
q.Open;
Writeln(q.Fields[0].FieldName, ' ', q.Fields[0].AsString);
То есть, если бы я в примере с UniDAC использовал Open, то код менять не пришлось бы.

Подведем итог.

  1. Обе библиотеки для получения значения первичного ключа новой строки работают одинаково – в зависимости от СУБД возвращают его через параметр (используя метод Execute/ExecSQL) или через поле (используя метод Open).
  2. У FireDAC есть одно небольшое преимущество – наличие псевдопараметра "INTO", который позволяет использовать INSERT с RETURNING одинаково на разных СУБД (Execute + Params).

Комментариев нет:

Отправить комментарий