如何在 SQL 服务器中 运行 字符串数学表达式(触发器或函数)
How to run string math expression in SQL Server (trigger or function)
我要运行:
select Mycalculatefunction('((3*4)-3)*5')
select ('((3*4)-3)*5')
输出:((3*4)-3)*5
错误(不是整数值)
我想要的输出是:45
我定义了一个存储过程:
create PROCEDURE dbo.Eval
(@exp varchar(MAX))
AS
SET NOCOUNT ON
DECLARE @SQLString NVARCHAR(MAX)
SET @SQLString = 'SELECT '+@exp
EXEC sp_executesql @SQLString
我称之为:
exec dbo.Eval '((3*4)-3)*5'
这个过程是触发器怎么办?
您的 SP 存在注入漏洞。 F.e。我通过了exec dbo.Eval '1;DROP TABLE some_table;'
。最好使用 xml.query:
CREATE PROCEDURE dbo.Eval
@formula nvarchar(max)
AS
DECLARE @sql nvarchar(max)
SELECT @sql = N'
DECLARE @x xml = ''''
SELECT CAST(@x.query('''+@formula+''') as nvarchar(max))'
EXEC sp_executesql @sql
然后
EXEC dbo.Eval '((3*4)-3)*5'
输出:
45
触发器部分(因为没有关于你的表的信息,只是一般性的解释,我添加了完整的批次和评论):
--Create table that will store Formulas
CREATE TABLE Formulas (
ID int IDENTITY(1,1) NOT NULL,
Formula nvarchar(max) NULL,
CONSTRAINT PK_ID PRIMARY KEY (ID)
)
GO
--Create table to store results of the formulas
CREATE TABLE Results (
T1_ID int NOT NULL,
Result int NULL
)
GO
--Linked by ID
ALTER TABLE Results ADD CONSTRAINT FK_Formulas_Results FOREIGN KEY (T1_ID)
REFERENCES Formulas (ID)
GO
--Create a Table Valued Parameter
CREATE TYPE FormulaResults AS TABLE (
ID int NOT NULL,
Formula nvarchar(max) NULL
)
GO
--Create a procedure to do the count
CREATE PROCEDURE dbo.GetResults
@TVP FormulaResults READONLY
AS
DECLARE @sql nvarchar(max)
SELECT @sql = N'DECLARE @x xml = '''' '
SELECT @sql = @sql + 'SELECT '+CAST(ID as nvarchar(max))+' as ID, CAST(@x.query('''+Formula+''') as nvarchar(max)) UNION ALL '
FROM @TVP
SELECT @sql = LEFT(@sql,LEN(@sql)-LEN('UNION ALL '))
EXEC sp_executesql @sql
GO
--Create a trigger that will count formula after insert and update
CREATE TRIGGER GetResultsTrigger
ON Formulas
AFTER INSERT, UPDATE
AS
DECLARE @FormulaTVP AS FormulaResults
DECLARE @Results TABLE(
T1_ID int NOT NULL,
Result int NULL
)
INSERT INTO @FormulaTVP
SELECT *
FROM inserted
INSERT INTO @Results
EXEC dbo.GetResults @FormulaTVP
MERGE Results r
USING @Results s
ON r.T1_ID = s.T1_ID
WHEN NOT MATCHED THEN
INSERT VALUES (s.T1_ID, s.Result)
WHEN MATCHED THEN
UPDATE SET Result = s.Result;
之后运行:
INSERT INTO [Formulas] VALUES
('1+3'),('2+2*8')
SELECT [ID],
[Formula]
FROM [Test].[dbo].[Formulas]
SELECT [T1_ID],
[Result]
FROM [Test].[dbo].[Results]
输出:
ID Formula
1 1+3
2 2+2*8
T1_ID Result
1 4
2 18
图片1
ALTER FUNCTION [dbo].[Calculate]
( @expression AS VARCHAR(MAX)
)
RETURNS xml
AS
BEGIN
-- routine body goes here, e.g.
-- SELECT 'Navicat for SQL Server'
DECLARE @result xml
declare @x xml=''
--I can not pass as a parameter
select @result=@x.query(('(4*3)*5-10'))
return @result;
END
我调用这个函数:
SELECT CAST(CAST(CAST(dbo.[Calculate]('How do I pass parameters') AS XML) AS VARCHAR(100)) AS DECIMAL(4,2))
输出:
50.00
select @result=@x.query('sql:variable("@expression")')
result: '2+2' :(((
我要运行:
select Mycalculatefunction('((3*4)-3)*5')
select ('((3*4)-3)*5')
输出:((3*4)-3)*5
错误(不是整数值)
我想要的输出是:45
我定义了一个存储过程:
create PROCEDURE dbo.Eval
(@exp varchar(MAX))
AS
SET NOCOUNT ON
DECLARE @SQLString NVARCHAR(MAX)
SET @SQLString = 'SELECT '+@exp
EXEC sp_executesql @SQLString
我称之为:
exec dbo.Eval '((3*4)-3)*5'
这个过程是触发器怎么办?
您的 SP 存在注入漏洞。 F.e。我通过了exec dbo.Eval '1;DROP TABLE some_table;'
。最好使用 xml.query:
CREATE PROCEDURE dbo.Eval
@formula nvarchar(max)
AS
DECLARE @sql nvarchar(max)
SELECT @sql = N'
DECLARE @x xml = ''''
SELECT CAST(@x.query('''+@formula+''') as nvarchar(max))'
EXEC sp_executesql @sql
然后
EXEC dbo.Eval '((3*4)-3)*5'
输出:
45
触发器部分(因为没有关于你的表的信息,只是一般性的解释,我添加了完整的批次和评论):
--Create table that will store Formulas
CREATE TABLE Formulas (
ID int IDENTITY(1,1) NOT NULL,
Formula nvarchar(max) NULL,
CONSTRAINT PK_ID PRIMARY KEY (ID)
)
GO
--Create table to store results of the formulas
CREATE TABLE Results (
T1_ID int NOT NULL,
Result int NULL
)
GO
--Linked by ID
ALTER TABLE Results ADD CONSTRAINT FK_Formulas_Results FOREIGN KEY (T1_ID)
REFERENCES Formulas (ID)
GO
--Create a Table Valued Parameter
CREATE TYPE FormulaResults AS TABLE (
ID int NOT NULL,
Formula nvarchar(max) NULL
)
GO
--Create a procedure to do the count
CREATE PROCEDURE dbo.GetResults
@TVP FormulaResults READONLY
AS
DECLARE @sql nvarchar(max)
SELECT @sql = N'DECLARE @x xml = '''' '
SELECT @sql = @sql + 'SELECT '+CAST(ID as nvarchar(max))+' as ID, CAST(@x.query('''+Formula+''') as nvarchar(max)) UNION ALL '
FROM @TVP
SELECT @sql = LEFT(@sql,LEN(@sql)-LEN('UNION ALL '))
EXEC sp_executesql @sql
GO
--Create a trigger that will count formula after insert and update
CREATE TRIGGER GetResultsTrigger
ON Formulas
AFTER INSERT, UPDATE
AS
DECLARE @FormulaTVP AS FormulaResults
DECLARE @Results TABLE(
T1_ID int NOT NULL,
Result int NULL
)
INSERT INTO @FormulaTVP
SELECT *
FROM inserted
INSERT INTO @Results
EXEC dbo.GetResults @FormulaTVP
MERGE Results r
USING @Results s
ON r.T1_ID = s.T1_ID
WHEN NOT MATCHED THEN
INSERT VALUES (s.T1_ID, s.Result)
WHEN MATCHED THEN
UPDATE SET Result = s.Result;
之后运行:
INSERT INTO [Formulas] VALUES
('1+3'),('2+2*8')
SELECT [ID],
[Formula]
FROM [Test].[dbo].[Formulas]
SELECT [T1_ID],
[Result]
FROM [Test].[dbo].[Results]
输出:
ID Formula
1 1+3
2 2+2*8
T1_ID Result
1 4
2 18
图片1
ALTER FUNCTION [dbo].[Calculate]
( @expression AS VARCHAR(MAX)
)
RETURNS xml
AS
BEGIN
-- routine body goes here, e.g.
-- SELECT 'Navicat for SQL Server'
DECLARE @result xml
declare @x xml=''
--I can not pass as a parameter
select @result=@x.query(('(4*3)*5-10'))
return @result;
END
我调用这个函数:
SELECT CAST(CAST(CAST(dbo.[Calculate]('How do I pass parameters') AS XML) AS VARCHAR(100)) AS DECIMAL(4,2))
输出:
50.00
select @result=@x.query('sql:variable("@expression")')
result: '2+2' :(((