【发布时间】:2015-08-13 18:57:04
【问题描述】:
这是一个棘手的问题。我正在编写我的第一个需要动态 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
解释一下:
- 'Sequence' 显示方程式中步骤的顺序。
- 'UseVariable' 告诉我们这一步计算是否使用 将项目表值作为变量,或者是否使用常量值。
- 取决于“UseVariable”的值,“ItemVariable”或 'Constant' 将有一个值,另一个将为 null。
- 'ItemVariable' 是对项目列表的外键引用,然后链接到该项目的值。
- 'Operator' 存储一个 tinyint,其中从 1 到 4 的每个值代表一个 数字运算符(+、-、*、/)。
- '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 语句(但不执行它),然后在存储过程的上下文中执行它
标签: sql sql-server stored-procedures dynamic-sql