多个参数化 Delphi SQL 更新基于先前的 select 查询

Multiple parameterized Delphi SQL updates within a transaction based on previous select query

我正在使用 Delphi XE8 中的参数化查询在同一个循环中更新两个不同的 SQL table。整个事情都包含在一个交易中,因此如果循环中的任何事情失败,table 都不会更新。

我的原始查询已发布 ,这是在得到本网站帮助后的当前简化代码:

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.    

我刚刚意识到我可能有问题,在 table B 的更新部分(即查询 2)涉及首先从 table 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;

所以整个循环理论上可以更新同一个产品的数量,更新的数量是基于之前的数量。

这有可能吗,还是我必须一次只接受一个更新? 对不起,如果这是一个愚蠢的问题。

此外,虽然 table A 当然可以使用上面的代码进行更新,因为它没有与 table B 相同的问题,但我根本不希望它更新,如果table B 更新有任何问题。

第 2 部分:简化示例

Table A 是我实际上试图从这个问题中理解的噪音,抱歉,所以让我改用 table B。发生的事情是正在读取文件按顺序,并根据每一行的信息,必须更新 table B 的 运行 总数。

假设 table B 有以下记录:

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

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

例如:

  1. 红色小部件+6
  2. 红色小部件 +2
  3. 蓝色小部件 +1
  4. 红色小部件+2 ...等等。

查看下面的代码示例....

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; 

...其中肯定 随着您的进行而更新。只是想确保我正确理解这些参数化查询。根据我的阅读,如果使用参数,似乎肯定会有一些优化?我认为我不是 clear/correct 的地方是我的印象,即使用参数时 'optimization' 并不意味着更少的数据库访问? 对数据库的调用更少 = 运行 总数不同步,这是我脑子里的一个问题!

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

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

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

这在 Fiddle 中适用于 SQL 服务器,YMMV (http://sqlfiddle.com/#!6/017df/7/0)

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

为了解决您的实际问题,我看不出有任何理由表明它不起作用(它只会比上面的单个 SQL 语句慢很多)。该事务有效 'freezes' 您使用 UPDATE 语句触及的任何记录。您将能够使用(SELECT 和更新)任何被操纵的记录,就好像没有交易一样,但其他人可能会或可能不会看到它们,具体取决于其他数据库设置(并且绝对不能 update/delete 它们),因此只要您不 运行 您的其他查询在单独的 SQL 连接中就没问题。

修改以下问题编辑:

是的,应该没问题,但我强烈建议您这样做

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

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