Evaluating String Arithmetic Expressions in SQL Server 2005

I saw a wish list blogged at http://omnibuzz-sql.blogspot.com/2006/07/next-version-of-sql-server-wish-list.html. It wanted a sql function that would evaluate an string arithmetic expression in the next version of SQL Server. It turns out that function is already available, sort of, in T-SQL on SQL Server 2005. Here is an example:

 
DECLARE @x xml
DECLARE @v FLOAT
SET @x = ''
SET @v = @x.value('(1 + 4) div 3', 'FLOAT')
SELECT @v
 ----------------
1.666666

Unfortunately the following will not work...

DECLARE @x xml
SET @x = ''
DECLARE @e VARCHAR(MAX)
DECLARE @v FLOAT
SET @e = '(1 + 4) div 3'
SET @v = @x.value(@v, 'FLOAT')
SELECT @v

... because value and other XQuery functions require literal XQuery expressions. But all is not lost!

We can weave together a number of .NET technologies in SQL Server 2005 and make our own EvaluateArithmethicExpression scalar valued user defined function. All we have to do is add some basic XPath to an XPathNavigator and regular expressions. XPath itself has builtin support for evaluating arithmetic expressions, albeit some of the operations are names rather than single characters, but a regular expression can clean that up. At the end of this article is a C# function that implements a CLR based user defined function that evaluates string arithmetic expressions. Here is an example of using this function:

SELECT dbo.EvaluateArithmethicExpression('(50.1234 * 1 + 2)/3')

DECLARE @f FLOAT

DECLARE @e VARCHAR(MAX)

SET @e = '(50.1234 * 1 + 2)/3'

SET @f = dbo.EvaluateArithmethicExpression(@e)

SELECT @f

SET @e = '((50.1234 * 1 + 2)/3) % 5'

SET @f = dbo.EvaluateArithmethicExpression(@e)

SELECT @f

 

----------------------

17.3744666666667

(1 row(s) affected)

 

----------------------

17.3744666666667

(1 row(s) affected)

 

----------------------

2.37446666666667

(1 row(s) affected)

 

You can even access all the math functions in XPath itself...

DECLARE @f FLOAT

SET @f = dbo.EvaluateArithmethicExpression('ceiling((50.1234 * 1 + 2)/3) % 5')

SELECT @f

----------------------

3

 

Here is the source code for the EvaluateArithmeticExpression

using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Xml;
using System.Xml.XPath;
using System.IO;
using System.Text.RegularExpressions;
public class ArithmeticCalculations
{
 // dummy doc for XPath eval's
 static readonly XPathDocument xpdoc;
 static ArithmeticCalculations()
 {
  StringReader sr = new StringReader("");
  // doesn't matter whats in the doc
  // we just need something to call evaluate on
  xpdoc = new XPathDocument(sr);
  sr.Close();
 }
 [Microsoft.SqlServer.Server.SqlFunction]
 public static SqlDouble EvaluateArithmethicExpression(SqlString expression)
 {
  // give me a null and you will get on back
  if (expression.IsNull)
  {
   return SqlDouble.Null;
  }
  // use regex's to convert typical arithmetic ops to the ones XPath likes
  // also force the result to be a double
  String useExpression = "number("+Regex.Replace(expression.Value, "/", " div ")+")";
  useExpression = Regex.Replace(useExpression, "%", " mod ");
  XPathNavigator nav = xpdoc.CreateNavigator();
  SqlDouble retval = SqlDouble.Null;
  try
  {
   retval = (Double)nav.Evaluate(useExpression);
  }
  catch
  {
   // if its not a plain ol' arithmetic expression
   // you will get a null back
   retval = SqlDouble.Null;
  }
  return retval;
 }
};

Posted Jul 27 2006, 10:50 AM by dan-sullivan
Filed under: ,

Comments

Olie Markie wrote re: Evaluating String Arithmetic Expressions in SQL Server 2005
on 07-28-2006 12:28 AM
DECLARE @f1 FLOAT
CREATE TABLE #t(f float)
INSERT #t
EXEC('DECLARE @f2 FLOAT;SET @f2 = 1.0 + 2.0/3.0;SELECT @f2')
SELECT *
FROM #t
DROP TABLE #t
Omnibuzz wrote re: Evaluating String Arithmetic Expressions in SQL Server 2005
on 08-03-2006 9:34 PM
Nice article Dan. the XML way was good.. only one problem, value method requires a string literal..
And if I am going to use a string literal it defeats the purpose of using a string to evaluate... Correct me if I am wrong...
The CLR way is cool.. juz what I was looking for... Thanks for granting my wish.. may god bless you :)

-Dark Omni

Rainabba wrote re: Evaluating String Arithmetic Expressions - Exception
on 09-12-2006 7:15 PM
When I try to run your sample query, I get an exception: "The type initializer for 'TESTSCORING1.ArithmeticCalculations' threw an exception. ---> System.Xml.XmlException: Root element is missing." I don't see how any object would even require a Root element, I mean, there's not XMLDocument here I see and XPathDocument is ReadOnly.

What am I missing?
Dan wrote re: Evaluating String Arithmetic Expressions in SQL Server 2005
on 09-13-2006 3:55 AM
Not quite sure what happened but replace:

static readonly XPathDocument xpdoc;

with

static readonly XPathNavigator nav = new XmlDocument().CreateNavigator();

and remove the static constructor.

and remove the line that says:

XPathNavigator nav = xpdoc.CreateNavigator();
Rainabba wrote re: Evaluating String Arithmetic Expressions in SQL Server 2005
on 09-14-2006 11:54 AM
That did the trick. You've helped us make a major advancement in our database. This one issue is 40% of the reason we have upgraded to SQL 2005. Thank you.
Neil wrote re: Evaluating String Arithmetic Expressions in SQL Server 2005
on 11-08-2006 7:55 AM
Hello,

I am trying to bind the result of this function:

SELECT DISTINCT DATENAME(month, CONVERT(Datetime, WeekEnd, 105)) AS [Month Name]
FROM StaffHours

To a web control, drop down list.

When i load the page, i get 3 items in the DDL, which is correct. But the values in the ddl are "System.Data.DataRowView"

Does this mean i cant bind it directly? How would i go about this please?

Many thanks
Senthil wrote re: Evaluating String Arithmetic Expressions in SQL Server 2005
on 08-28-2007 9:24 PM
Hi,
When i try to create the procedure it throws this error
Msg 6567, Level 16, State 2, Procedure EvaluateArithmethicExpression, Line 1
CREATE PROCEDURE failed because a CLR Procedure may only be defined on CLR methods that return either SqlInt32, System.Int32, void.

this is my Procedure
CREATE PROCEDURE EvaluateArithmethicExpression
@input nvarchar(200)
AS EXTERNAL NAME
ArithmeticCalculations.ArithmeticCalculations.EvaluateArithmethicExpression
Maddy wrote re: Evaluating String Arithmetic Expressions in SQL Server 2005
on 10-17-2007 6:10 AM
I tried deploying the CLR function given above. Now when i try to execute the function from SQL server, It's not giving the proper results.
Like if I pass the parameter as 1+2+3, then it return me value of 1 i.e. always returns the 1st number.
Need Help wrote re: Evaluating String Arithmetic Expressions in SQL Server 2005
on 05-22-2008 2:35 PM
Where should I be putting the c# code? in SQL Server or in VS 2005

Add a Comment

(required)  
(optional)
(required)  
Remember Me?