2016-02-11 6 views
11

Я хочу выполнить арифметическую операцию, основанную на моих значениях столбца. Рассмотрим следующий примерАрифметика/Логическая операция на основе значения столбца

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 

который, очевидно, не работает говоря

Msg 102, Level 15, State 1, Line 1 Неправильный синтаксис около 'check_value'.

+4

Используйте 'выражение CASE'. Это будет самое простое рассуждать. –

+3

Вы имеете в виду 'SET @sql = 'select cont_sal' + @op + 'check_value from #test'' .. – Deepshikha

+2

@Mini - Каждая строка будет иметь собственный оператор, поэтому мы не сможем использовать переменную –

ответ

5

Большой вопрос.

Я хотел бы использовать выражение случай, потому что:

  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 
; 

Здесь вычисляется всякая возможная операция. Те, которые не требуются, отфильтровываются из набора результатов. Этот подход легче читать и писать, но выполняет вычисления, которые никогда не требуются. Тем не менее, это привело к тому, что мой динамический пример создал более быстрый план запросов.

EDIT

Благодаря @Damien_The_Unbeliever, который отметил свою уязвимость разделить на ноль ошибок. Я использовал NULLIF для замены 0s для нулей, что позволяет избежать ошибки.

Я только обновил второй пример.

+2

. Последний пример - существует риск здесь деление на нулевую ошибку, если 'check_value' равно 0, даже для строк, где' operator' не делит, так как система может решить выполнить все вычисления. –

+0

Я знал, что мне не избежать этого! Хорошо, я отредактирую. –

+0

Используется метод 'Cross Apply' :) –

1

Отказ от ответственности: Я владелец проекта Eval SQL.NET

Эта библиотека позволяет использовать синтаксис C# непосредственно в T-SQL для оценки динамически арифметическое выражение. Приоритет и скобки оператора соблюдаются, и библиотека выходит за рамки простого математического выражения.

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

+0

Спасибо. Посмотрите это –