基于先前选择查询的事务中的多个参数化Delphi SQL更新

时间:2015-07-13 10:51:30

标签: sql-server delphi parameters sqlite transactions

我使用Delphi XE8中的参数化查询在同一循环中更新两个不同的SQL表。整个事情都包含在一个事务中,因此如果循环中的任何内容都失败,那么这两个表都不会更新。

我的原始查询已发布here,以下是此网站提供帮助后的当前简化代码:

begin
  Query1:=TSQLQuery.Create(nil);
  try
    Query1.SQLConnection:=Connection;
    Query1.SQL.Text:='UPDATE TABLE A SET QUANTITY = :Quantity 
                      WHERE PRODUCT_NAME = :Product_name'; 

    Query2:=TSQLQuery.Create(nil);
    try
      Query2.SQLConnection:=Connection;
      Query2.SQL.Text:= 'UPDATE TABLE B SET QUANTITY = :Quantity 
                         WHERE PRODUCT_NAME = :Product_name'; 

      Transaction:=Connection.BeginTransaction;
      try
        for I := 1 to whatever to
        begin
          { fill params here and execute the commands }
          Query1.ExecSQL;
          Query2.ExecSQL;
        end;
        Connection.CommitFreeAndNil(Transaction);
      except
        Connection.RollbackFreeAndNil(Transaction);
        raise;
      end;
      .... etc.    

我刚刚意识到我可能有问题,因为表B的更新部分(即查询2)涉及首先从表B中检索记录,然后根据其中一个值更新它返回。

所以,如果我充实了上面的循环:

for I:= 1 to whatever do
begin
  //Retrieve relevant values from file being read 
  Product_name:=Product_name[I]; 
  Quantity:=Value[I];

  //Execute query 1, no problems here 
  SQL_query1.Params.ParamByName('Product_name').AsString:=
                                                   Product_name;
  SQL_query1.Params.ParamByName('Quantity').AsString:=
                                                   Quantity;      
  Query1.ExecSQL;

  //Interim get from Table B 
  //I am using datasets here that are already open in my actual code, 
  //but it could also be a SQL_query3 component; I am simply showing 
  //the logic here of what's going on
  SQL_dataset1.CommandType:=ctQuery; 
  SQL_dataset1.CommandText:=
      'SELECT QUANTITY FROM TABLE B WHERE PRODUCT_NAME = '+Product_name;
  SQL_dataset1.Open; 

  Old_quantity:=SQL_dataset1.FieldByName('Quantity').AsString;

  New_quantity:=Old_quantity+Quantity;

  //Execute query 2
  SQL_query2.Params.ParamByName('Product_name').AsString:=
                                                   Trim_str(Product_name);
  SQL_query2.Params.ParamByName('Quantity').AsString:=
                                                   Trim_str(Quantity);   
  Query2.ExecSQL; 
  ... etc.
end;

因此整个循环理论上可以更新相同产品的数量,更新的数量基于之前的数量。

这是否可能,或者我是否必须一次解决一个更新? 对不起,如果这是一个愚蠢的问题。

此外,虽然表A肯定可以使用上面的代码进行更新,因为它没有表B所示的问题,如果表存在任何问题,我根本不想让它更新B更新。

第2部分:简化示例

表A是我实际上试图从这个问题中理解的噪音,对不起,所以让我只用表B来改写。发生的事情是正在按顺序读取文件,并且基于在每一行的信息中,表B的总计必须更新。

所以,让我们说表B有以下记录:

产品名称数量
红色小部件3
蓝色小部件5

我们按顺序读取小部件购买的文件,该文件是随机排列的,并且包含一些混合的红色和蓝色购买。

例如:

  1. 红色小部件+6
  2. 红色小部件+2
  3. Blue widget +1
  4. 红色小工具+2 ......等等。
  5. 查看下面的代码示例....

    Query2.SQL.Text:= 'UPDATE TABLE B SET QUANTITY = :Quantity 
                       WHERE PRODUCT_NAME = :Product_name'; 
    
    for I := 1 to file length do
    begin
      //get the current quantity for that widget
      //add the quantity purchased in that row in the file to the 
      //quantity just retrieved 
    
      SQL_query2.Params.ParamByName('Product_name').AsString:=
                                                       Trim_str(Product_name);
      SQL_query2.Params.ParamByName('Quantity').AsString:=
                                                       Trim_str(Quantity); 
      Query2.ExecSQL;
    end;
    

    ....我的问题是:将循环遍历参数化查询,如此更新运行总计我们一起去?这可能是一个愚蠢的问题,但只是试图围绕它和这之间的差异...

    for I := 1 to file length do
    begin
      //get the current quantity for that widget
      //add the quantity purchased in that row in the file to the  
      //quantity just retrieved 
    
      Query2.SQL.Text:= 'UPDATE TABLE B SET QUANTITY = ' + Quantity + 
                        'WHERE PRODUCT_NAME = 'Red widget'; 
      Query2.ExecSQL;
    end; 
    

    ...当你继续进行 更新时。只是想确保我正确理解这些参数化查询。根据我的阅读,如果您使用参数,肯定会有一些优化?我认为我不清楚/正确的地方是我的印象是“优化'使用参数时,并不意味着减少了到数据库的次数? 对数据库的调用次数减少=运行总数不同步,这是我头脑中的一个问题!

    显然,表格和文件比这更复杂,我们将ID设置为键等。我的坏例子只是出于问题逻辑的目的。一旦我理解了这一点,我就可以运用我有限的知识来改进查询!

1 个答案:

答案 0 :(得分:2)

作为对您尝试执行的操作的猜测,我认为以下SQL方法优于您尝试循环更新。我的基础是包含数量变化列表的表A和包含当前数量的表B(即如果表B有3个foo,2个条和表A有+ 2 foo,-1 foo,+ 1 bar结果之后的结果操作将是表B有4 foo和3 bar)

UPDATE TableA
SET Quantity = TableA.Quantity + 
   (SELECT sum(tableB.Change)
    FROM TableB
    WHERE TableA.ID = TableB.ID)

这适用于Fiddle for SQL Server,YMMV(http://sqlfiddle.com/#!6/017df/7/0

另外,您可能希望加入ProductID主键而不是产品名称。如果您想了解原因,请查看数据库规范化

为了覆盖你的实际问题,我看不出任何原因它不会起作用(它只会比上面的单个SQL语句慢很多)。该交易有效地“冻结”您使用UPDATE语句接触的任何记录。您将能够使用(SELECT和UPDATE)任何被操纵的记录,就像没有事务一样,但是其他人可能会或可能不会看到它们,这取决于其他数据库设置(并且肯定无法更新/删除它们),所以只要你不在一个单独的SQLConnection中运行你的其他查询,你就可以了。

修改后的问题编辑:

是的,这应该可行,但我强烈建议

UPDATE TableB 
SET Quantity = Quantity + :QuantityChange 
WHERE PRODUCT_NAME = :Product_name

然后您不需要运行其他SELECT查询(除非您需要更新的总客户端,例如在写出日志时)。参数的最大好处是防止SQL注入攻击,但它也可以帮助DB端的查询优化。就数据库的访问而言,每次执行都会得到其中一个,优化只是意味着数据库每次花费更少的时间来考虑它。