SQL> CREATE OR REPLACE FUNCTION F_SUM_MULTI(P_IN IN NUMBER) RETURN NUMBER AS
2 V_RESULT_MULTI NUMBER DEFAULT 1;
3 V_RESULT NUMBER DEFAULT 0;
4 BEGIN
5 FOR I IN 1..P_IN LOOP
6 V_RESULT_MULTI := V_RESULT_MULTI * I;
7 V_RESULT := V_RESULT + V_RESULT_MULTI;
8 END LOOP;
9 RETURN V_RESULT;
10 END;
11 /
函数已创建。
SQL> SELECT F_SUM_MULTI(5) FROM DUAL;
F_SUM_MULTI(5)
--------------
153
代码很简单,功能也已经实现了。但是,问题并不想我想的这么简单。
SQL> SELECT F_SUM_MULTI(100) FROM DUAL;
F_SUM_MULTI(100)
----------------
~
奇怪,输出结果为什么会是~呢?莫非是超过了NUMBER能表示的最大的范围?
SQL> SELECT F_SUM_MULTI(83) FROM DUAL;
F_SUM_MULTI(83)
---------------
3.994E+124
SQL> SELECT F_SUM_MULTI(84) FROM DUAL;
F_SUM_MULTI(84)
---------------
~
果然是超过了NUMBER能表示的最大的范围。NUMBER类型能表达的最大值是9.9999999999999999999999999999999999*10E125。以前还从没有碰到过超出Oracle最大处理范围的情况,也一直没有想到过会碰到超出最大精度。看来阶乘不愧是结果增长最迅速的操作。
由于超过了Oracle能处理的最大值,Oracle已经很难处理这个问题了。莫非已经没有办法来处理这个问题了?
上文以及提到,由于计算的数值的大小以及超过了Oracle所能表示的最大范围,因此,Oracle中已经无法进行计算了。
首先考虑的是能否通过外部过程来实现。利用C或JAVA程序来计算,并将最终结果通过字符串的方式返回给Oracle。
这篇文章给出通过外部C过程的方式来实现。本例是模仿Tom的EXPERT ONE ON ONE ORACLE中C外部过程例子进行编写的。OCI程序的连接、初始化,以及数据传递部分和Tom的例子很相似,只是在需要的地方进行了一些小的修改。
整个程序代码如下,multi_sum.c:
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <ctype.h>
#include <oci.h>
#define INI_FILE_NAME "/tmp/multi_sum.ini"
typedef struct ociStruct
{
OCIExtProcContext * ctx;
OCIEnv * envhp;
OCISvcCtx * svchp;
OCIError * errhp;
ub1 debugf_flag;
char debugf_path[255];
char debugf_filename[50];
} ocihStruct;
void _debugf(ocihStruct * ocih, char * fmt, ...)
{
va_list ap;
OCIFileObject * fp;
time_t theTime=time(NULL);
char msg[8192];
ub4 bytes;
if (OCIFileOpen(ocih->envhp, ocih->errhp, &fp, ocih->debugf_filename, ocih->debugf_path,
OCI_FILE_WRITE_ONLY, OCI_FILE_APPEND|OCI_FILE_CREATE, OCI_FILE_TEXT) != OCI_SUCCESS)
return;
strftime(msg, sizeof(msg), "%Y%m%d%H%M%S GMT", gmtime(&theTime));
OCIFileWrite(ocih->envhp, ocih->errhp, fp, msg, strlen(msg), &bytes);
va_start(ap, fmt);
vsprintf(msg, fmt, ap);
va_end(ap);
strcat(msg, "n");
OCIFileWrite(ocih->envhp, ocih->errhp, fp, msg, strlen(msg), &bytes);
OCIFileClose(ocih->envhp, ocih->errhp, fp);
}
void _debugf(ocihStruct * ocih, char * fmt, ...);
#define debugf if((ocih!=NULL) && (ocih->debugf_flag)) _debugf
static int raise_application_error(ocihStruct * ocih, int errCode, char * errMsg, ...)
{
char msg[8192];
va_list ap;
va_start(ap, errMsg);
vsprintf(msg, errMsg, ap);
va_end(ap);
debugf(ocih, "raise application error(%d, %s)", errCode, msg);
if(OCIExtProcRaiseExcpWithMsg(ocih->ctx, errCode, msg, 0) == OCIEXTPROC_ERROR)
{
debugf(ocih, "Unable to raise exception");
}
return -1;
}
static char * lastOciError(ocihStruct * ocih)
{
sb4 errcode;
char * errbuf=(ch