解析表达式的算法!

犀山居士 2008-02-27 03:14:57
有如下项目,其中有些是指定数据来源(从其它表中查询数据,如[销售收入]的数据来源:从[销售表]中查询sum([金额])条件[项目]为'销售产品a1'或'销售产品a2'或'销售产品a3'),而有些是以下多个项的混合四则运算的表达式(如[销售利润]的表达式:[销售利润]=销售收入-销售成本,[净利润]的表达式:[净利润]=毛利润-应交税),要写出能解析此表达式(不是在写程序的时候就去直接计算结果,而是写个能根据表达式来计算结果的程序,在你写这些算法的时候,表达式是未知,也就是说,今天给你的是表达式x = y + z,明天改成x = 2y + 3z,你的算法都要能计算出正确的结果)并计算出结果,请问有没有人在这方面有过经验?从自己不会到写出来需要多少时间?请高手指点指点!怎么个思路?谢谢!
序号 项目名称
===========================================================
1 一、邮政业务收入
2   减:邮政业务成本
3     营业税金及附加
4 二、邮政业务收支差额
5   加:其他业务收入
6   减:其他业务支出
7     管理费用
8     财务费用
9 三、邮政营业收支差额
10   加:投资收益
11     其中:计提的短期投资跌价准备
12        计提的长期投资减值准备
13        计提的委托贷款减值准备
14     补贴收入
15     营业外收入
16   减:营业外支出
17 四、邮政企业收支差额
18   加:附属企业收入
19   减:附属企业支出
20 五、收支差额总额(亏损以“-”号表示)
...全文
302 15 打赏 收藏 转发到动态 举报
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
犀山居士 2008-03-26
  • 打赏
  • 举报
回复
我现在使用这样的思路来实现:
先用函数解析表达式,返回为可以计算运行的SQL语句字符串
在存储过程里来执行这个语句字符串,返回查询结果,不过会有多个记录集被返回,而且其中还会包含有空记录集(这个是存储过程会存在的问题,没办法,只能在程序中先判断是否为空,并且状态为打开,再取数)

存储过程代码:

create proc p_Indicators_MonthAnalysis
(
@orga varchar(50), --机构编码
@date1 varchar(50), --开始日期
@date2 varchar(50) --结束日期
)
as
begin
declare @strsql nvarchar(4000)
declare @syear varchar(10)
declare @ind_head nvarchar(1000)
declare @ind_foot nvarchar(1000)
declare @ind_body nvarchar(4000)
set @syear = datename(year,@date1)
select @ind_head =
' declare @orga varchar(30)
declare @date1 datetime
declare @date2 datetime
declare @year int
select @orga='''+@orga+''',@date1='''+@date1+''',@date2='''+@date2+''',@year='+@syear+'
declare @tmp table([id] int identity,[指标ID] int,[指标名称] nvarchar(256),[预算阶次ID] int,[预算阶次] nvarchar(20),[基本分] dec(8,2),[阶次系数] dec(8,2),[去年同期] dec(18,2),[本期预算数] dec(18,2),[实际完成数] dec(18,2),[完成率] dec(18,4),[实际得分] dec(18,2))'
, @ind_foot = '
select [id],[指标ID],[指标名称],[预算阶次ID],[预算阶次],[基本分],[阶次系数],[去年同期],[本期预算数],[实际完成数],[完成率],[实际得分] from @tmp order by [id]'
--查询基本指标
--查询业务收入、收支差额
set @ind_body = [dbo].[f_q_MonthAnalysis_base_byname]('业务收入')
if len(@ind_body)>0 exec(@ind_head+@ind_body+@ind_foot)
set @ind_body = [dbo].[f_q_MonthAnalysis_base_byname]('收支差额')
if len(@ind_body)>0 exec(@ind_head+@ind_body+@ind_foot)
end
go


返回组合的查询语句的函数:

create function f_q_MonthAnalysis_base_byname
(
@item nvarchar(50) --项目名称
)
returns nvarchar(4000)
as
begin
declare @tmp nvarchar(4000)
select @tmp = '
insert into @tmp([指标ID],[指标名称],[基本分],[预算阶次ID],[预算阶次],[阶次系数],[去年同期],[本期预算数],[实际完成数])
select top 1 a.[id],a.[指标名称],['+@item+'基本分] as [基本分值],d.[id],d.[阶次名称],[系数]
,(case when exists(select [实际上报] from [PracticalIndicators_Report]
where dateadd(year,-1,[上报年月]) between @date1 and @date2
and [机构编码] like @orga and [考核指标ID]=a.[id]) then (select sum([实际上报]) from [PracticalIndicators_Report]
where dateadd(year,-1,[上报年月]) between @date1 and @date2
and [机构编码] like @orga and [考核指标ID]=a.[id]) else '+[dbo].[f_q_comp_byexp]([计算表达式],-1,0,0)+' end) as [去年同期]
,'+[dbo].[f_q_budg_byexp]([计算表达式])+' as [本期预算数]
,(case when exists(select [实际上报] from [PracticalIndicators_Report] where [上报年月] between @date1 and @date2
and [机构编码] like @orga and [考核指标ID]=a.[id]) then (select sum([实际上报]) from [PracticalIndicators_Report]
where [上报年月] between @date1 and @date2
and [机构编码] like @orga and [考核指标ID]=a.[id]) else '+[dbo].[f_q_comp_byexp]([计算表达式],0,0,0)+' end) as [实际完成数]
from [BasicIndicators_Info] as a, [MonthPoints_parameters] as b
,[AdvancedPlan_Info] as c,[Budget_Order] as d,[dbo].[f_OrderCoefficient_Year](@year) as e
where a.[指标名称] like '''+@item+''' and datepart(year,b.[生效年月]) <= @year
and d.[id]=c.['+@item+'阶次ID] and [机构编码] like @orga and c.[年度]=@year and e.[阶次ID] = d.[id]
order by b.[生效年月] desc
update @tmp set [完成率]=[dbo].[f_ind_rate_of_progress]([本年预算数],[实际完成数]),[实际得分]=[dbo].[f_ind_allot_scoring]('+(case when @item like '业务收入' then '0' else '1' end)+',@year,[预算阶次ID],[本年预算数],[实际完成数])'
from [BasicIndicators_Info] where [指标名称] like @item
return (@tmp)
end
go


解析表达式的函数:

create function f_q_budg_byexp --为了减少字符串长度,使用简写名称,全名:[f_get_query_budget_by_expression]
(
@expression nvarchar(512) --表达式,由一系列数据项和运算操作符组合而成。
)
returns nvarchar(4000)
as
begin
declare @strsql nvarchar(4000)
--先按样式表中的项目查,如果在样式表中不存在,再去查询表达式元素表
if (len(@expression) > 1 and left(@expression,1) = '=')
begin
declare @i int
declare @j int
declare @l int
declare @ctype int
declare @chr nchar
declare @tmp nvarchar(64)
select @i = 2, @l = len(@expression), @strsql = ''
while @i <= @l
begin
set @chr = substring(@expression,@i,1)
if len(@chr) > 0 --忽略空格字符
begin
set @ctype = [dbo].[f_emelenttype_by_char](@chr)
set @j = (case @ctype when 1 then [dbo].[f_endpoint_by_numeric](@expression,@i)
when 2 then charindex(']',@expression,@i)+1
else @i + 1 end)
set @tmp = substring(@expression,@i,@j-@i)
set @strsql = @strsql + (
case @ctype when 1 then [dbo].[f_value_by_numeric](@tmp) --操作数,转换为数值
when 2 then [dbo].[f_q_budg_byeme](@tmp) --元素项目,转换为查询数据的查询语句
else [dbo].[f_expressionchar_by_char](@tmp) end) --运算操作符,特号等,转换为正确的符号
set @i = @j
end
else
set @i = @i + 1
end
end
return (case when len(@strsql) = 0 then '0' else replace([dbo].[f_empty_byexp](@strsql),'''','''''') end)
end
go



解析表达式的函数所实现的功能并不是计算,而是解析成一个计算表达式,并且加入了除数为零的判断,如果为零则表达式的计算结果要为空,否则运行将会报错而无法执行!
比如表达式:'=[业务总收入] - [业务总支出] +[本地GDP] * 100% +400/0/0'
返回的查询语句:0-0+0*1.00000000+400/[dbo].[f_empty_byval](0)/[dbo].[f_empty_byval](0)

当然还写了不少函数,共写了十多个函数,这里就不一一贴出来了,这是关键的几个函数。

如果你们有更优的算法希望大家共同学习共同研究共同讨论!谢谢!
犀山居士 2008-02-27
  • 打赏
  • 举报
回复
我不想在VB程序里去解析这些表达式,我是想在SQL的函数里去解析,这样的好处是速度快,如果在VB里解析,需要先查询出所有项目的表达式,再一一解析,然后再一一去组查询语句合,最后还是一个一个地单独查询结果,我这不是只有一个表达式,就如我在提问中的那样,有许多项目,如果这样一个一个地解析,一个一个地查询,速度简直太慢了,我想在SQL写个函数,在VB中只要查询这个函数,在函数内部一次性查询出结果来
犀山居士 2008-02-27
  • 打赏
  • 举报
回复
请问好像在SQL的函数(function)里不能使用这样的语句是吧?exec('select [金额] from [销售记录表] where [项目序号] in (' + '1,2,3' + ')')

也不能去执行一个非扩展存储过程是吧?
犀山居士 2008-02-27
  • 打赏
  • 举报
回复
指的是直接从表中取数,筛选的条件为项目序号在列表中的记录

如:
项目表
序号 项目名称
=========================
1 销售软件
2 软件服务费
3 销售硬件
4 包装材料费
...

销售记录表
序号 日期 项目序号 金额
==================================================
1 2008-02-01 2 1500.00
2 2008-02-02 3 500.00
3 2008-02-05 1 35000.00
4 2008-02-05 4 500.00
...

若销售收入中的数据来源:1,2,3,则是从销售记录表中查询金额条件是项目序号in(1,2,3),查询语句就应该是:
select sum([金额]) as [销售收入] from [销售记录表] where [日期] between '2008-02-01' and '2008-02-29 23:59:59' and [项目序号] in (1,2,3)
liuyann 2008-02-27
  • 打赏
  • 举报
回复

数据来源

是什么意思?
== 思想重于技巧 ==
liuyann 2008-02-27
  • 打赏
  • 举报
回复

如果你的表入下,则可以用VBA直接处理

序号  项目名称    数据来源    表达式
===========================================================
1    业务收入            =销售收入+其它收入
2     销售收入   1,2,3
3     其它收入   5,8
4    业务支出            =销售成本+管理费用+财务费用
5     销售成本   4,6,9
6     管理费用   10,12
7     财务费用   11
8    销售利润            =销售收入*0.22
9    收支差额总额          =业务收入-业务支出
== 思想重于技巧 ==
犀山居士 2008-02-27
  • 打赏
  • 举报
回复
序号  项目名称    数据来源    表达式
===========================================================
1    业务收入            =销售收入+其它收入
2     销售收入   1,2,3
3     其它收入   5,8
4    业务支出            =销售成本+管理费用+财务费用
5     销售成本   4,6,9
6     管理费用   10,12
7     财务费用   11
8    销售利润            =销售收入*0.22
9    收支差额总额          =业务收入-业务支出
WWWWA 2008-02-27
  • 打赏
  • 举报
回复
用VBA动态生成SQL语句,再生成相关数据
犀山居士 2008-02-27
  • 打赏
  • 举报
回复
比如:
数据来源与表达式只取其中之一,有表达式就取表达式,无表达式就取数据来源(从指定表中查数,条件是项目为清单中的项目序号)
序号 项目名称 数据来源 表达式
===========================================================
1 业务收入 =销售收入+其它收入
2  销售收入 1,2,3
3  其它收入 5,8
4 业务支出 =销售成本+管理费用+财务费用
5  销售成本 4,6,9
6  管理费用 10,12
7  财务费用 11
8 销售利润 =销售收入*0.22
9 收支差额总额 =业务收入-业务支出
当查询有数据来源的项时,可以直接从指定表中查询筛选条件为数据来源中的项目序号
当查询业务收入,按表达式,我们应该分别查询出销售收入和其它收入再求和,即是销售收入(查询出来的结果)+其它收入(查询出来的结果)
而当查询收支差额总额时,我们的程序就要知道是实际上是:销售收入(查询出来的结果)+其它收入(查询出来的结果)-销售成本(查询出来的结果)+管理费用(查询出来的结果)+财务费用(查询出来的结果)
还要根据表达式中的运算关系运算出结果
假如:
销售收入=20000,其它收入=1500,销售成本=12000,管理费用=1800,财务费用=1450
那么:
收支差额总额=20000+1500-12000-1800-1450=6250
销售利润=20000*0.22=4400

现在,我们是看到表达式并用人的眼睛去看,去分析,但现在是改成用程序去识别
liuyann 2008-02-27
  • 打赏
  • 举报
回复

有兴趣的话,可以加入这个新群
19055578 - CSDN-VBA&Access

=========================================================
可加入QQ群 19055578 晚19:00 - 24:00在线
=========================================================


== 思想重于技巧 ==
liuyann 2008-02-27
  • 打赏
  • 举报
回复

计算是替换一下即可
== 思想重于技巧 ==
liuyann 2008-02-27
  • 打赏
  • 举报
回复

定义
1 select sum([金额]) from [销售表]
2. select sum(.....
3...
== 思想重于技巧 ==
liuyann 2008-02-27
  • 打赏
  • 举报
回复

计算公式
[20] = [1]-[2]-[3]+[4]+[5]-[6]-[7]-[8]+....

定义你的计算公式这样即可
== 思想重于技巧 ==
liuyann 2008-02-27
  • 打赏
  • 举报
回复

不难,你的计算公式放在哪儿?
== 思想重于技巧 ==

2,209

社区成员

发帖
与我相关
我的任务
社区描述
其他数据库开发 其他数据库
社区管理员
  • 其他数据库社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧