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


Автор: serega1983 2.10.2013, 11:00
Доброго времени суток.
Есть база данных Firebird.
Необходимо реализовать списание товаров со склада.
Делаю следующее.
1) создаю расходную накладную
2) заполняю ее данными из таблицы склада
3) выставляю необходимо количество товара для списания
4) жму сохранить и записи из dataSet вставляются в таблицу расходная накладная
5) со склада вычитается кол-во товара, указанное в dataSet
Проблема возникает со списанием товара со склада.

Как делаю.

Таблицы
user posted image

Форма
user posted image

Код

FDataSet: TIBCustomDataSet;
ibQuery.cashedUpdate := true;




запрос для query
Код

select ss.amount,
       ss.ID,
       ss.product_id,
       s.title
from sales_invoice ss
join storehouse s
    on s.id            = ss.product_id
    and ss.register_id = :idDocument
order by s.title


Последний вариант моего "мучения" был такой 

Код

procedure TForm1.Button2Click(Sender: TObject);
var Q: TIBQuery;
begin
  try
      IBTransactionWriteoffProducts.StartTransaction;
      if FDataSet.RecordCount = 0 then begin
          raise Exception.Create('Документ пуст!');
      end;

      Q := TIBQuery.Create(nil);
      Q.Transaction := IBTransactionWriteoffProducts;
      FDataSet.First;
      while not FDataSet.Eof do begin
          if(FDataSet.CachedUpdateStatus = cusInserted) then begin
              Q.Close;
              Q.SQL.Clear;
              Q.SQL.Add('update storehouse set amount = amount - :amount where id = :id and amount - :amount >= 0');
              Q.Prepare;
              Q.ParamByName('id').AsInteger   := FDataSet.FieldByName('product_id').AsInteger;
              Q.ParamByName('amount').AsFloat := FDataSet.FieldByName('amount').AsFloat;
              Q.ExecSQL;

              if(Q.RowsAffected < 1) then begin
                  raise Exception.Create('Вы пытаетесь списать товаров больше чем их есть на складе. Товар "'
                                         + FDataSet.FieldByName('title').AsString + '" кол-во ' + FDataSet.FieldByName('amount').AsString);
              end;
          end;

          FDataSet.Next;
      end;


      FDataSet.Database.ApplyUpdates([FDataSet]);
      IBTransactionWriteoffProducts.Commit;
  except
      on E : Exception do begin
          IBTransactionWriteoffProducts.Rollback;
          ShowMessage(E.ClassName + ' ошибка : ' + E.Message);
      end;
  end;
end;

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

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

Автор: DYUMON 2.10.2013, 11:20
если в птичке есть тригеры то кури в их сторону

Автор: serega1983 2.10.2013, 11:26
Цитата(DYUMON @ 2.10.2013,  11:20)
если в птичке есть тригеры то кури в их сторону

Не используя возможности БД(тригерры, хранимые процедуры), есть варианты?

Автор: Akella 2.10.2013, 15:19
Цитата(serega1983 @  2.10.2013,  11:00 Найти цитируемый пост)
ошибка транзакция уже активна.

Так а зачем ты её второй раз стартуешь?

Добавлено @ 15:19
Цитата(serega1983 @  2.10.2013,  11:00 Найти цитируемый пост)
Почему так происходит я понимаю. Как это победить - не знаю.

Значит не понимаешь.

Добавлено @ 15:20
Зачем используешь механизм cashedUpdate?

Добавлено через 5 минут и 22 секунды
У тебя обе операции должны проходить в рамках одной активной транзакции (IBTransactionWriteoffProducts). Обе квери должны быть подключены к одной и той же  транзакции.

Если условно, то так.
Код

if not IBTransactionWriteoffProducts.inTransaction then IBTransactionWriteoffProducts.Start;
Try
  списываешь товар
  добавляешь в расходную накладную
  IBTransactionWriteoffProducts.commit;
except
  IBTransactionWriteoffProducts.rollback;
end;

Автор: serega1983 2.10.2013, 15:50
Цитата(Akella @ 2.10.2013,  15:19)
Так а зачем ты её второй раз стартуешь?


FDataSet.Database.ApplyUpdates([FDataSet]);
Здесь она стартует автоматически.

Цитата(Akella @ 2.10.2013,  15:19)
Значит не понимаешь.

Не исключаем такого варианта =)

Цитата(Akella @ 2.10.2013,  15:19)
Зачем используешь механизм cashedUpdate?


Для занесения изменений в БД после нажатия кнопочки "Сохранить"

Цитата(Akella @ 2.10.2013,  15:19)
У тебя обе операции должны проходить в рамках одной активной транзакции (IBTransactionWriteoffProducts). Обе квери должны быть подключены к одной и той же  транзакции.

Все привязано к транзакции IBTransactionWriteoffProducts;







Автор: serega1983 2.10.2013, 21:49
Проблема решилась сама, но скорее всего было 

Цитата(Akella @  2.10.2013,  15:19 Найти цитируемый пост)
У тебя обе операции должны проходить в рамках одной активной транзакции (IBTransactionWriteoffProducts). Обе квери должны быть подключены к одной и той же  транзакции.


и это добавил
Цитата(Akella @  2.10.2013,  15:19 Найти цитируемый пост)

Код

if not IBTransactionWriteoffProducts.inTransaction then IBTransactionWriteoffProducts.Start;





Всем спасибо.

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