【问题标题】:Arithmetic/Logical operation's based on Column value基于列值的算术/逻辑运算
【发布时间】:2016-05-21 22:03:15
【问题描述】:

我想根据我的列值进行算术运算。考虑下面的例子

CREATE TABLE #test
  (
     cont_sal    INT,
     check_value INT,
     operator    VARCHAR(50)
  )

INSERT #test
VALUES (10,20,'+'),
       (20,10,'+'),
       (10,20,'-'),
       (20,10,'-') 

预期结果:

cont_sal    check_value result
--------    ----------- ------
10          20          30
20          10          30
10          20          -10
20          10          10

我可以使用CASE 语句来做到这一点。

SELECT cont_sal,
       check_value,
       CASE
         WHEN operator = '+' THEN cont_sal + check_value
         when operator = '-' THEN cont_sal - check_value
       END result
FROM   #test 

但是有没有办法动态地做到这一点。运算符可以是 /,%,*。像这样的

DECLARE @sql NVARCHAR(max)=''

SET @sql = 'select cont_sal ' + 'operator'
           + ' check_value from #test '

--PRINT @sql

EXEC Sp_executesql
  @sql 

这显然没有用说

消息 102,级别 15,状态 1,第 1 行附近的语法不正确 'check_value'。

【问题讨论】:

  • 使用CASE 表达式。这将是最简单的推理。
  • 你的意思是SET @sql = 'select cont_sal ' + @op + ' check_value from #test '..
  • @Mini - 每行都有自己的运算符,所以我们不能使用变量

标签: sql sql-server tsql sql-server-2012


【解决方案1】:

很好的问题。

我会使用 case 表达式,因为:

  1. 只有five arithmetic operators
  2. 您不能直接对算术运算符进行参数化。
  3. 您不能内联执行动态 SQL。

但还有其他选择。您可以构建并执行动态 SQL 语句。

-- Query will be stored here.
DECLARE @Qry VARCHAR(255) = '';

-- Build up the query.
SELECT
    @Qry = 
        @Qry 
        +   CASE ROW_NUMBER() OVER (ORDER BY cont_sal)
                WHEN 1 THEN 'SELECT '
                ELSE 'UNION ALL SELECT '
            END 
        + ''''
        + Expression 
        + ''''
        + ' AS Expression,'
        + Expression
        + ' AS Result '
FROM
    #test AS t
        CROSS APPLY
            (
                -- Avoid typing expression twice.
                SELECT
                    CAST(cont_sal AS VARCHAR(50)) 
                        + ' '
                        + operator 
                        + ' '
                        + CAST(check_value AS VARCHAR(50)) AS Expression        
            ) AS ex
;

-- Execute it.
EXECUTE(@Qry);

或者您可以使用交叉应用来使用蛮力计算结果。

SELECT
    *       
FROM
    #test AS t

        CROSS APPLY
            (
                VALUES
                    ('+', cont_sal + check_value),
                    ('-', cont_sal - check_value),
                    ('*', cont_sal * check_value),
                    ('/', cont_sal / NULLIF(check_value, 0)),
                    ('%', cont_sal % NULLIF(check_value, 0))
            ) AS ex(operator, result)
WHERE
    ex.operator = t.operator
;

在这里计算所有可能的操作。那些不需要的会从结果集中过滤掉。这种方法更易于读写,但运行的计算从不需要。也就是说,它确实生成了比我的动态示例更快的查询计划。

编辑

感谢@Damien_The_Unbeliever,他指出了我除以零错误的脆弱性。我已经使用NULLIF 将 0 替换为空值,从而避免了错误。

我只更新了第二个例子。

【讨论】:

  • 最后一个示例 - 如果check_value 为 0,则此处存在被零除错误的风险,即使对于 operator 未除的行也是如此,因为系统可能会决定运行所有计算。
  • 我知道我不会逃脱惩罚!好点,我会编辑。
  • 使用Cross Apply 方法:)
【解决方案2】:

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

这个库允许在 T-SQL 中直接使用 C# 语法来动态计算算术表达式。运算符优先级和括号得到尊重,库超越了简单的数学表达式。

CREATE TABLE #test
    (
      cont_sal INT ,
      check_value INT ,
      operator VARCHAR(50)
    )

INSERT  #test
VALUES  ( 10, 20, '+' ),
        ( 20, 10, '+' ),
        ( 10, 20, '-' ),
        ( 20, 10, '-' )

DECLARE @sqlnet SQLNET = SQLNET::New('')

SELECT  cont_sal ,
        check_value ,
        @sqlnet.Code('x ' + operator + ' y')
           .Val('x', cont_sal)
           .Val('y', check_value)
           .Eval()
FROM    #test

DROP TABLE #test

文档:SQL Server Eval - Dynamically evaluate arithmetic operation and expression

【讨论】:

  • 谢谢。会去看看
猜你喜欢
  • 2019-09-20
  • 1970-01-01
  • 2021-12-29
  • 2011-04-11
  • 2017-06-30
  • 2019-10-29
  • 1970-01-01
  • 2010-09-05
  • 1970-01-01
相关资源
最近更新 更多