达梦数据库计算错误分析 (参数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)=1∑nn1
我 们 只 计 算 前 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} 我们只计算前10项:sum(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 星期二 12月 6 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的返回一致,可以直接测试我的那些监控脚本了。