簡體   English   中英

存儲過程中的動態SQL計算

[英]Dynamic SQL Calculations in Stored Procedure

這是一個棘手的問題。 我正在編寫第一個需要動態SQL的SQL Server存儲過程,但遇到了一些問題。

這里是這種情況:我試圖允許用戶在我的應用程序中建立方程式,然后根據其中存儲的數據在SQL Server中執行它們。

例:

x * (y + 10)

我有一系列項目,每個項目中每個星期的任何細分都可以輸入一個值。 該表的簡化版本如下所示:

Item | WeekEndingDate | Subdivision | Value
-------------------------------------------
1    | 13-Aug-15      | 4           | 100

之所以這樣,是因為與每周的每天相比,輸入值的頻率更高。

我還有一個細分表,該表細分了一周的每個部分。

簡化后看起來像這樣:

Subdivision | Name
----------------------------
1           | Monday Morning

還有第三張表(我認為不需要進入該表),其中包含用戶創建的每個方程式的不同步驟。

我想做的是讓用戶提供一個“ WeekEndingDate”,然后針對該周的每個“細分”在每個“值”上執行給定的用戶定義方程式。

這是我嘗試過的:

我將計算結果放入T-SQL UDF的形式,其中使用光標在方程式的步驟中循環並構建動態SQL字符串,然后可以對其執行結果並從函數中返回結果。

然后,我嘗試在如下查詢中使用它:

SELECT dbo.DoCalculations(@Item, @WeekEnding, Subdivision), Subdivision, etc...
FROM Subdivisions

問題是,正如我發現的那樣,我無法在UDF中執行動態SQL。 這使我無法在dbo.DoCalculations進行計算,並破壞了整個過程。

我還有其他方法可以得到理想的結果嗎?

編輯:

這是我正在做的更多數據和示例。

計算表如下所示:

ID | Sequence | UseVariable | ItemVariable | Constant | Operator | ParenLevel
------------------------------------------------------------------------------
1  | 1        | True        | 1            | NULL     | 1        | 0

解釋:

  • “順序”顯示方程式中步驟的順序。
  • “ UseVariable”告訴我們計算的這一步驟是使用項目表值作為變量還是使用常量值。
  • 根據“ UseVariable”的值,“ ItemVariable”或“ Constant”將具有一個值,另一個將為null。
  • “ ItemVariable”是對項目列表的外鍵引用,然后鏈接到該項目的值。
  • “運算符”存儲一個tinyint,其中從1到4的每個值代表一個數字運算符(+,-,*,/)。
  • 通過將“ ParenLevel”與等式中上一步的“ ParenLevel”進行比較,可將“ ParenLevel”括在“(”和“)”中。

這是我的計算功能:

FUNCTION [dbo].[DoCalculations]
(
    -- Add the parameters for the function here
    @ItemID nvarchar(MAX),
    @Subdivision nvarchar(MAX),
    @WeekEnding date
)
RETURNS decimal(18, 5)
AS
BEGIN
    --Return variable
    DECLARE @R decimal(18, 5)

    --Variables for cursor use
    DECLARE @UseVariable bit
    DECLARE @ItemVar nvarchar(MAX)
    DECLARE @Constant decimal(18, 5)
    DECLARE @Operator tinyint
    DECLARE @ParenLevel tinyint

    --Working variables
    DECLARE @CalcSQL varchar(MAX) = 'SELECT @R = (' --Note I'm leaving one open paren and that I am selecting the result into '@R'
    DECLARE @CurrentParenLevel tinyint
    DECLARE @StepTerm nvarchar(MAX)

    --Create the cursor to loop through the calculation steps
    DECLARE CalcCursor CURSOR FAST_FORWARD FOR
        SELECT UseVariable, ItemVariable, Constant, Operator, ParenLevel
        FROM CalculationSteps
        WHERE CalculationSteps.Item = @ItemID
        ORDER BY Sequence

    --Start looping
    OPEN CalcCursor
    FETCH NEXT FROM CalcCursor INTO @UseVariable, @ItemVar, @Constant, @Operator, @ParenLevel
    WHILE @@FETCH_STATUS = 0
    BEGIN
        --Check if wee need to add opening parens to the equation
        IF @ParenLevel > @CurrentParenLevel
        BEGIN
            WHILE (@CurrentParenLevel <> @ParenLevel)
            BEGIN
                SET @CalcSQL = @CalcSQL + '('
                SET @CurrentParenLevel = @CurrentParenLevel + 1
            END
        END

        --Check if this step is using a variable or a constant
        IF @UseVariable = 'True'
        BEGIN
            --If it's using a variable, create the sub-query string to get its value
            SET @StepTerm = '(SELECT ReportValue FROM Reports WHERE Slot = @Slot AND WeekEnding = @WeekEnding AND Stat = ' + @ItemVar + ')'
        END
        ELSE
        BEGIN
            --If its's using a constant, append its value
            SET @StepTerm = '(' + @Constant + ')'
        END

        --Add the step to the equation
        SET @CalcSQL = @CalcSQL + @StepTerm

        --Check if wee need to add closing parens to the equation
        IF @ParenLevel < @CurrentParenLevel
        BEGIN
            WHILE (@CurrentParenLevel <> @ParenLevel)
            BEGIN
                SET @CalcSQL = @CalcSQL + ')'
                SET @CurrentParenLevel = @CurrentParenLevel - 1
            END
        END

        --Add the operator between this step and the next, if any
        SET @CalcSQL = @CalcSQL + (CASE @Operator WHEN 0 THEN '' WHEN 1 THEN '+' WHEN 2 THEN '-' WHEN 3 THEN '*' WHEN 4 THEN '/' END)

        --Go to the next step
        FETCH NEXT FROM CalcCursor INTO @UseVariable, @ItemVar, @Constant, @Operator, @ParenLevel
    END
    CLOSE CalcCursor
    DEALLOCATE CalcCursor

    --Close any open parens in the equation
    WHILE (@CurrentParenLevel > 0)
    BEGIN
        SET @CalcSQL = @CalcSQL + ')'
        SET @CurrentParenLevel = @CurrentParenLevel - 1
    END

    --Close the original open paren to enclose the whole equation
    SET @CalcSQL = @CalcSQL + ')'

    --Execute the equation which should set the result to '@R'
    Exec @CalcSQL

    --Return '@R'
    RETURN @R

您實際上並不需要動態SQL來構建公式,我相信您只需執行它即可。 而且由於您不能真正在函數內使用EXEC或sp_executesql,因此該部分需要保持獨立。 這是一個粗糙的示例,但是您可以使用表值參數來完成此操作。 如果我們對結構有更多了解,也許對一些其他數據樣本了解更多,那么您也許可以完全避免使用Dynamic SQL。

CREATE TYPE CalcVariables as TABLE
(
VariableIndex int IDENTITY(1,1),
VariableName varchar(255),
VariableValue varchar(255)
)

CREATE FUNCTION dbo.Dyn_Calc
(
@CalcFormula varchar(255),
@CaclVars CalcVariables ReadOnly
)
RETURNS varchar(255)
BEGIN

DECLARE @Calculation varchar(255) = @CalcFormula
DECLARE @Index int
DECLARE @iName varchar(255)
DECLARE @iValue varchar(255) 
SET @Index = (SELECT MAX(VariableIndex) FROM @CaclVars)

WHILE @Index > 0
BEGIN
SET @iName = (SELECT VariableName FROM @CaclVars WHERE VariableIndex = @Index)
SET @iValue = (SELECT VariableValue FROM @CaclVars WHERE VariableIndex = @Index)

SET @Calculation = REPLACE(@Calculation,@iName,@iValue)

SET @Index = @Index -1
END

RETURN @Calculation
END

DECLARE @CalcFormula varchar(255),
@CaclVars CalcVariables,
@SQL nvarchar(3000)

SET @CalcFormula = '[x] * ([y] + 10)'
INSERT INTO @CaclVars
VALUES ('[x]','10'),
    ('[y]','5')


SET @SQL = 'SELECT ' + (SELECT dbo.Dyn_Calc (@CalcFormula, @CaclVars))
SELECT @SQL

EXEC(@SQL)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM