【发布时间】:2015-07-14 17:53:40
【问题描述】:
我正在使用 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
我们依次读入一个小部件购买文件,该文件是随机顺序的,包含许多混合的红色和蓝色购买。
例如:
- 红色小部件 +6
- 红色小部件 +2
- 蓝色小部件 +1
- 红色小部件 +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;
...随着您的进行,它肯定会更新。只是想确保我正确理解这些参数化查询。根据我的阅读,如果您使用参数,肯定会有一些优化?我认为我不清楚/正确的地方是我的印象是使用参数时的“优化”并不意味着更少的数据库访问? 对数据库的调用减少 = 运行总数不同步,这是我脑海中的一个问题!
显然,表和文件比这更复杂,我们将 ID 设置为键等。我的坏例子只是为了问题的逻辑。一旦我理解了这一点,我就可以运用我有限的知识来改进查询!
【问题讨论】:
-
那么你想要什么,你正在迭代的数据集以反映 UPDATE 命令的结果?如果是这样,您将需要重新运行 SELECT 查询(如您在示例 2 中所写)。但是,除非您有一些奇怪的逻辑需要在客户端执行,否则尝试在一个大型 SQL 语句中完成所有这些操作会更有效率。数据库不喜欢在循环(或游标)中运行大量更新。
-
关键的
{ fill params here and execute the commands }位于for I := 1 to whatever to循环之外是否正确? IOW 您一次又一次地使用完全相同的参数执行完全相同的查询。你不应该改变循环内的参数吗? -
@Matt Allwood 我以为我已经通过参数化更新来解决这个问题。问题是第二次更新取决于我们在循环中的更新结果,因此我不确定参数化在这种情况下是否有效?
-
@Arioch '对不起,填充参数在循环内,只是循环外的评论。已编辑。
-
为了澄清您的简化示例,您是否基本上得到了包含数量变化列表的表 A 和包含需要更新的数量列表的表 B?如果是这样,这可以作为单个 DB 调用中的集合操作而不是循环来完成。如果是这种情况,那么我们可以说明如何做到这一点。或者,您不能编写查询 2 来考虑存储在那里的当前数量而不需要 SELECT 吗?
标签: sql-server delphi parameters sqlite transactions