You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

存储过程执行过慢求优化:从VARCHAR类型Marks列派生计算列

绝对不用等!用批量更新替代逐行循环,效率提升N倍

你的问题根源在于逐行循环+多次单条更新的方式——34万条数据要执行34万次查询+3*34万次更新,这会产生巨量的磁盘IO和事务日志开销,速度慢是必然的。SQL是为集合操作设计的,完全可以用单条语句一次性完成所有计算和更新,速度会快到离谱。

最优方案:单条批量UPDATE语句

直接对整个表执行一次更新,所有计算逻辑在SET子句中完成,不需要任何循环或变量:

UPDATE [dbo].[Drv Tevta 3 Full]
SET 
    [Marked Obtained] = CAST(SUBSTRING([Marks], 0, CHARINDEX('/', [Marks])) AS INT),
    [Total Marks] = CAST(SUBSTRING([Marks], CHARINDEX('/', [Marks]) + 1, 10) AS INT),
    [Average] = (CAST(SUBSTRING([Marks], 0, CHARINDEX('/', [Marks])) AS INT) * 100) 
                / CAST(SUBSTRING([Marks], CHARINDEX('/', [Marks]) + 1, 10) AS INT)

为什么这更快?

  • 只需要扫描表一次,而不是34万次
  • 所有更新操作批量执行,大幅减少磁盘IO和事务日志生成
  • 避免了循环变量的维护、逐行查询的额外开销

健壮性优化:处理异常数据

如果你的Marks列存在格式不规范的记录(比如没有/、空值、非数字内容),上面的语句会报错。可以用TRY_CAST和条件过滤来避免问题:

UPDATE [dbo].[Drv Tevta 3 Full]
SET 
    [Marked Obtained] = TRY_CAST(SUBSTRING([Marks], 0, CHARINDEX('/', [Marks])) AS INT),
    [Total Marks] = TRY_CAST(SUBSTRING([Marks], CHARINDEX('/', [Marks]) + 1, 10) AS INT),
    [Average] = CASE 
                    -- 避免除以0的情况
                    WHEN TRY_CAST(SUBSTRING([Marks], CHARINDEX('/', [Marks]) + 1, 10) AS INT) <> 0
                    THEN (TRY_CAST(SUBSTRING([Marks], 0, CHARINDEX('/', [Marks])) AS INT) * 100) 
                         / TRY_CAST(SUBSTRING([Marks], CHARINDEX('/', [Marks]) + 1, 10) AS INT)
                    ELSE NULL 
                END
-- 只处理包含/的有效记录
WHERE CHARINDEX('/', [Marks]) > 0

超大表备选:分批更新

如果表实在太大(比如超过百万级),担心一次性更新会占满事务日志或锁表,可以分批处理,每次更新1万条:

DECLARE @BatchSize INT = 10000;
DECLARE @UpdatedRows INT = 1;

WHILE @UpdatedRows > 0
BEGIN
    UPDATE TOP(@BatchSize) [dbo].[Drv Tevta 3 Full]
    SET 
        [Marked Obtained] = CAST(SUBSTRING([Marks], 0, CHARINDEX('/', [Marks])) AS INT),
        [Total Marks] = CAST(SUBSTRING([Marks], CHARINDEX('/', [Marks]) + 1, 10) AS INT),
        [Average] = (CAST(SUBSTRING([Marks], 0, CHARINDEX('/', [Marks])) AS INT) * 100) 
                    / CAST(SUBSTRING([Marks], CHARINDEX('/', [Marks]) + 1, 10) AS INT)
    -- 只更新未处理过的记录(假设新增列初始为NULL)
    WHERE [Marked Obtained] IS NULL;

    SET @UpdatedRows = @@ROWCOUNT;
END

总结

绝对不要继续用原有的循环方式,这是对SQL集合操作能力的浪费。用上面的批量更新方案,34万条数据应该在几秒到几十秒内就能完成,而不是等几个小时甚至更久。

内容的提问来源于stack exchange,提问作者MakesReal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:01:27