ITPUB论坛-中国专业的IT技术社区

标题: 实现四则运算的一条sql语句 [打印本页]

作者: grubbyoo    时间: 2008-9-5 16:19
标题: 实现四则运算的一条sql语句
1.建立一个测试表
create table mar_test( id number, text varchar2(200))
      
      insert into mar_Test values ( 1,'12+556-543*152/2423+23*2*435+34-234');
      insert into mar_Test values ( 2,'12/2/3/4');
      insert into mar_Test values ( 3,'1*2*34');
      insert into mar_Test values ( 4,'1+5-5*2/5+3*2*4*5+34-2');

   select a.id,max(text) text,
          sum(regexp_substr(add_text,'[0-9]+',1,n)*decode(regexp_substr('+'||add_text,'[^0-9]',1,n),'+',1,-1))   --加减部分 计算之和
          +
          nvl(sum( (select  decode(substr(regexp_substr('+'||text,'[+|-]([0-9]+[*|/]+)+[0-9]+',1,n),1,1),'+',1,-1)
                                   *power(10,Sum(Log(10, decode( regexp_substr('*'||regexp_substr(text,'([0-9]+[*|/]+)+[0-9]+',1,n),'[^0-9]',1,rownum),
                                                                                        '*',  regexp_substr(regexp_substr(text,'([0-9]+[*|/]+)+[0-9]+',1,n),'[0-9]+',1,rownum) ,
                                                                                              1/regexp_substr(regexp_substr(text,'([0-9]+[*|/]+)+[0-9]+',1,n),'[0-9]+',1,rownum)
                                               )  )  )  )
                          from dual
                          connect by rownum <=len )) ,0)  wanted    --乘除部分 计算之和
    from  
                 (select a.id,a.text,
                            length(regexp_replace(text,'[0-9]+'))+1 len,
                            regexp_replace(text,'([0-9]+[*|/]+)+[0-9]+',0) add_text
                   from mar_test a  )     a,
                (select rownum n from dual connect by rownum<100) b
  where a.len>=b.n
  group by id

sql 实现对 text中算式的计算 ,限制就是不能加括号
运算结果
ID        TEXT                                                                        WANTED
1        12+556-543*152/2423+23*2*435+34-234        20343.93644
2        12/2/3/4                                                                        0.5
4        1+5-5*2/5+3*2*4*5+34-2                                        156
3        1*2*34                                                                        68

以前如果算式只有加减没有乘除,结果为null,是以前考虑不周, 现在对乘除部分计算之和加上nvl判断

[ 本帖最后由 grubbyoo 于 2008-10-7 12:05 编辑 ]
作者: junsansi    时间: 2008-9-5 16:22

作者: hotiice    时间: 2008-9-5 16:31
我想给你加精华可惜没有权限,那就来一只好评
作者: huangzhiyun123    时间: 2008-9-5 16:33
顶下,楼主好强!!
作者: huangzhiyun123    时间: 2008-9-5 16:37
顶下,楼主好强!!
作者: grubbyoo    时间: 2008-9-5 16:39
原帖由 hotiice 于 2008-9-5 16:31 发表
我想给你加精华可惜没有权限,那就来一只好评

so感谢版主,其实都是看了你的帖子,才搞这么一个 繁复的sql
作者: jiqing1004    时间: 2008-9-5 16:49
楼主太牛了   佩服  佩服  
下来研究研究

发现楼主很喜欢用 正则式
作者: nyfor    时间: 2008-9-5 17:01
顶一下, 给朵花

作者: Silentman    时间: 2008-9-5 17:08
强,使用上了正则表达式!
作者: grubbyoo    时间: 2008-9-5 17:23
感谢各位版主和坛友的鼓励,格式了一下代码

算法就是
将       '12+556-543*152/2423+23*2*435+34-234'
变成2部分  '12+556-                       0+              0+34-234'
        和  -543*152/2423 ;23*2*435
分别计算求和


select a.id,
       max(text) text,
       sum(regexp_substr(add_text, '[0-9]+', 1, n) *
           decode(regexp_substr('+' || add_text, '[^0-9]', 1, n),
                  '+',
                  1,
                  -1)) +                                                                            --加法的数字求和,乘法和除法分开计算 ,取每个子串前的运算符号,如果为 ‘-’就 乘以-1
       sum((select decode(substr(regexp_substr('+' || text,            
                                              '[+|-]([0-9]+[*|/]+)+[0-9]+',
                                              1,
                                              n),
                                1,
                                1),
                         '+',
                         1,
                         -1) *                                                                   -- 如 25*515/544  子串前的运算符号,如果为 ‘-’就 乘以-1
                  power(10,                                                                  -- 从itpub上学到的 利用lg将 连乘 改为加法
                        Sum(Log(10,
                                decode(regexp_substr('*' ||
                                                     regexp_substr(text,
                                                                   '([0-9]+[*|/]+)+[0-9]+',
                                                                   1,
                                                                   n),
                                                     '[^0-9]',
                                                     1,
                                                     rownum),
                                       '*',
                                       regexp_substr(regexp_substr(text,                       
                                                                   '([0-9]+[*|/]+)+[0-9]+',
                                                                   1,
                                                                   n),
                                                     '[0-9]+',
                                                     1,
                                                     rownum),
                                       1 / regexp_substr(regexp_substr(text,
                                                                       '([0-9]+[*|/]+)+[0-9]+',
                                                                       1,
                                                                       n),
                                                         '[0-9]+',
                                                         1,
                                                         rownum)))))
             from dual
           connect by rownum <= len)) wanted
  from (select a.id,
               a.text,
               length(regexp_replace(text, '[0-9]+')) + 1 len,                         --算式中的数字个数
               regexp_replace(text, '([0-9]+[*|/]+)+[0-9]+', 0) add_text        --将算式中 乘的子式 代替成0, 后面分开计算
          from mar_test a) a,
       (select rownum n from dual connect by rownum < 100) b                 --默认算式最多数字100个
where a.len >= b.n
group by id
作者: sunfly1983    时间: 2008-9-5 17:30
终于成精了啊!赞一个!
作者: zhangweicai74    时间: 2008-9-5 17:40
支持,只是这个SQL好复杂啊
作者: smallnavy    时间: 2008-9-5 18:46
看来10G的东东,有空还是要研究一下,一朵花吧。
作者: smallnavy    时间: 2008-9-5 18:48
其实,个人不喜欢太复杂的SQL,难于维护啊。有时维护人员的水平比开发人员要差一些的。
作者: jack198409    时间: 2008-9-5 19:27
支持
有时间的话,也要多多研究哈啊正则
作者: newkid    时间: 2008-9-5 21:42
睡眼惺松来上班,看到grubbyoo老弟的强贴精神为之一振!最近像这么提神的贴子很少了!
作者: smallnavy    时间: 2008-9-5 21:56
LS的刚上班啊。辛苦啊。
作者: kmpx    时间: 2008-9-5 22:03
10G还没用上,复杂语句练手,不错,顶下
作者: lemonlau    时间: 2008-9-6 11:35
真强。。。。。
作者: qingyun    时间: 2008-9-8 12:54
好事做到底,楼主干脆生猛一点,做个一个函数,支持所有四则运算,包括刮号,幂函数Power,对数函数ln等等,
凡是pl/sql支持的数学函数都支持,这样就很有实用价值;
也许一个函数搞不定,可以搞几个中间函数中转一下。
比如:
   '1+greatest(8,10)+sin(3.14/2)+ln(2)*(power(20,2)+5)' =...
作者: 阿日    时间: 2008-9-8 13:37
楼主真强,赞一下牛呀!
利用了正则,在10G中才支持吧
作者: darmee    时间: 2008-9-8 14:13
果然我给LZ发的信息没错,哈哈

不愧是强人,还很谦虚!
作者: hotiice    时间: 2008-9-8 17:20
标题: 能否把中缀式表示的简单算术表达式转化为逆波兰表示
逆波兰表达式是一种把运算符后置的算术表达式,例如普通的表达式2 + 3的逆波兰表示法为2 3+ 。逆波兰表达式的优点是运算符之间不必有优先级关系,也不必用括号改变运算

[ 本帖最后由 hotiice 于 2008-9-8 18:50 编辑 ]
作者: grubbyoo    时间: 2008-9-8 17:26
543*152/2423+23*2*435
如果是这样  逆波兰表达式该如何 表示
作者: hotiice    时间: 2008-9-8 18:52
标题: 大约是,如果没有算错的话
543,152,*,2423 ,/, 23,2,*,435,*,+
作者: hotiice    时间: 2008-9-8 20:46
逆波兰的转换和计算算法。
作者: llee84(http://llee84.itpub.net)
发表于: 2005.04.12 01:20
分类: Windows编程

逆波兰表达式是一种十分有用的表达式,它将复杂表达式转换为可以依靠简单的操作得到计算结果的表达式。例如(a+b)*(c+d)转换为ab+cd+*d+

它的优势在于只用两种简单操作,入栈和出栈就可以搞定任何普通表达式的运算。其运算方式如下:

如果当前字符为变量或者为数字,则压栈,如果是运算符,则将栈顶两个元素弹出作相应运算,结果再入栈,最后当表达式扫描完后,栈里的就是结果。

将一个普通的中序表达式转换为逆波兰表达式的一般算法是:

1)首先构造一个运算符栈,此运算符在栈内遵循越往栈顶优先级越高的原则。

(2)读入一个用中缀表示的简单算术表达式,为方便起见,设该简单算术表达式的右端多加上了优先级最低的特殊符号“#”。

(3)从左至右扫描该算术表达式,从第一个字符开始判断,如果该字符是数字,则分析到该数字串的结束并将该数字串直接输出。

(4)如果不是数字,该字符则是运算符,此时需比较优先关系。

做法如下:将该字符与运算符栈顶的运算符的优先关系相比较。如果,该字符优先关系高于此运算符栈顶的运算符,则将该运算符入栈。倘若不是的话,则将栈顶的运算符从栈中弹出,直到栈顶运算符的优先级低于当前运算符,将该字符入栈。

(5)重复上述操作(1)-(2)直至扫描完整个简单算术表达式,确定所有字符都得到正确处理,我们便可以将中缀式表示的简单算术表达式转化为逆波兰表示的简单算术表达式。
其中运算符优先级如下:

*/:4

+-:3

(:2

):1
作者: 书剑浪子    时间: 2008-9-9 14:01
学习了。。。
作者: wangfans    时间: 2008-9-9 14:24
支持·!
作者: kelsoncong    时间: 2008-9-9 14:42
SO提神的帖子!
作者: hllwuxin    时间: 2008-9-9 15:14
都是强人啊。收徒弟不。收下我吧。
作者: jason_tu    时间: 2008-9-10 16:53

作者: 小小大象    时间: 2008-9-19 12:04

作者: iamclq    时间: 2008-9-20 23:18
正则表达式还没用过,支持一下
作者: Hopewell_Go    时间: 2008-9-22 17:26
强啊。。。
作者: shiguibao    时间: 2008-9-22 17:50
不顶能行吗?
作者: rjfy    时间: 2008-9-30 09:29

作者: lyqcfo    时间: 2008-9-30 19:55
thanks!!!
作者: jlandzpa    时间: 2008-10-1 23:04
很好.
作者: passionyqz    时间: 2008-10-2 10:21
看的很复杂..
作者: phoenix-nirvana    时间: 2008-10-4 11:38
顶下,楼主好强!!
作者: liweixtu    时间: 2008-10-6 10:08
标题: 回复 #1 grubbyoo 的帖子
LZ如果能解释下就更好了!
作者: roland_wg    时间: 2008-10-6 10:44
强人 厉害!
作者: stronghearted    时间: 2008-10-6 22:09

作者: showmewhat    时间: 2008-10-7 09:07
真是有点复杂呀。。。强
作者: gkl0818    时间: 2008-10-7 11:39
有错误
我在里面添加一条记录
insert into mar_test values(5,'6+6');
再运行结果为NULL
作者: grubbyoo    时间: 2008-10-7 11:54
的确有这个问题,我把 加减 和 乘除分开计算, 如果没有乘除,乘除部分就是null ,结果 就是null 了,
需要 用nvl判断null 变换为0,十分感谢gkl0818


   select a.id,max(text) text,
          sum(regexp_substr(add_text,'[0-9]+',1,n)*decode(regexp_substr('+'||add_text,'[^0-9]',1,n),'+',1,-1))
          +
          nvl(sum( (select decode(substr(regexp_substr('+'||text,'[+|-]([0-9]+[*|/]+)+[0-9]+',1,n),1,1),'+',1,-1)
                             *power(10,Sum(Log(10, decode( regexp_substr('*'||regexp_substr(text,'([0-9]+[*|/]+)+[0-9]+',1,n),'[^0-9]',1,rownum),
                                               '*',  regexp_substr(regexp_substr(text,'([0-9]+[*|/]+)+[0-9]+',1,n),'[0-9]+',1,rownum) ,
                                                     1/regexp_substr(regexp_substr(text,'([0-9]+[*|/]+)+[0-9]+',1,n),'[0-9]+',1,rownum)
                                              )  )  )  )
           from dual
           connect by rownum <=len )) ,0)wanted
    from  
                 (select a.id,a.text,
                            length(regexp_replace(text,'[0-9]+'))+1 len,
                            regexp_replace(text,'([0-9]+[*|/]+)+[0-9]+',0) add_text
                   from mar_test a  )     a,
              (select rownum n from dual connect by rownum<100) b
  where a.len>=b.n
group by id
作者: yanhua312    时间: 2008-10-8 10:59
标题: 学习
我是来学习的。
作者: 哈儿1号    时间: 2008-10-8 16:16
貌似不支持带括号的运算表达式
作者: 枯木萧萧    时间: 2008-10-9 22:20
很强大
慢慢看
作者: microns    时间: 2008-10-12 01:31
学习
作者: hdeyong    时间: 2008-10-13 18:36
晕,我用的是 9i
作者: lxhde    时间: 2008-10-20 21:18
呵呵,厉害。sql不是强项,还须多多学习lz才是!
作者: caizhuoyi    时间: 2008-10-20 21:20
标题: 技术值得钦佩。
动态sql实现起来多简单啊!
作者: yangi1314    时间: 2008-10-21 16:51
我的9i,没有regexp_replace这个函数,先学习下
作者: casper0511    时间: 2008-10-22 09:00
向lz学习
作者: speedcomet    时间: 2008-10-22 11:43
厉害啊
作者: 成功的起点    时间: 2008-10-25 09:59
标题: 我有个不太好理解的SQL,帮帮呀
下面以显示JONES及其下属为例,说明使用PRIOR column1=column2从顶向下显示层次数据的方法。
  SQL> SELECT LPAD(' ' ,3*(LEVEI-1))||ename ename FROM emp
     start with ename='JONES'
    CONNECT BY PRIOR EMPNO=MGR;
    ENAME
    JONES
          SCOTT
                 ADMS
          FORD
                 SMITH
问题是;名字前有空格,job job是啥意思
作者: 成功的起点    时间: 2008-10-25 10:00
标题: 我有个不太好理解的SQL,帮帮呀
下面以显示JONES及其下属为例,说明使用PRIOR column1=column2从顶向下显示层次数据的方法。
  SQL> SELECT LPAD(' ' ,3*(LEVEI-1))||ename ename FROM emp
     start with ename='JONES'
    CONNECT BY PRIOR EMPNO=MGR;
    ENAME
    JONES
          SCOTT
                 ADMS
          FORD
                 SMITH
问题是;名字前有空格,ename ename是啥意思
作者: lukytoday    时间: 2008-10-25 11:03
前一个ename是表的实际字段,后一个是显示的别名。可以起过不一样的名字
作者: SKmadfrog    时间: 2008-10-27 17:19
有点复杂
作者: czhtony    时间: 2008-10-29 09:23
水平高
作者: fireguok    时间: 2008-11-1 13:28
I tried a few times and seems not working all the time.
作者: yanfei082927    时间: 2008-11-3 10:12
我用的也是9i,没有regexp_replace啊,该怎么转换呢
作者: sfgvvv__    时间: 2008-11-4 15:19
我是这样做的:
function charToNumber(p_char varchar2)return number
  is
  lv_sql varchar2(500);
  lv_num number;
  begin
    lv_sql:='select '||p_char||' from dual';
    execute immediate lv_sql into lv_num;
     return(lv_num);
  exception when others then
  --dbms_output.put_line('error!');
    return(0);
  end charToNumber;
作者: longwansheng    时间: 2008-11-4 17:03
ERROR 在行 14:
ORA-00904: "REGEXP_REPLACE": ID 無效
作者: catherine_2008    时间: 2008-11-7 09:27
厉害
作者: juyang811    时间: 2008-11-10 16:48
强!不过,最好能把括号一起判断了
作者: zhongct    时间: 2008-11-10 22:25
看君一篇贴,胜读十年书!
作者: IT光棍PUB    时间: 2008-11-12 09:05
学习了.....
作者: 黑暗小王子    时间: 2008-11-16 10:47
学到知识了!!多谢楼主
作者: 顾而不问    时间: 2008-11-19 11:15
果然有创意!
作者: tanli1984    时间: 2008-11-19 21:49
标题: 回复 #1 grubbyoo 的帖子
楼主的SQL好复杂,我还是喜欢简单点的。好奇就用存储过程实现了一下。
CREATE OR REPLACE PROCEDURE p_mar_test IS
v_str1 VARCHAR2(500);
v_str2 VARCHAR2(500);
v_count INT;
BEGIN
     SELECT COUNT(1) INTO v_count FROM mar_test;
     WHILE v_count >=1 LOOP
            SELECT TEXT INTO v_str1 FROM mar_test WHERE ID = v_count;
            v_str2 := 'insert into mar_test SELECT id,'||v_str1 ||'  FROM mar_test where ID = ' || v_count;
            EXECUTE IMMEDIATE v_str2;
            v_count := v_count - 1;
     END LOOP;
     COMMIT;
END p_mar_test;
作者: happyqmz    时间: 2008-11-20 11:05
但是楼主,你这个不错,但是
如果我直接就用  select  12+556-543*152/2423+23*2*435+34-234 from dual不就可以了吗
作者: grubbyoo    时间: 2008-11-20 11:51
标题: 回复 #74 tanli1984, happyqmz 的帖子
是很麻烦,这个只为了好玩,顺便学习正则表达式
作者: wfqqwer5213    时间: 2008-11-20 16:24
写个函数execute immediate 'select  '||str|| '  from dual' into result;不就可以了,看了头大的SQL,LZ正则表达式很强大罢
作者: xiaoe_7    时间: 2008-12-3 17:52
qiang han
作者: kingstar2008    时间: 2008-12-15 11:07
标题: 一个更简单解法
这样是不是更简单,根本不用复杂的sql
declare
  cursor cur is
  select t.text
  from mar_test t;
  str varchar2(100);
  i number(10,4);
begin
  open cur;
  loop
    fetch cur into str;
    exit when cur%notfound;
    str := 'select ' || str || ' from dual';
    execute immediate str into i;
    dbms_output.put_line(i);
  end loop;
  close cur;
end;
作者: daikefei2008    时间: 2008-12-15 11:29
我公司是搜狐、奇虎网、中华网、和讯网、海量科技、DISCUZ!、财经网等大型网站的IDC托管、网站安全、运维外包维护服务商。具有10年以上大型财经门户安全、运维经验团队技术支持。本公司是中国互联网络信息中心 (CNNIC)注册会员,具有ICP备案正规公司。拥有廊坊网通、电信运营维护权,机房环境优秀,各种保障设施俱全。高品质BGP带宽出口省级带宽40G,经各大门户网站考验,稳定高效。是距离北京最近的正规机房,京津塘高速廊坊出口南200米,距离北京市区25分钟车程,专业的7X24小时运维团队维护专业方便。价格只有北京市内的三分之一。丰富的互联网业务合作经验,实在的价格。欢迎来电洽谈。

欢迎致电,随时为您解答网络安全的技术问题:
联系人:戴克非  
手机:13701225154  
公司电话:010-62167939-821  
QQ:739620691    MSN:daikefei2008@hotmail.com
作者: zzzdwd    时间: 2008-12-16 16:38
嗯,新特性使用不错啊。
作者: rockyan712    时间: 2008-12-18 11:27
标题: mark

作者: iamlomen    时间: 2008-12-20 09:31
太牛了 !!
作者: fengzj    时间: 2008-12-24 11:54
涨涨见识
作者: ugly927846    时间: 2008-12-26 12:23
good      。。。。。。。。。
作者: cow006    时间: 2009-1-2 11:05
Y的,学习了
作者: wabjtam123    时间: 2009-1-3 22:13
9i好象不行啊
作者: hanjs    时间: 2009-1-6 22:51
构思不错,可是有必要搞这么复杂么?

写一个函数处理计算公式,效率也不比你的差吧,而且还支持带()的
作者: popo0319    时间: 2009-1-8 15:34
是呀,为啥不直接计算而要拐这么多弯呢?
从来没用过正则表达式,LZ的东西好复杂,看明白了倒是,不知道啥时候用的上。请提点。
作者: zuohao_lu    时间: 2009-1-12 15:24
佩服
作者: lyc7676    时间: 2009-1-14 15:06
太强了,下下来好好学习~~
作者: ssunwei317    时间: 2009-1-16 10:57
标题: 强,顶一个

作者: thinker    时间: 2009-1-22 11:18
行行出狀元~~真的是不知道該如何表達敬意~~
作者: freed609    时间: 2009-1-22 11:25
精神可嘉,不过在我公司肯定被枪毙了。
作者: maks_    时间: 2009-1-22 18:29

作者: illpig    时间: 2009-2-4 11:26
标题: 回复 #1 grubbyoo 的帖子
楼主真厉害
作者: ironicshuang    时间: 2009-2-6 11:52
绝了,给顶了
作者: lyrryl    时间: 2009-2-7 21:35
学习了
作者: gehy1    时间: 2009-2-9 12:34
学习了。。
作者: freedom2k    时间: 2009-2-14 15:05
牛人,学习一下
作者: 〇〇    时间: 2009-3-4 09:07
如果表达式中包含变量
如a+b*c-d/e
另外有一个表存储变量值,
var value
a 1
b 2
c 3
d 4
e 5
怎么处理?
作者: duanzhihui    时间: 2009-3-4 09:48
高手,好好学习~~




欢迎光临 ITPUB论坛-中国专业的IT技术社区 (http://www.itpub.net/) Powered by Discuz! X3.2