多个参数化 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
我们按顺序读入一个小部件购买文件,该文件是随机排列的,并且包含一些混合的红色和蓝色购买。
例如:
- 红色小部件+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;
...其中肯定 是 随着您的进行而更新。只是想确保我正确理解这些参数化查询。根据我的阅读,如果使用参数,似乎肯定会有一些优化?我认为我不是 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 每次花更少的时间思考它。
我正在使用 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
我们按顺序读入一个小部件购买文件,该文件是随机排列的,并且包含一些混合的红色和蓝色购买。
例如:
- 红色小部件+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;
...其中肯定 是 随着您的进行而更新。只是想确保我正确理解这些参数化查询。根据我的阅读,如果使用参数,似乎肯定会有一些优化?我认为我不是 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 每次花更少的时间思考它。