Строковое выражение, которое будет оценено как число - PullRequest
7 голосов
/ 24 марта 2012

Мне нужно написать пользовательскую функцию TSQL, которая будет принимать строку и возвращать число.

Я назову функцию как dbo.EvaluateExpression('10*4.5*0.5'), должна вернуть номер 22.5

Может ли кто-нибудь помочь мне написать эту функцию EvaluateExpression.

В настоящее время я использую функцию CLR, которую мне нужно избегать.

Edit1

Я знаю, что это можно сделать с помощью хранимой процедуры, но я хочу вызвать эту функцию в некоторых выражениях, например: select 10* dbo.EvaluateExpression('10*4.5*0.5')

Также у меня есть около 400 000 подобных формул, которые нужно оценить.

Edit2

Я знаю, что мы можем сделать это, используя osql.exe внутри функции, как объяснено здесь . Но из-за настроек разрешений я тоже не могу это использовать.

Ответы [ 4 ]

6 голосов
/ 24 марта 2012

Я не думаю, что это возможно в пользовательской функции.

Вы можете сделать это в хранимой процедуре, например:

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, например, exec или sp_executesql, не допускается в пользовательских функциях.

3 голосов
/ 10 февраля 2016

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

Для SQL 2012+ вы можете использовать Eval SQL.NET, который может бытьработать с безопасным разрешением.

Производительность отличная (лучше, чем 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()
0 голосов
/ 31 августа 2016

Вы можете использовать хранимую процедуру SQL ниже для вычисления результата любой формулы с любым количеством переменных:

Я написал в 2012 году решение, которое может оценивать математические формулы любого типа, используя SQL SERVER.Решение может обрабатывать любую формулу с N переменными:

Меня попросили найти способ оценить значение, заданное формулой, которое заполняется пользователем.Формула содержит математические операции (сложение, умножение, деление и вычитание). Параметры, используемые для вычисления формулы, хранятся в БАЗЕ ДАННЫХ сервера SQL.

Решение, которое я нашел сам, было следующим:

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

  • Таблица данных, содержащая n строк для использования вформула называется tab_value

  • Мне нужно сохранить n значений, найденных в n строках (в tab_values) в одной строке в одной новой таблице, используя курсор SQL,

  • для этого я создаю новую таблицу с именем tab_formula

  • В курсоре я добавлю новый столбец для каждого значения, имя столбца будет Id1, Id2,Id3 и т. Д.

  • Затем я создаю сценарий SQL, содержащий формулу для оценки формулы

Здесь, после полного сценария, я надеюсь, вы найдетеэто полезно,вы можете спросить меня об этом.

Процедура использует в качестве входных данных:

-Формулу

-А таблица, содержащая значения, используемые для расчета формулы

если существует (выберите 1 из системных объектов, где name = 'usp_evaluate_formula' и xtype = 'p') drop proc usp_evaluate_formula go

создайте тип type_tab в виде таблицы (id int identity (1,1), val decimal (10,2)) go create proc usp_evaluate_formula (@formula как nvarchar (100), @ значения как type_tab только для чтения) как начало --KAMEL GAZZAH --kamelgazzah@gmail.com --05 / 09/2016

объявить таблицу @tab_values ​​(id int, val decimal (10,2))

удалить из @tab_values ​​вставить в @tab_values ​​(id, val) select * from @values ​​

объявить @id как int объявить @val как десятичное (10,2)

, если не существует (выберите 1 из системных объектов, где name = 'tab_formula') создать таблицу tab_formula (id int identity (1,1), формулаnvarchar (1000)), если не существует (выберите 1 из tab_formula, где формула = @ формула) вставить в tab_formula (значения формулы) (@formula)

объявить курсор c для выбора идентификатора, val из @tab_values ​​объявить @script как nvarchar (4000) открыть c извлечь c в @ id, @ val в то время как @@ fetch_status = 0begin set @script = 'если не существует (выберите 1 из системных столбцов c системными объектами внутреннего объединения o на c.id = o.id где o.name =' 'tab_formula' 'и c.name =' 'id' + convert (nvarchar)(3), @ id) + '' ') изменить таблицу tab_formula добавить id' + convert (nvarchar (3), @ id) + 'decimal (10,2)' print @script exec (@script) set @ script =«обновить таб_формулу set id» + convert (nvarchar (3), @ id) + '=' + convert (nvarchar (10), @ val) + ', где формула =' '' + @ формула + '' '' print @scriptexec (@script) извлекает c в @ id, @ val end close c освобождает c

set @ script = 'select *, конвертирует (десятичное (10,2),' + @ формула + ') "Result"из tab_formula, где формула = '' '+ @ формула +' '' 'печать @script exec (@script)

end

go

объявить @mytab как вставку type_tab в@mytab (val) значения (1.56) вставляют в @mytab (val) значения (15) вставляют в @mytab (val) значения (25) вставляютt в значения @mytab (val) (32) вставить в значения @mytab (val) (17) вставить в значения @mytab (val) (33) вставить в значения @mytab (val) (37,9)

exec usp_evaluate_formula'cos (id1) + cos (id2) + cos (id3) + cos (id4) + cos (id5) + cos (id6) + cos (id7) / cos (Id6) ', @ mytab

go drop proc usp_evaluate_formula drop type type_tab drop table tab_formula

0 голосов
/ 24 марта 2012

Используйте эту функцию, она будет абсолютно работать.

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')
...