我有以下查询,根据采购订单检查收货,以查看最初订购的商品以及通过收货预订的商品数量。例如,我下了10个香蕉奶昔的采购订单,然后我生成了一个收货,说明我在所述采购订单上收到了5个这样的奶昔。
SELECT t.PONUM, t.ITMNUM, t.ordered,
SUM(t.received) as received,
t.ordered - ISNULL(SUM(t.received),0) as remaining,
SUM(t.orderedcartons) as orderedcartons,
SUM(t.cartonsreceived) as cartonsreceived,
SUM(t.remainingcartons) as remainingcartonsFROM(SELECT pod.PONUM,
pod.ITMNUM, pod.QTY as ordered, ISNULL(grd.QTYRECEIVED, 0) as received,
pod.DELIVERYSIZE as orderedcartons,
ISNULL(grd.DELIVERYSIZERECEIVED, 0) as cartonsreceived,
(pod.DELIVERYSIZE - ISNULL(grd.DELIVERYSIZERECEIVED, 0)) as remainingcartons
FROM TBLPODETAILS pod
LEFT OUTER JOIN TBLGRDETAILS grd
ON pod.PONUM = grd.PONUM and pod.ITMNUM = grd.ITMNUM) t
GROUP BY t.ITMNUM, t.PONUM, t.ordered
ORDER BY t.PONUM
返回以下数据:
PONUM ITMNUM ordered received remaining orderedcartons cartonsreceived remainingcartons
1 1 5.0000 3.0000 2.0000 5.0000 3.0000 2.0000
接下来我有一个C#循环来根据我从上面的查询中获得的数据生成更新查询:
foreach (DataRow POUpdate in dt.Rows) {...
query += "UPDATE MYTABLE SET REMAININGITEMS=" + remainingQty.ToString()
+ ", REMAININGDELIVERYSIZE=" + remainingBoxes.ToString() + " WHERE ITMNUM="
+ itemNumber + " AND PONUM=" + poNumber + ";";
然后,我针对数据库执行每个更新查询。哪个在我的本地开发机器上工作正常。
但是,部署到生产服务器会在第一次查询时回收超过150,000条记录。
因此循环这么多行会锁定SQL和我的应用程序。是foreach吗?是原始选择将所有数据加载到内存中吗?都?我可以将此查询转换为单个查询并删除C#循环吗?如果是这样,最有效的方法是什么?
答案 0 :(得分:5)
在SQL中,目标应该是立即在整个表上编写操作。这样做SQL服务器非常有效,但是在任何交互上都需要很大的开销,因为它需要处理事务的一致性,原子性等等。所以在某种程度上,每个事务的固定成本很高,因为服务器可以做到这一点,但是事务中额外行的边际成本非常低 - 更新1m行的速度可能是更新10行的1/2。
这意味着foreach将导致SQL服务器不断地与您的应用程序来回,并且每次都会发生锁定/解锁和执行事务的固定成本。
您可以编写查询以在SQL中运行,而不是在C#中操作数据吗?您似乎希望根据select语句编写相对简单的更新(例如,请参阅SQL update from one Table to another based on a ID match。
尝试以下内容(未经过代码测试,因为我无法访问您的数据库结构等):
UPDATE MYTABLE
SET REMAININGITEMS = remainingQty,
REMAININGDELIVERYSIZE=remainingBoxes
From
(SELECT t.PONUM, t.ITMNUM, t.ordered,
SUM(t.received) as received,
t.ordered - ISNULL(SUM(t.received),0) as remaining,
SUM(t.orderedcartons) as orderedcartons,
SUM(t.cartonsreceived) as cartonsreceived,
SUM(t.remainingcartons) as remainingcartonsFROM(SELECT pod.PONUM,
pod.ITMNUM, pod.QTY as ordered, ISNULL(grd.QTYRECEIVED, 0) as received,
pod.DELIVERYSIZE as orderedcartons,
ISNULL(grd.DELIVERYSIZERECEIVED, 0) as cartonsreceived,
(pod.DELIVERYSIZE - ISNULL(grd.DELIVERYSIZERECEIVED, 0)) as remainingcartons
FROM TBLPODETAILS pod
LEFT OUTER JOIN TBLGRDETAILS grd
ON pod.PONUM = grd.PONUM and pod.ITMNUM = grd.ITMNUM) t
GROUP BY t.ITMNUM, t.PONUM, t.ordered
ORDER BY t.PONUM ) as x
join MYTABLE on MYTABLE.ITMNUM=x.itmnum AND MYTABLE.PONUM=i.ponum
答案 1 :(得分:3)
正如KM在评论中所说,这里的问题是回到客户端应用程序,然后在每行进行另一次数据库访问。这很慢,并且可能导致愚蠢的小错误,这可能导致虚假数据。
此外,在你正在做的时候将字符串连接到SQL通常被认为是一个非常糟糕的主意 - SQL注入(正如Joel Coehoorn写的那样)是一种真正的可能性。
怎么样:
create view OrderBalance
as
SELECT t.PONUM, t.ITMNUM, t.ordered,
SUM(t.received) as received,
t.ordered - ISNULL(SUM(t.received),0) as remaining,
SUM(t.orderedcartons) as orderedcartons,
SUM(t.cartonsreceived) as cartonsreceived,
SUM(t.remainingcartons) as remainingcartonsFROM(SELECT pod.PONUM,
pod.ITMNUM, pod.QTY as ordered, ISNULL(grd.QTYRECEIVED, 0) as received,
pod.DELIVERYSIZE as orderedcartons,
ISNULL(grd.DELIVERYSIZERECEIVED, 0) as cartonsreceived,
(pod.DELIVERYSIZE - ISNULL(grd.DELIVERYSIZERECEIVED, 0)) as remainingcartons
FROM TBLPODETAILS pod
LEFT OUTER JOIN TBLGRDETAILS grd
ON pod.PONUM = grd.PONUM and pod.ITMNUM = grd.ITMNUM) t
GROUP BY t.ITMNUM, t.PONUM, t.ordered
这似乎与您的“MYTABLE”具有的数据完全相同 - 也许您甚至不再需要MYTABLE,您可以使用该视图!
如果您在MYTABLE上有其他数据,则您的更新将变为:
UPDATE MYTABLE
SET REMAININGITEMS = ob.remainingitems,
REMAININGDELIVERYSIZE = ob.remainingBoxes
from MYTABLE mt
join OrderBalance ob
on mt.ITMNUM = ob.itemNumber
AND mt.PONUM = ob.poNumber
(尽管正如David Mannheim写的那样,最好不要使用视图并使用类似于他提出的解决方案)。
答案 2 :(得分:0)
其他答案向您展示了在RDBMS中完全执行整个更新的好方法。如果你能做到这一点,那就是完美的解决方案:由于额外的往返和数据传输问题,你不能用C#/ RDBMS组合来打败它。
但是,如果您的更新需要某些计算,由于某种原因或其他原因无法在RDBMS中执行,您应修改代码以构建单个参数化更新,以代替可能巨大的150000您当前正在构建的行更新。
using (var upd = conn.CreateCommand()) {
upd.CommandText = @"
UPDATE MYTABLE SET
REMAININGITEMS=@remainingQty
, REMAININGDELIVERYSIZE=@remainingBoxes
WHERE ITMNUM=@itemNumber AND PONUM=@poNumber";
var remainingQtyParam = upd.CreateParameter();
remainingQtyParam.ParameterName = "@remainingQty";
remainingQtyParam.DbType = DbType.Int64; // <<== Correct for your specific type
upd.Parameters.Add(remainingQtyParam);
var remainingBoxesParam = upd.CreateParameter();
remainingBoxesParam.ParameterName = "@remainingBoxes";
remainingBoxesParam.DbType = DbType.Int64; // <<== Correct for your specific type
upd.Parameters.Add(remainingBoxesParam);
...
foreach (DataRow POUpdate in dt.Rows) {
remainingQtyParam.Value = ...
remainingBoxesParam.Value = ...
upd.ExecuteNonQuery();
}
}
我们的想法是将150,000个看起来相同的更新整合到一个参数化更新中,这个更新实际上只是一个声明。