首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> SQL Server 資料庫 >> 關於SqlServer

SQL 程式化統計多次考試加權成績:CTE + 集合式更新替代游標

     編輯:關於SqlServer

需求背景

  • 三表:`web_inputCj_cjb` (學生-課程)、`web_inputCj_xsksb` (課程-考試-比例)、`web_inputCj_xscjb` (學生-考試-成績)。
  • 依考試比例 (`cjbl`,0-100) 計算加權總分,寫回 `web_inputCj_cjb.cj`。

原始游標方案 (效能差、鎖久、難維護)

DECLARE c1 CURSOR FOR
SELECT zzcj.cj, zzcj.xh FROM ...; -- 複雜子查詢
OPEN c1;
FETCH NEXT FROM c1 INTO @cj, @xh;
WHILE @@FETCH_STATUS = 0
BEGIN
    UPDATE web_inputCj_cjb SET cj=@cj WHERE xh=@xh AND kcId=@kcId;
    FETCH NEXT FROM c1 INTO @cj, @xh;
END
CLOSE c1; DEALLOCATE c1;

優化方案:單一陳述式集合式更新 (CTE + JOIN)

;WITH ExamWeights AS (
    -- 1. 取得該課程所有考試及比例
    SELECT ksid, cjbl FROM web_inputCj_xsksb WHERE kcId = @kcid
),
StudentScores AS (
    -- 2. 學生各考試成績
    SELECT xscjb.xh, xscjb.ksid, ISNULL(xscjb.cj, 0) AS cj
    FROM web_inputCj_xscjb xscjb
    JOIN ExamWeights ew ON xscjb.ksid = ew.ksid
),
WeightedTotal AS (
    -- 3. 計算加權總分 (四捨五入)
    SELECT 
        ss.xh,
        ROUND(SUM(ss.cj * ew.cjbl * 0.01), 0) AS final_score
    FROM StudentScores ss
    JOIN ExamWeights ew ON ss.ksid = ew.ksid
    GROUP BY ss.xh
)
-- 4. 集合式更新目標表
UPDATE cjb
SET cjb.cj = wt.final_score
FROM web_inputCj_cjb cjb
JOIN WeightedTotal wt ON cjb.xh = wt.xh
WHERE cjb.kcId = @kcid;

關鍵優化點

維度游標方案集合式方案
執行計畫N 次單獨 UPDATE (N = 學生數)單一掃描 + Hash/Aggregate + 單次 UPDATE
鎖持有時間長 (逐列提交)短 (單一交易)
記錄檔產生大量小交易單一大交易 (可配合分批)
平行處理不可優化器可並行化聚合/聯結
可讀性/維護性低 (過程式)高 (宣告式)

大量資料分批更新模式 (避免長鎖/記錄檔暴增)

DECLARE @batchSize INT = 5000, @rows INT = 1;
WHILE @rows > 0
BEGIN
    ;WITH wt AS ( ...同上... )
    UPDATE TOP (@batchSize) cjb
    SET cjb.cj = wt.final_score
    FROM web_inputCj_cjb cjb
    JOIN wt ON cjb.xh = wt.xh
    WHERE cjb.kcId = @kcid AND cjb.cj <> wt.final_score; -- 僅更新有變化列
    SET @rows = @@ROWCOUNT;
    WAITFOR DELAY '00:00:02';
END

進階:MERGE 語法 (SQL 2008+,同步 INSERT/UPDATE/DELETE)

;WITH wt AS ( ...同上... )
MERGE web_inputCj_cjb AS target
USING wt AS source ON target.xh = source.xh AND target.kcId = @kcid
WHEN MATCHED AND target.cj <> source.final_score
    THEN UPDATE SET cj = source.final_score
WHEN NOT MATCHED BY TARGET
    THEN INSERT (kcId, xh, cj) VALUES (@kcid, xh, final_score);
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved