SQL 服务器:如何循环 table 将多记录更新应用到另一个?
SQL Server : how to loop through a table applying multi-record updates to another?
我希望根据包含更新详细信息的 table Y 来更新 table X 中的记录。困难在于
- Y 的一行表示对 X 中指定数量的记录的更新。
- Y 中可能有多个记录指定对 X 中记录的不同更新,这些记录在它们匹配的字段中是相似的;在这种情况下,更新应应用于 X 的不相交子集。
假设 X = materials(id, type_id, status, data)
; Y = material_updates(run_id, type_id, quantity, data)
(id
只是一个内部主键域)
然后我想做的是(相当于)循环一个简单的查询,比如
SELECT *
FROM material_updates
WHERE run_id = :run;
并且对于结果集中的每个 row
,应用类似
的东西
UPDATE TOP(row.quantity) materials
SET data = row.data, status = 1
WHERE status = 0 AND type_id = row.type_id;
(对status
的更改恰好在我试图解决的问题中是不变的)
示例数据
materials_update table:
run_id type_id quantity data
1 1 3 42
1 2 2 69
1 2 1 105
材料table更新前:
type_id status data
1 1 17
1 1 17
1 0 0
1 0 0
1 0 0
1 0 0
2 0 0
2 0 0
2 0 0
2 0 0
材料table更新后:
type_id status data
1 1 17
1 1 17
1 1 42
1 1 42
1 1 42
1 0 0
2 1 69
2 1 69
2 1 105
2 0 0
我认为可以使用游标来完成,但这是最好的解决方案,还是有更有效的方法?
这非常适合 CURSOR (msdn link),它允许您逐行遍历查询结果并为每个结果执行操作。
This one here 是一个很好的教程。
您的需求将通过这段代码解决:
-- the best fit for this code would be a Stored Procedure with one parameter
-- which is the run_id value you want.
-- error checking omitted for brevity
DECLARE CURSOR theCursor
FOR SELECT type_id, quantity, data FROM material_updates WHERE run_id = @run_id;
DECLARE @type_id int; -- types should match your material_updates fields
DECLARE @quantity int;
DECLARE @data int;
OPEN theCursor;
FETCH NEXT FROM theCursor INTO @type_id, @quantity, @data;
WHILE @@FETCH_STATUS = 0
BEGIN
UPDATE TOP(@quantity) materials
SET data = @data, status = 1
WHERE status = 0 AND type_id = @type_id;
END;
CLOSE theCursor;
DEALLOCATE theCursor;
另一个解决方案是使用 UPDATE FROM (SO already has info about it) 但我不知道有什么方法可以让它更新特定数量的行。 它很可能不能这样做。
请注意,您最终得到的数据毫无意义,因为没有顺序:您永远不会知道哪些行会be/have 已更新。
我希望根据包含更新详细信息的 table Y 来更新 table X 中的记录。困难在于
- Y 的一行表示对 X 中指定数量的记录的更新。
- Y 中可能有多个记录指定对 X 中记录的不同更新,这些记录在它们匹配的字段中是相似的;在这种情况下,更新应应用于 X 的不相交子集。
假设 X = materials(id, type_id, status, data)
; Y = material_updates(run_id, type_id, quantity, data)
(id
只是一个内部主键域)
然后我想做的是(相当于)循环一个简单的查询,比如
SELECT *
FROM material_updates
WHERE run_id = :run;
并且对于结果集中的每个 row
,应用类似
UPDATE TOP(row.quantity) materials
SET data = row.data, status = 1
WHERE status = 0 AND type_id = row.type_id;
(对status
的更改恰好在我试图解决的问题中是不变的)
示例数据
materials_update table:
run_id type_id quantity data
1 1 3 42
1 2 2 69
1 2 1 105
材料table更新前:
type_id status data
1 1 17
1 1 17
1 0 0
1 0 0
1 0 0
1 0 0
2 0 0
2 0 0
2 0 0
2 0 0
材料table更新后:
type_id status data
1 1 17
1 1 17
1 1 42
1 1 42
1 1 42
1 0 0
2 1 69
2 1 69
2 1 105
2 0 0
我认为可以使用游标来完成,但这是最好的解决方案,还是有更有效的方法?
这非常适合 CURSOR (msdn link),它允许您逐行遍历查询结果并为每个结果执行操作。
This one here 是一个很好的教程。
您的需求将通过这段代码解决:
-- the best fit for this code would be a Stored Procedure with one parameter
-- which is the run_id value you want.
-- error checking omitted for brevity
DECLARE CURSOR theCursor
FOR SELECT type_id, quantity, data FROM material_updates WHERE run_id = @run_id;
DECLARE @type_id int; -- types should match your material_updates fields
DECLARE @quantity int;
DECLARE @data int;
OPEN theCursor;
FETCH NEXT FROM theCursor INTO @type_id, @quantity, @data;
WHILE @@FETCH_STATUS = 0
BEGIN
UPDATE TOP(@quantity) materials
SET data = @data, status = 1
WHERE status = 0 AND type_id = @type_id;
END;
CLOSE theCursor;
DEALLOCATE theCursor;
另一个解决方案是使用 UPDATE FROM (SO already has info about it) 但我不知道有什么方法可以让它更新特定数量的行。 它很可能不能这样做。
请注意,您最终得到的数据毫无意义,因为没有顺序:您永远不会知道哪些行会be/have 已更新。