如何在SQL Server函数中实现类似JavaScript eval()的字符串表达式计算?
可行的变通方案
针对你的需求(SQL Server 2022 16.0.1000.6版本),以下两种方案可以实现字符串表达式计算并封装为可在查询中直接调用的函数:
方案一:CLR标量函数(推荐,支持完整四则运算及优先级)
SQL Server 2022支持CLR集成,通过CLR函数可以直接实现类似eval()的表达式计算能力,且能在SELECT语句中直接调用。
实现步骤:
- 启用CLR集成(若未启用):
sp_configure 'show advanced options', 1; RECONFIGURE; sp_configure 'clr enabled', 1; RECONFIGURE; -- 若处于严格安全模式,需执行以下语句 sp_configure 'clr strict security', 0; RECONFIGURE;
- 创建C#类库生成DLL:
编写简单的C#类实现表达式计算逻辑:
using System; using System.Data.SqlTypes; using Microsoft.SqlServer.Server; using System.Data; public class ExpressionEvaluator { [SqlFunction(DataAccess = DataAccessKind.None)] public static SqlDecimal CalculateString(SqlString expression) { if (expression.IsNull) return SqlDecimal.Null; using (DataTable dt = new DataTable()) { var result = dt.Compute(expression.Value, string.Empty); return new SqlDecimal(Convert.ToDecimal(result)); } } }
编译该类生成DLL文件。
- 在SQL Server中注册程序集并创建函数:
CREATE ASSEMBLY ExpressionEvaluatorAssembly FROM 'C:\你的DLL文件路径\ExpressionEvaluator.dll' WITH PERMISSION_SET = SAFE; CREATE FUNCTION dbo.CalculateString(@expression NVARCHAR(MAX)) RETURNS DECIMAL(18,1) AS EXTERNAL NAME ExpressionEvaluatorAssembly.ExpressionEvaluator.CalculateString;
- 测试使用:
-- 模拟你的Stocks表数据 CREATE TABLE Stocks (Name NVARCHAR(50), Supply NVARCHAR(50), Demand DECIMAL(18,1)); INSERT INTO Stocks VALUES ('Candy', '2 + 2 * 3', 2); -- 执行目标查询 SELECT Name, CAST(dbo.CalculateString(Supply) * Demand AS DECIMAL(18, 1)) AS CalculatedValue FROM Stocks;
执行后会返回Candy | 16.0,完全符合你的需求。
方案二:纯SQL递归CTE实现(仅支持简单四则运算)
若无法使用CLR,可通过纯SQL逻辑处理简单的加减乘除表达式(需手动处理运算符优先级),适合表达式规则固定的场景。
实现函数:
CREATE FUNCTION dbo.CalculateSimpleExpression(@expression NVARCHAR(MAX)) RETURNS DECIMAL(18,1) AS BEGIN DECLARE @result DECIMAL(18,1); -- 先处理乘除运算 WHILE CHARINDEX('*', @expression) > 0 OR CHARINDEX('/', @expression) > 0 BEGIN DECLARE @opIndex INT = CASE WHEN CHARINDEX('*', @expression) > 0 AND (CHARINDEX('/', @expression) = 0 OR CHARINDEX('*', @expression) < CHARINDEX('/', @expression)) THEN CHARINDEX('*', @expression) ELSE CHARINDEX('/', @expression) END; DECLARE @left NVARCHAR(MAX) = SUBSTRING(@expression, 1, @opIndex - 1); DECLARE @right NVARCHAR(MAX) = SUBSTRING(@expression, @opIndex + 1, LEN(@expression)); -- 提取左侧有效数字 WHILE PATINDEX('%[^0-9.]%', REVERSE(@left)) > 0 SET @left = LEFT(@left, LEN(@left) - 1); -- 提取右侧有效数字 WHILE PATINDEX('%[^0-9.]%', @right) > 0 SET @right = RIGHT(@right, LEN(@right) - 1); -- 计算并替换表达式 DECLARE @temp DECIMAL(18,1) = CASE WHEN SUBSTRING(@expression, @opIndex, 1) = '*' THEN CAST(@left AS DECIMAL(18,1)) * CAST(@right AS DECIMAL(18,1)) ELSE CAST(@left AS DECIMAL(18,1)) / CAST(@right AS DECIMAL(18,1)) END; SET @expression = REPLACE(@expression, @left + SUBSTRING(@expression, @opIndex, 1) + @right, CAST(@temp AS NVARCHAR(MAX))); END; -- 处理加减运算 WHILE CHARINDEX('+', @expression) > 0 OR CHARINDEX('-', @expression) > 0 BEGIN DECLARE @opIndex2 INT = CASE WHEN CHARINDEX('+', @expression) > 0 AND (CHARINDEX('-', @expression) = 0 OR CHARINDEX('+', @expression) < CHARINDEX('-', @expression)) THEN CHARINDEX('+', @expression) ELSE CHARINDEX('-', @expression) END; DECLARE @left2 NVARCHAR(MAX) = SUBSTRING(@expression, 1, @opIndex2 - 1); DECLARE @right2 NVARCHAR(MAX) = SUBSTRING(@expression, @opIndex2 + 1, LEN(@expression)); -- 提取左侧有效数字 WHILE PATINDEX('%[^0-9.]%', REVERSE(@left2)) > 0 SET @left2 = LEFT(@left2, LEN(@left2) - 1); -- 提取右侧有效数字 WHILE PATINDEX('%[^0-9.]%', @right2) > 0 SET @right2 = RIGHT(@right2, LEN(@right2) - 1); -- 计算并替换表达式 DECLARE @temp2 DECIMAL(18,1) = CASE WHEN SUBSTRING(@expression, @opIndex2, 1) = '+' THEN CAST(@left2 AS DECIMAL(18,1)) + CAST(@right2 AS DECIMAL(18,1)) ELSE CAST(@left2 AS DECIMAL(18,1)) - CAST(@right2 AS DECIMAL(18,1)) END; SET @expression = REPLACE(@expression, @left2 + SUBSTRING(@expression, @opIndex2, 1) + @right2, CAST(@temp2 AS NVARCHAR(MAX))); END; SET @result = CAST(@expression AS DECIMAL(18,1)); RETURN @result; END;
测试使用:
SELECT Name, CAST(dbo.CalculateSimpleExpression(Supply) * Demand AS DECIMAL(18, 1)) AS CalculatedValue FROM Stocks;
同样会返回Candy | 16.0的结果,但该方案仅支持基础四则运算,不支持括号、函数等复杂表达式。
内容的提问来源于stack exchange,提问作者Hosse Fernando
相关产品推荐
相关产品推荐

