【问题标题】:String Expression to be evaluated to number要评估为数字的字符串表达式
【发布时间】:2012-03-24 10:16:46
【问题描述】:

我需要编写一个 TSQL 用户定义函数,该函数将接受一个字符串并返回一个数字。

我会调用像dbo.EvaluateExpression('10*4.5*0.5')这样的函数应该返回数字22.5

谁能帮我写这个函数EvaluateExpression

目前我正在使用需要避免的 CLR 函数。

编辑1

我知道这可以使用存储过程来完成,但我想在某些语句中调用此函数,例如:select 10* dbo.EvaluateExpression('10*4.5*0.5')

我还有大约 400,000 个这样的公式要评估。

编辑2

我知道我们可以使用函数内部的 osql.exe 来做到这一点,如 here 所述。但是由于权限设置,我也不能用这个。

【问题讨论】:

  • 您说您有一个有效的 CLR 函数,但“需要避免”它。如果您能解释原因会有所帮助; TSQL 根本不是一种很好的语言,这样做可能会比仅使用 CLR 遇到更多问题。
  • 我试图避免 CLR 获得性能和安全原因
  • 有哪些安全原因可以避免 CLR?性能是另一个问题,但如果您当前的解决方案太慢,您是否尝试过在更改为完全不同的语言之前对其进行优化?
  • 我的想法是在原生 sql 中有一些东西

标签: sql-server tsql evaluate


【解决方案1】:

我认为这在用户定义的函数中是不可能的。

您可以在存储过程中执行此操作,例如:

declare @calc varchar(max)
set @calc = '10*4.5*0.5'

declare @sql nvarchar(max)
declare @result float
set @sql = N'set @result = ' + @calc
exec sp_executesql @sql, N'@result float output', @result out
select @result

但动态 SQL,如 execsp_executesql,不允许在用户定义的函数中使用。

【讨论】:

  • 是的。我已经尝试过 procs,但我想在某些语句中调用此函数,例如:select 10* dbo.EvaluateExpression('10*4.5*0.5')
  • AFAIK,没有 CLR 支持就无法完成,对功能的限制是 quite extensive
  • @PraVn:您可能想详细说明您正在尝试做什么,特别是这些表达式的来源。也许您可以将表达式与一些键一起存储在临时表中,让存储过程评估表达式,然后在查询/查询中使用临时表(使用上述键连接),读取/使用您认为合适的结果.
  • 谢谢@Andriy。但它是一个计算引擎,有近 400,000 个这样的公式。从性能方面来看,将其存储在临时表中是不切实际的。
  • @Andomar:嗯,可以通过一个函数(见这里:sswug.org/DATABASES/default.aspx?id=22848)但是,当然,使用这些东西是一件坏事,除非这是一个“一次性” " 锻炼。
【解决方案2】:

免责声明:我是项目的所有者Eval SQL.NET

对于 SQL 2012+,您可以使用 Eval SQL.NET,它可以在 SAFE 权限下运行。

性能很好(比 UDF 更好)并且尊重运算符优先级和括号。事实上,几乎所有的 C# 语言都支持。

您还可以为公式指定参数。

-- SELECT 225.00
SELECT 10 * CAST(SQLNET::New('10*4.5*0.5').Eval() AS DECIMAL(18, 2))

-- SELECT 70
DECLARE @formula VARCHAR(50) = 'a+b*c'
SELECT  10 * SQLNET::New(@formula)
                    .Val('a', 1)
                    .Val('b', 2)
                    .Val('c', 3)
                    .EvalInt()

【讨论】:

    【解决方案3】:

    使用这个功能,绝对可以。

    CREATE FUNCTION dbo.EvaluateExpression(@list nvarchar(MAX))
    
    RETURNS Decimal(10,2)
    AS
    
    BEGIN
    Declare @Result Decimal(10,2)
    set @Result=1
     DECLARE @pos        int,
           @nextpos    int,
           @valuelen   int
    
    SELECT @pos = 0, @nextpos = 1
    
    
    WHILE @nextpos > 0
      BEGIN
         SELECT @nextpos = charindex('*', @list, @pos + 1)
         SELECT @valuelen = CASE WHEN @nextpos > 0
                                 THEN @nextpos
                                 ELSE len(@list) + 1
                            END - @pos - 1
    
                            Set @Result=@Result*convert(decimal(10,2),substring(@list, @pos + 1, @valuelen))
    
    
         SELECT @pos = @nextpos
      END
    
    RETURN @Result
    END
    

    你可以用这个

    Select 10* dbo.EvaluateExpression('10*4.5*0.5')
    

    【讨论】:

    • 它将适用于给定的示例。但它不会评估所有表达式。例如:'10+20' 会失败
    • 很难以这种方式尊重运算符优先级或括号,f.e. 10+20*(5+1)
    【解决方案4】:

    您可以使用下面的 SQL 存储过程来计算具有任意数量变量的任何公式的结果:

    我在 2012 年编写了一个解决方案,该解决方案可以使用 SQL SERVER 评估任何类型的数学公式。该解决方案可以处理具有 N 个变量的任何公式:

    我被要求找到一种方法来评估用户填写的公式给出的值。 公式包含数学运算(加法、乘法、除法和减法) 用于计算公式的参数存储在 SQL server DATA BASE 中。

    我自己找到的解决方法如下:

    假设我有n个参数用于计算公式,每个参数存储在一个数据表的一行中。

    • 包含要在公式中使用的 n 行的数据表称为 tab_value

    • 我必须使用 SQL 游标将 n 行(在 tab_values 中)中找到的 n 个值存储在一个新表的一行中,

    • 为此,我创建了一个名为 tab_formula 的新表

    • 在游标中,我将为每个值添加一个新列,列名将是Id1、Id2、Id3等。

    • 然后我构造一个包含公式的 SQL 脚本来计算公式

    这里是完整的脚本,希望对你有用,欢迎来问我。

    程序用作输入:

    -公式

    -包含用于计算公式的值的表格

    if exists(select 1 from sysobjects where name='usp_evaluate_formula' and xtype='p') 
    
    drop proc usp_evaluate_formula 
    
    go
    
    create type type_tab as table(id int identity(1,1),val decimal(10,2)) 
    
    go 
    create proc usp_evaluate_formula(@formula as nvarchar(100),@values as type_tab readonly) 
    
    as begin 
    
    declare @tab_values table (id int, val decimal(10,2))
    
    insert into @tab_values(id,val) select * from @values
    
    declare @id as int declare @val as decimal(10,2)
    if not exists(select 1 from sysobjects where name ='tab_formula') 
    create table tab_formula(id int identity(1,1), formula nvarchar(1000))
    
    if not exists(select 1 from tab_formula where formula=@formula) 
    insert into tab_formula(formula) values(@formula)
    
    
    declare c cursor for select id,val from @tab_values 
    
    declare @script as nvarchar(4000) 
    
    open c 
    
    fetch c into @id,@val 
    
    while @@fetch_status=0 
    
    begin 
    
    set @script = 'if not exists(select 1 from syscolumns c inner join sysobjects o on c.id=o.id where o.name=''tab_formula'' and c.name=''id'+
    convert(nvarchar(3),@id)+ ''') 
    alter table tab_formula add id'+convert(nvarchar(3),@id)+ ' decimal(10,2)' 
    
    print @script 
    
    exec(@script) 
    
    set @script='update tab_formula set id'+convert(nvarchar(3),@id)+'='+convert(nvarchar(10),@val)+' where formula='''+@formula+'''' print @script exec(@script) fetch c into @id,@val end close c deallocate c
    
    set @script='select *,convert(decimal(10,2),'+@formula+') "Result" from tab_formula where formula='''+@formula+'''' 
    
    print @script 
    
    exec(@script)
    
    end
    
    go
    
    declare @mytab as type_tab 
    
    insert into @mytab(val) values(1.56),(1.5) ,(2.5) ,(32),(1.7) ,(3.3) ,(3.9)
    
    exec usp_evaluate_formula'2*cos(id1)+cos(id2)+cos(id3)+3*cos(id4)+cos(id5)+cos(id6)+cos(id7)/2*cos(Id6)',@mytab
    
    go 
    drop proc usp_evaluate_formula 
    
    drop type type_tab 
    
    drop table tab_formula
    

    【讨论】:

      猜你喜欢
      • 2016-04-24
      • 1970-01-01
      • 2012-05-29
      • 1970-01-01
      • 2010-12-17
      • 1970-01-01
      相关资源
      最近更新 更多