[英]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
解釋:
這是我的計算功能:
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.