我使用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
我们按顺序读取小部件购买的文件,该文件是随机排列的,并且包含一些混合的红色和蓝色购买。
例如:
查看下面的代码示例....
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设置为键等。我的坏例子只是出于问题逻辑的目的。一旦我理解了这一点,我就可以运用我有限的知识来改进查询!
答案 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端的查询优化。就数据库的访问而言,每次执行都会得到其中一个,优化只是意味着数据库每次花费更少的时间来考虑它。