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

SQL Server 純量函數兩大實戰模式:UPDATE 內嵌呼叫與彙總運算重構

     編輯:關於SqlServer

前言

在 T-SQL 開發中,純量函數 常被誤解為效能殺手。但若善用其「封裝邏輯、支援內嵌於查詢」的特性,能優雅解決兩類棘手問題:UPDATE 中需引用複雜計算邏輯,以及 彙總查詢中需逐列計算後再加總。本文透過兩個實戰案例深度解析。

模式一:UPDATE 陳述式中透過函數實現跨表計算

場景

需根據 TABLETASKPHASE 的工作進度百分比,計算 TABLEFMECA 中的關鍵度數值 Cvalue_M,並彙總更新至 TABLERELATION.CRITICALITY。因 UPDATE 不支援直接呼叫預存程序,傳統做法使用游標,效能差且程式碼冗長。

解決方案:純量函數 + UPDATE ... FROM

1. 定義計算函數

CREATE FUNCTION [dbo].[FUN_GETTIME] (@TASKPHASEID INT)
RETURNS FLOAT
AS
BEGIN
    DECLARE @TASKID INT, @HOUR FLOAT, @PERCENT FLOAT, @RETURN FLOAT;
    
    IF @TASKPHASEID IS NULL RETURN 0.0;
    
    SELECT @TASKID = TASKID, @PERCENT = ISNULL(WORKPERCENT, 0) / 100.0
    FROM TABLETASKPHASE WHERE ID = @TASKPHASEID;
    
    SELECT @HOUR = ISNULL(TASKTIME, 0) FROM TABLETASK WHERE ID = @TASKID;
    
    SET @RETURN = @HOUR * @PERCENT;
    RETURN @RETURN;
END

2. 呼叫函數的 UPDATE 預存程序

CREATE PROCEDURE [dbo].[PROC_CALCCA] @ROID INT
AS
BEGIN
    DECLARE @CA FLOAT;
    
    UPDATE TABLEFMECA
    SET Cvalue_M = ISNULL(MODERATE, 0) * ISNULL(FMERATE, 0) 
                   * ISNULL(B.BASFAILURERATE, 0) * [dbo].[FUN_GETTIME](C.ID)
    FROM TABLEFMECA
         JOIN TABLERELATION B ON B.ID = @ROID
         JOIN TABLETASKPHASE C ON C.ID = TABLEFMECA.TASKPHASEID
    WHERE TABLEFMECA.ROID = @ROID;
    
    SELECT @CA = SUM(ISNULL(Cvalue_M, 0)) FROM TABLEFMECA WHERE ROID = @ROID;
    
    UPDATE TABLERELATION SET CRITICALITY = @CA WHERE ID = @ROID;
END

優勢分析

  • 邏輯封裝於函數,預存程序碼簡潔易維護
  • 集合式操作,避免游標逐列處理的效能損耗
  • 函數可被其他查詢重用,符合 DRY 原則

模式二:彙總查詢中使用函數表達式實現逐列計算後加總

場景

需根據零件、環境、資料來源、品質等級、數量等多維參數查詢失效率,再乘以工作百分比加總。傳統做法需游標逐列計算並累加中間值,但 SQL Server 2000+ 支援 SUM(函數運算式) 語法,可直接在彙總中完成。

核心語法

SUM ( [ ALL | DISTINCT ] expression )
-- expression 可為:常數、欄位、函數、或算術/位元/字串運算式的任意組合

實作範例

1. 多參數失效率計算函數

CREATE FUNCTION [dbo].[FUN_RATE] (
    @PARTID INT, @ENID INT, @SOURCEID INT, @QUALITYID INT, @COUNT INT
)
RETURNS FLOAT
AS
BEGIN
    DECLARE @QXS FLOAT, @G FLOAT, @RATE FLOAT;
    
    IF @ENID IS NULL OR @PARTID IS NULL OR @SOURCEID IS NULL OR @QUALITYID IS NULL
        RETURN 0.0;
    
    SELECT @QXS = ISNULL(XS, 0) FROM TABLEQUALITY WHERE ID = @QUALITYID;
    
    SELECT @G = ISNULL(FRATE_G, 0)
    FROM TABLEFAILURERATE
    WHERE SUBKINDID = @PARTID
      AND ENID = @ENID
      AND DATASOURCEID = @SOURCEID
      AND ( (ISNULL(MINCOUNT, 0) <= ISNULL(@COUNT, 0) AND ISNULL(MAXCOUNT, 0) >= ISNULL(@COUNT, 0))
         OR (ISNULL(@COUNT, 0) > ISNULL(MAXCOUNT, 0)) );
    
    SET @RATE = ISNULL(@QXS * @G, 0);
    RETURN @RATE;
END

2. 彙總呼叫預存程序

CREATE PROC PROC_FAULTRATE
    @PARTID INTEGER, @QUALITYID INTEGER, @SOURCEID INTEGER,
    @COUNT INTEGER, @ROID INT, @GRADE INT, @RATE FLOAT = 0 OUTPUT
AS
BEGIN
    DECLARE @TASKID INT;
    SET @RATE = 0.0;
    
    SELECT @TASKID = ISNULL(TASKPROID, -1)
    FROM TABLERELATION
    WHERE ID = (SELECT PID FROM TABLERELATION WHERE ID = @ROID);
    
    IF @TASKID = -1 OR @GRADE = 1 BEGIN
        SET @RATE = 0; RETURN;
    END
    
    SELECT @RATE = SUM( [dbo].[FUN_RATE](@PARTID, ENID, @SOURCEID, @QUALITYID, @COUNT) 
                        * ISNULL(WORKPERCENT, 0) / 100.0 )
    FROM TABLETASKPHASE
    WHERE TASKID = @TASKID;
END

關鍵優勢

  • 集合化彙總:單一 SUM() 完成「逐列呼叫函數 → 乘權重 → 加總」全流程
  • 無中間表/變數:避免暫存表寫入與游標迴圈開銷
  • 優化器友善:查詢優化器可嘗試內嵌函數邏輯(視複雜度而定)

效能與最佳實務提醒

  1. 避免多語句函數:本文範例為單一 RETURN 的純量函數,較易被優化器內嵌;多語句表值函數或複雜純量函數可能導致逐列執行(RBAR)
  2. 考慮 CROSS APPLY 替代:若函數邏輯極複雜,改用 CROSS APPLY 配合內嵌表值函數 (iTVF) 通常效能更優
  3. SCHEMABINDING:為函數加上 WITH SCHEMABINDING 可提升優化器決策品質
  4. 監控執行計畫:關注 Compute Scalar 操作符成本,必要時改寫為內聯運算式

總結

純量函數在 UPDATE 內嵌計算 與 彙總運算式重構 兩大場景中,能將原本需要游標或中間表的程序式邏輯,轉化為宣告式、集合導向的 SQL 陳述式。關鍵在於:函數邏輯單一、確定性、無副作用,並搭配適當的索引與統計資料,讓優化器發揮最大效能。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved