达梦数据库计算错误分析 (参数CALC_AS_DECIMAL=1修正)

引子

新近安装了一批DM8的生产库和测试库,现有生产库多为Oracle数据库,运维力量集中在Oracle,团队也开始DM的认证与学习。DM数据库与Oracle数据库的兼容性,使得学习达梦显得比较容易。但DM特有的内容,还是要看官方文档。
比如前段时间,新安装的数据库在查询系统时间时结果不正确。( select sysdate from dual;)

最终确认原因:按照Linux和oracle的参数设置习惯,东八区的参数设置为“+ 08:00”,
DM数据库安装后,设置了如下时区,查询系统时间错误:

错误设置:
[root@dem ~]# vi /etc/dm_svc.conf
TIME_ZONE=(+08:00)
LANGUAGE=(en)
正确的设置:
[root@dem ~]# cat /etc/dm_svc.conf
TIME_ZONE=(480)
LANGUAGE=(en)
在修改参数并重启数据库后,系统查询时间正常。

求分数累加和

公 式 : s u m ( n ) = ∑ 1 n 1 n 公式 :sum(n)=\displaystyle\sum_1^n\frac 1 n sum(n)=1nn1

我 们 只 计 算 前 10 项 : s u m ( 10 ) = 1 1 + 1 2 + 1 3 + ⋯ + 1 9 + 1 10 我们只计算前10项:sum(10)=\displaystyle\frac 1 1+\frac 1 2+\frac 1 3+\dots+\frac 1 9+\frac 1 {10} 10sum(10)=11+21+31++91+101

Oracle 中计算该公式结果正常

在Oracle中使用一个简单的FOR循环,即可实现,为了直观,显示了中间的每个分数计算结果。
完整测试如下:

C:\Users\cdb>sqlplus / as sysdba

SQL*Plus: Release 19.0.0.0.0 - Production on 星期二 126 17:02:07 2022
Version 19.3.0.0.0

Copyright (c) 1982, 2019, Oracle.  All rights reserved.

连接到:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.3.0.0.0

SQL> SET SERVEROUTPUT ON
SQL> DECLARE
  2  n  int:=1;
  3  v_n float:=0;
  4  v_sum float:=0;
  5  BEGIN
  6  for n in 1..10 LOOP
  7  SELECT 1/n  into v_n  FROM dual;
  8  v_sum:=v_sum+v_n;
  9  dbms_output.put_line('Values of 1/'||n||'='||1/n);
 10  END LOOP;
 11  dbms_output.put_line('Values of 1/1+1/2+1/3+...+1/10='||v_sum);
 12  END;
 13  /
Values of 1/1=1
Values of 1/2=.5
Values of 1/3=.3333333333333333333333333333333333333333
Values of 1/4=.25
Values of 1/5=.2
Values of 1/6=.1666666666666666666666666666666666666667
Values of 1/7=.1428571428571428571428571428571428571429
Values of 1/8=.125
Values of 1/9=.1111111111111111111111111111111111111111
Values of 1/10=.1
Values of 1/1+1/2+1/3+...+1/10=2.92896825396825396825396825396825396825

PL/SQL 过程已成功完成。

SQL>

Dm中计算该公式结果错误

使用和Oracle中一样的FOR循环结构,语法正确,显示结果错误:
完整测试如下:

[dmdba@dem ~]$  disql sysdba/Oracle_123@localhost:1801

Server[localhost:1801]:mode is normal, state is open
login used time : 2.523(ms)
disql V8
SQL> SET SERVEROUTPUT ON  
SQL> DECLARE  
2   n  int:=1;
3   v_n float:=0;
4   v_sum float:=0;  
5   BEGIN  
6   for n in 1..10 LOOP  
7   SELECT 1/n  into v_n  FROM dual;  
8   v_sum:=v_sum+v_n;
9   dbms_output.put_line('Values of 1/'||n||'='||1/n);
10  END LOOP;  
11  dbms_output.put_line('Values of 1/1+1/2+1/3+...+1/10='||v_sum);
12  END;  
13  /
Values of 1/1=1
Values of 1/2=0
Values of 1/3=0
Values of 1/4=0
Values of 1/5=0
Values of 1/6=0
Values of 1/7=0
Values of 1/8=0
Values of 1/9=0
Values of 1/10=0
Values of 1/1+1/2+1/3+...+1/10=1E0
DMSQL executed successfully
used time: 1.346(ms). Execute id is 601.
SQL> 
SQL> 

考虑到DM的兼容性参数设置影响,修改了数据库参数COMPATIBLE_MODE=2 。
修改配置文件dm.ini后并重新启动数据库。

[dmdba@dem ~]$ vi /dm/data/DEM/dm.ini
#DaMeng Database Server Configuration file
 COMPATIBLE_MODE                 = 2               #Server compatible mode, 0:none, 1:SQL92, 2:Oracle, 3:MS SQL Server, 4:MySQL, 5:DM6, 6:Teradata

重新执行FOR循环语句,依然报错:

[dmdba@dem ~]$  disql sysdba/Oracle_123@localhost:1801
Server[localhost:1801]:mode is normal, state is open
login used time : 2.344(ms)
disql V8
SQL> select PARA_NAME,PARA_VALUE,PARA_TYPE from v$dm_ini where para_name like 'COMPA%';

LINEID     PARA_NAME       PARA_VALUE PARA_TYPE
---------- --------------- ---------- ---------
1          COMPATIBLE_MODE 2          IN FILE
DMSQL executed successfully
used time: 0.453(ms). Execute id is 1601.
SQL> SET SERVEROUTPUT ON  
SQL> DECLARE  
2   n  int:=1;
3   v_n float:=0;
4   v_sum float:=0;  
5   BEGIN  
6   for n in 1..10 LOOP  
7   SELECT 1/n  into v_n  FROM dual;  
8   v_sum:=v_sum+v_n;
9   dbms_output.put_line('Values of 1/'||n||'='||1/n);
10  END LOOP;  
11  dbms_output.put_line('Values of 1/1+1/2+1/3+...+1/10='||v_sum);
12  END;  
13  /
Values of 1/1=1
Values of 1/2=0
Values of 1/3=0
Values of 1/4=0
Values of 1/5=0
Values of 1/6=0
Values of 1/7=0
Values of 1/8=0
Values of 1/9=0
Values of 1/10=0
Values of 1/1+1/2+1/3+...+1/10=1E0
used time: 8.128(ms). Execute id is 1602.
SQL> 

数据库运维人员的敏感

看到分数运算测试结果,数据库运维人员应该立刻敏感的意识到两个问题,一是运算结果错误本身,二是我们的一大堆监控脚本等怎么办?
看到 1/10没有感觉?

SQL> select 1/10 from dual;
LINEID     1/10       
---------- -----------
1          0
used time: 7.240(ms). Execute id is 500.

那么这个呢?

SQL> select 5/1440 from dual;
LINEID     5/1440     
---------- -----------
1          0
used time: 0.466(ms). Execute id is 501.

再看看这个,我要的5分钟前怎么办?

SQL> select sysdate,sysdate-5/1440 from dual;
LINEID     SYSDATE             SYSDATE-5/1440     
---------- ------------------- -------------------
1          2022-12-12 12:16:28 2022-12-12 12:16:28
used time: 5.318(ms). Execute id is 502.
   

解决的办法(加一个点)

睁大眼睛仔细看差异,下面的程序比之前的多了两个点(“."),分别在2行中。

SQL> SET SERVEROUTPUT ON  
SQL> DECLARE  
2   n  int:=1;
3   v_n float:=0;
4   v_sum float:=0;  
5   BEGIN  
6   for n in 1..10 LOOP  
7   SELECT 1./n  into v_n  FROM dual;  
8   v_sum:=v_sum+v_n;
9   dbms_output.put_line('Values of 1/'||n||'='||1./n);
10  END LOOP;  
11  dbms_output.put_line('Values of 1/1+1/2+1/3+...+1/10='||v_sum);
12  END;  
13  /
Values of 1/1=1
Values of 1/2=0.5
Values of 1/3=0.3333333333333333333333333333333333333
Values of 1/4=0.25
Values of 1/5=0.2
Values of 1/6=0.1666666666666666666666666666666666667
Values of 1/7=0.1428571428571428571428571428571428571
Values of 1/8=0.125
Values of 1/9=0.1111111111111111111111111111111111111
Values of 1/10=0.1
Values of 1/1+1/2+1/3+...+1/10=2.928968253968254E0
DMSQL executed successfully
used time: 0.847(ms). Execute id is 1609.

其实因该是这样的:

SQL> SET SERVEROUTPUT ON  
SQL> DECLARE  
2   n  int:=1;
3   v_n float:=0;
4   v_sum float:=0;  
5   BEGIN  
6   for n in 1..10 LOOP  
7   SELECT 1.0/n  into v_n  FROM dual;  
8   v_sum:=v_sum+v_n;
9   dbms_output.put_line('Values of 1/'||n||'='||1.0/n);
10  END LOOP;  
11  dbms_output.put_line('Values of 1/1+1/2+1/3+...+1/10='||v_sum);
12  END;  
13  /
Values of 1/1=1
Values of 1/2=0.5
Values of 1/3=0.3333333333333333333333333333333333333
Values of 1/4=0.25
Values of 1/5=0.2
Values of 1/6=0.1666666666666666666666666666666666667
Values of 1/7=0.1428571428571428571428571428571428571
Values of 1/8=0.125
Values of 1/9=0.1111111111111111111111111111111111111
Values of 1/10=0.1
Values of 1/1+1/2+1/3+...+1/10=2.928968253968254E0
DMSQL executed successfully
used time: 0.902(ms). Execute id is 1607.

终极同框对比:

SQL> select 1/10 from dual;
LINEID     1/10       
---------- -----------
1          0
used time: 0.132(ms). Execute id is 509.
SQL> select 1.0/10 from dual;
LINEID     1./10
---------- -----
1          0.1
used time: 0.157(ms). Execute id is 510.
SQL> select 1./10 from dual;
LINEID     1./10
---------- -----
1          0.1
used time: 0.288(ms). Execute id is 511.
SQL> select 5/1440 from dual;
LINEID     5/1440     
---------- -----------
1          0
used time: 0.182(ms). Execute id is 512.
SQL> select 5.0/1440 from dual;
LINEID     5./1440                                  
---------- -----------------------------------------
1          0.003472222222222222222222222222222222222
used time: 0.226(ms). Execute id is 513.
SQL> select 5./1440 from dual;
LINEID     5./1440                                  
---------- -----------------------------------------
1          0.003472222222222222222222222222222222222
used time: 0.191(ms). Execute id is 514.
SQL> select sysdate,sysdate-5/1440 from dual;
LINEID     SYSDATE             SYSDATE-5/1440     
---------- ------------------- -------------------
1          2022-12-12 12:35:43 2022-12-12 12:35:43
used time: 0.201(ms). Execute id is 515.
SQL> 
SQL> select sysdate,sysdate-5.0/1440 from dual;
LINEID     SYSDATE             SYSDATE-5./1440    
---------- ------------------- -------------------
1          2022-12-12 12:35:52 2022-12-12 12:30:52
used time: 0.171(ms). Execute id is 516.
SQL> 
SQL> select sysdate,sysdate-5./1440 from dual;
LINEID     SYSDATE             SYSDATE-5./1440    
---------- ------------------- -------------------
1          2022-12-12 12:35:57 2022-12-12 12:30:57
used time: 0.162(ms). Execute id is 517.

C语言编程中类似问题

前一段时间重拾C语言,亦遇到类似问题,本次特地测试了C++和C语言的文章开头分数累加公式:
C++源程序:

#include <iostream>
using namespace std;
 
int main()
{
    int n;
   float sum = 0;
for(int n=1;n<=10;n++)
   {
        cout << "1/" <<n<< "="<< 1/n<<endl; 
		sum = sum+ 1/n;
    }
     cout << "1/1+1/2+1/3+...+1/8+1/9+1/10=" << sum<<endl;    
    return 0;
}

在这里插入图片描述

修改代码加入1.0:
在这里插入图片描述
修成程序加入1.:
在这里插入图片描述
C语言测试:
源程序:

#include <stdio.h>
 
int main()
{
    int n;
    float sum = 0;
for(int n=1;n<=10;n++)
   {
        printf("1/%d=%d\n" ,n,1/n) ;
		sum += 1/n;
    }
     printf("1/1+1/2+1/3+...+1/8+1/9+1/10=%f" ,sum);    
    return 0;
}

结果错误:
在这里插入图片描述
增加1.0的结果
在这里插入图片描述
加入1.结果:
在这里插入图片描述

总结

作为学习过程,保留下面信息:
在Oracle数据库中,隐式转换帮我们处理了类似1/9=0的问题,默认显示为小数结果。而在DM数据库中,需要我们自己处理这个问题,除法输入中必须有一个是浮点数(1.0/9或者1/9.0),运算结果显示为小数。就像我使用C或者C++编写程序代码的测试一样。 建议DM数据库最好还是默认实现这个隐式转换,为Oracle数据库向DM数据库的迁移提供便利。

在咨询DM工程师后,给出了在设置DM兼容Oracle参数后,再设置CALC_AS_DECIMAL =1可以解决该问题。

COMPATIBLE_MODE    = 2    #Server compatible mode, 0:none, 1:SQL92, 2:Oracle, 3:MS SQL Server, 4:MySQL, 5:DM6, 6:Teradata
CALC_AS_DECIMAL    = 1    #Whether integer CALC as decimal, 0: no, 1:only DIV, 2:all only has charactor, 3:all for digit

最终测试:

[dmdba@dem ~]$ disql sysdba/Oracle_123@localhost:1801

Server[localhost:1801]:mode is normal, state is open
login used time : 4.269(ms)
disql V8
SQL> select PARA_NAME,PARA_VALUE,PARA_TYPE from v$dm_ini where para_name like 'CALC_AS_DECIMAL%';

LINEID     PARA_NAME       PARA_VALUE PARA_TYPE
---------- --------------- ---------- ---------
1          CALC_AS_DECIMAL 1          IN FILE

used time: 9.418(ms). Execute id is 600.
SQL> select PARA_NAME,PARA_VALUE,PARA_TYPE from v$dm_ini where para_name like 'COMPA%';

LINEID     PARA_NAME       PARA_VALUE PARA_TYPE
---------- --------------- ---------- ---------
1          COMPATIBLE_MODE 2          IN FILE

used time: 10.104(ms). Execute id is 601.
SQL>  SET SERVEROUTPUT ON
SQL>  DECLARE
2    n  int:=1;
3    v_n float:=0;
4    v_sum float:=0;
5    BEGIN
6    for n in 1..10 LOOP
7    SELECT 1/n  into v_n  FROM dual;
8    v_sum:=v_sum+v_n;
9    dbms_output.put_line('Values of 1/'||n||'='||1/n);
10   END LOOP;
11   dbms_output.put_line('Values of 1/1+1/2+1/3+...+1/10='||v_sum);
12   END;
13   /
Values of 1/1=1
Values of 1/2=0.5
Values of 1/3=0.3333333333333333333333333333333333333
Values of 1/4=0.25
Values of 1/5=0.2
Values of 1/6=0.1666666666666666666666666666666666667
Values of 1/7=0.1428571428571428571428571428571428571
Values of 1/8=0.125
Values of 1/9=0.1111111111111111111111111111111111111
Values of 1/10=0.1
Values of 1/1+1/2+1/3+...+1/10=2.928968253968254E0
DMSQL executed successfully
used time: 0.541(ms). Execute id is 603.
SQL> 
     

结果与Oracle的返回一致,可以直接测试我的那些监控脚本了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

curating

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值