Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
KANSoftWare

Почему RowsAffected возвращает 1, даже если записи не были вставлены при использовании запроса INSERT...SELECT?

Delphi , Базы данных , Interbase

 

В Firebird, при использовании FireDAC в Delphi, часто возникает вопрос, почему свойство RowsAffected у TFDQuery возвращает 1, даже если запрос INSERT...SELECT не вставил ни одной записи. Рассмотрим эту проблему и предложим решения, учитывая совместимость с Firebird 3.0.

Проблема:

Код, подобный приведенному ниже, может ввести в заблуждение:

uses
  FireDAC.Comp.Client, FireDAC.Phys.FB, FireDAC.Stan.Intf, FireDAC.Stan.Option;

procedure TForm1.Button1Click(Sender: TObject);
var
  FDQuery: TFDQuery;
begin
  FDQuery := TFDQuery.Create(nil);
  try
    FDQuery.Connection := FDConnection1; // Укажите ваше соединение FireDAC
    FDQuery.SQL.Text := 'INSERT INTO t(c1) SELECT c2 FROM t2 WHERE c2 = 0';
    FDQuery.ExecSQL;

    ShowMessage('RowsAffected: ' + IntToStr(FDQuery.RowsAffected));
  finally
    FDQuery.Free;
  end;
end;

В этом примере, даже если в таблице t2 нет записей, удовлетворяющих условию c2 = 0, RowsAffected все равно вернет 1.

Причина:

FireDAC, вероятно, оптимизирует обработку запросов INSERT...SELECT. Вместо того, чтобы возвращать фактическое количество вставленных строк, он просто сообщает об успешном выполнении команды INSERT. Это может быть связано с тем, что FireDAC изначально проектировался с учетом INSERT ... VALUES ..., который всегда вставляет одну строку (или выдает ошибку).

Решение 1: Раздельный SELECT и INSERT (Совместимо с Firebird 3.0)

Самый надежный и совместимый способ получить точное количество вставленных строк - это выполнить SELECT COUNT(*) перед INSERT и использовать результат для определения, были ли вставлены строки.

procedure TForm1.Button1Click(Sender: TObject);
var
  FDQuery: TFDQuery;
  RowCount: Integer;
begin
  FDQuery := TFDQuery.Create(nil);
  try
    FDQuery.Connection := FDConnection1;

    // Получаем количество строк, которые будут вставлены
    FDQuery.SQL.Text := 'SELECT COUNT(*) FROM t2 WHERE c2 = 0';
    FDQuery.Open;
    RowCount := FDQuery.Fields[0].AsInteger;
    FDQuery.Close;

    // Выполняем вставку только если есть строки для вставки
    if RowCount > 0 then
    begin
      FDQuery.SQL.Text := 'INSERT INTO t(c1) SELECT c2 FROM t2 WHERE c2 = 0';
      FDQuery.ExecSQL;
      ShowMessage('RowsAffected: ' + IntToStr(FDQuery.RowsAffected) + ', Rows actually inserted: ' + IntToStr(RowCount));
    end
    else
    begin
      ShowMessage('No rows to insert.');
    end;

  finally
    FDQuery.Free;
  end;
end;

Преимущества:

  • Совместимость с Firebird 3.0 и более ранними версиями.
  • Точное количество вставленных строк.

Недостатки:

  • Требуется два запроса к базе данных.

Решение 2: Хранимая процедура с циклом и счетчиком (Совместимо с Firebird 3.0)

Можно создать хранимую процедуру, которая выполняет SELECT с использованием FOR ... SELECT ... DO и вставляет записи в цикле, ведя подсчет вставленных строк.

-- Хранимая процедура Firebird
CREATE PROCEDURE InsertAndCount
AS
DECLARE VARIABLE RowCount INTEGER = 0;
DECLARE VARIABLE C2_Value INTEGER;
BEGIN
  FOR SELECT c2 FROM t2 WHERE c2 = 0 INTO :C2_Value DO
  BEGIN
    INSERT INTO t (c1) VALUES (:C2_Value);
    RowCount = RowCount + 1;
  END
  SUSPEND;
END

В Delphi:

procedure TForm1.Button1Click(Sender: TObject);
var
  FDQuery: TFDQuery;
  RowCount: Integer;
begin
  FDQuery := TFDQuery.Create(nil);
  try
    FDQuery.Connection := FDConnection1;
    FDQuery.SQL.Text := 'EXECUTE PROCEDURE InsertAndCount';
    FDQuery.Open;
    // Получаем RowCount из первой записи результирующего набора
    if not FDQuery.IsEmpty then
      RowCount := FDQuery.Fields[0].AsInteger
    else
      RowCount := 0;

    FDQuery.Close;
    ShowMessage('Rows inserted: ' + IntToStr(RowCount));
  finally
    FDQuery.Free;
  end;
end;

Преимущества:

  • Выполнение логики в базе данных.
  • Точное количество вставленных строк.

Недостатки:

  • Более сложная реализация.
  • Необходимость создания хранимой процедуры.

Решение 3: Использование RETURNING (Не совместимо с Firebird 3.0, работает с Firebird 4.0+ для единичных вставок и с Firebird 5+ для множественных)

Начиная с Firebird 4.0, можно использовать предложение RETURNING для получения значений вставленных строк. Однако, в Firebird 4.0 и старше, RETURNING работает только для единичных вставок. Firebird 5+ позволяет использовать RETURNING для множественных вставок.

Важно: Это решение НЕ подходит для Firebird 3.0.

// Только для Firebird 5+
procedure TForm1.Button1Click(Sender: TObject);
var
  FDQuery: TFDQuery;
  RowCount: Integer;
begin
  FDQuery := TFDQuery.Create(nil);
  try
    FDQuery.Connection := FDConnection1;
    FDQuery.SQL.Text := 'INSERT INTO t(c1) SELECT c2 FROM t2 WHERE c2 = 0 RETURNING 1';
    FDQuery.Open; // Используем Open вместо ExecSQL

    RowCount := FDQuery.RecordCount;

    FDQuery.Close;
    ShowMessage('Rows inserted: ' + IntToStr(RowCount));
  finally
    FDQuery.Free;
  end;
end;

Преимущества:

  • Относительно простое решение (для Firebird 5+).

Недостатки:

  • Не совместимо с Firebird 3.0.
  • Требует использования Open вместо ExecSQL.
  • RETURNING в Firebird 4.0 работает только для единичных вставок.

Вывод:

Для Firebird 3.0 рекомендуется использовать решение 1 (раздельный SELECT и INSERT) или решение 2 (хранимая процедура с циклом и счетчиком). Решение 3 с RETURNING не подходит из-за несовместимости с Firebird 3.0. Выбор между решениями 1 и 2 зависит от конкретных требований проекта и предпочтений разработчика. Если важна простота и читаемость кода, решение 1 предпочтительнее. Если требуется оптимизация производительности и перенос логики в базу данных, решение 2 может быть более подходящим.

Создано по материалам из источника по ссылке.

В Firebird при использовании FireDAC свойство RowsAffected у TFDQuery может возвращать 1 при запросе INSERT...SELECT, даже если записи не были вставлены, что требует использования альтернативных методов для точного подсчета вставленных строк, таких как р


Комментарии и вопросы

Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.


:: Главная :: Interbase ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-12-22 20:14:06
2025-05-21 07:44:38/0.0062088966369629/0