Oracle”文本”报表输出
Sam.T
2012-12-12
现象:
一个普通的XML或者PL/SQL过程导出的Oracle报表,用Excel打开,第一次存档的时候,可能很大(之前碰到过,一财务的MM用XML报表导2年的数据,文档体积超过50M,而她们的电脑配置低,文档没法打开。尴尬。);但是用Excel另存为一个新的Excel文档之后,容量会小很多。为什么?
就是因为,通常我们看到的文档内容,事实上后面有大量的格式控制代码(特别是XML),这些代码也会占用很多容量,导致打开文档的时候会很慢。当你将这个Excel文档另存之后,Excel会将这些代码去掉,所以容量小很多。
XML的格式代码见下图,每个栏位都有:
实现思路:
先说明一下,此方法只适合(大量的)数据导出。就是对数据的显示格式没要求的话可以考虑用这个方法。如果对格式要求很高(例如发货单/销售单打印),还是乖乖用XML开发吧,这种一般是单对单的打印,容量大不了哪里去。
所以现在的目的主要是为了节省输出的文本字符。通常的包输出Excel文档的Procedure报表的开发,实际上是开发网页,一个显示Excel文档的网页。众多的网页控制代码(tr/td等等的网页格式控制代码),占用了很大空间。别小看这个,如果一个报表要输出几十万行的数据,就是网页格式控制代码都占用不少空间。同理,XML的也一样。
所以,基于这个思路,可以考虑直接输出文本txt,就是纯输出内容的文本,用制表符TAB按键作为栏位的分隔符。主要的步骤还是用FND_FILE.PUT_LINE输出用户要的内容,下面有例子。
因为后期是用Excel直接查看输出的内容(相当于用Excel打开txt文本),所以貌似没出现乱码等的问题。比较方便。
我自己实际测试过,用这个方法制作的报表导出的文档的容量,和在Toad里面直接导出Excel文档的容量几乎一样。
注意地方:
注意的是,如果您的报表的栏位本身就包含制表符,就是说,这个栏位会被自动分行。要特别注意的。可以用RAPLACE的办法,将栏位内容的TAB符号替代为空或者空格就行。
例子:
--XYG_XXXX公司物料编码信息报表
--All Inclusive GUI
PROCEDURE XYG_INV_ITEM_MSG_RPT(
X_ERR_MSG OUTVARCHAR2
,X_ERR_CODE OUTNUMBER
,P_ORGANIZATION_ID IN NUMBERDEFAULT107--跑哪个组织下的所有物料。默认是主组织的
,P_ITEM_NUMBER IN VARCHAR2DEFAULTNULL---物料编码以什么开头(因为抓所有的太多)
,P_AUTO_MAIL_RECEIVER IN VARCHAR2--发邮件通知的人,不填就是不自动发邮件。填错当然也发不了
);
PROCEDURE XYG_INV_ITEM_MSG_RPT(
X_ERR_MSG OUTVARCHAR2
,X_ERR_CODE OUTNUMBER
,P_ORGANIZATION_ID IN NUMBERDEFAULT107--跑哪个组织下的所有物料。默认是主组织的
,P_ITEM_NUMBER IN VARCHAR2DEFAULTNULL---物料编码以什么开头(因为抓所有的太多)
,P_AUTO_MAIL_RECEIVER IN VARCHAR2--发邮件通知的人,不填就是不自动发邮件。填错当然也发不了
)
IS
V_ORGANIZATION_CODE ORG_ORGANIZATION_DEFINITIONS.ORGANIZATION_CODE%TYPE;
V_ORGANIZATION_NAME ORG_ORGANIZATION_DEFINITIONS.ORGANIZATION_NAME%TYPE;
V_MAIL_TITLE VARCHAR2(1000);
V_MAIL_RECEIVERVARCHAR2(1000);
V_MAIL_CONTENTVARCHAR2(4000);
V_REQUEST_ID NUMBER;
V_RPT_LINK VARCHAR2(500);
V_RPT_LINK_CONTENTVARCHAR2(2000);
C_NULL CONSTANTVARCHAR2(10):=null;
CURSOR C_ALL_ITEM
IS
SELECT MSIB.ROWID ROW_ID,MSIB.SEGMENT1 ITEM_NUMBER
,MSIB.DESCRIPTION
,TO_CHAR(MSIB.CREATION_DATE,'YYYY-MM-DD HH24:MI:SS')
CREATION_DATE
,MSIB.PRIMARY_UOM_CODE
FROM MTL_SYSTEM_ITEMS_B MSIB
WHERE1=1--MSIB.ORGANIZATION_ID = OOD.ORGANIZATION_ID
AND MSIB.ENABLED_FLAG='Y'
AND MSIB.INVENTORY_ITEM_STATUS_CODE='Active'
ANDSYSDATEBETWEENNVL(TRUNC(MSIB.START_DATE_ACTIVE),SYSDATE)
AND NVL(TRUNC(MSIB.END_DATE_ACTIVE),SYSDATE)
AND MSIB.ORGANIZATION_ID= P_ORGANIZATION_ID
AND MSIB.SEGMENT1LIKE P_ITEM_NUMBER ||'%'
ORDERBY2;
----
V_ERROR_LOG_IDNUMBER;
V_PROGRAM_POSITION XYG_PROGRAM_ERROR_LOG.PROGRAM_POSITION%TYPE;
V_ERROR_CODE XYG_PROGRAM_ERROR_LOG.ERROR_CODE%TYPE;
V_ERROR_MESSAGE XYG_PROGRAM_ERROR_LOG.ERROR_MESSAGE%TYPE;
V_USER_ID NUMBER:=FND_GLOBAL.USER_ID;
V_LOGIN NUMBER:=FND_GLOBAL.LOGIN_ID;
BEGIN
V_REQUEST_ID:=FND_GLOBAL.CONC_REQUEST_ID;
-- Output Report Head
LOG('Output Report Head');
OUTPUT ('XYG_XXXXX物料编码信息报表');
SELECT ORGANIZATION_CODE,ORGANIZATION_NAME
INTO V_ORGANIZATION_CODE,V_ORGANIZATION_NAME
FROM ORG_ORGANIZATION_DEFINITIONS
WHERE ORGANIZATION_ID= P_ORGANIZATION_ID;
--用CHR(9),就是TAB制表符作为栏位的分割。用Excel可以直接打开。
OUTPUT( '编码组织:'||CHR(9)|| V_ORGANIZATION_CODE||'-'||V_ORGANIZATION_NAME);
OUTPUT ('物料编码'||CHR(9)||'物料描述'||CHR(9)||'主单位');
FOR RECIN C_ALL_ITEMLOOP
--DBMS_OUTPUT.PUT_LINE(REC.ROW_ID);
OUTPUT(NVL(REC.ITEM_NUMBER, C_NULL)||CHR(9)||NVL(REC.DESCRIPTION, C_NULL)||CHR(9)||NVL(REC.PRIMARY_UOM_CODE, C_NULL));
--DBMS_OUTPUT.PUT_LINE(NVL (REC.ITEM_NUMBER, C_NULL)||CHR(9)||NVL (REC.DESCRIPTION, C_NULL)||CHR(9)||NVL (REC.PRIMARY_UOM_CODE, C_NULL));
--DBMS_OUTPUT.PUT_LINE(LENGTHB(NVL (REC.ITEM_NUMBER, C_NULL))||CHR(9)
--||LENGTHB(NVL (REC.DESCRIPTION, C_NULL))||CHR(9)||LENGTHB(NVL (REC.PRIMARY_UOM_CODE, C_NULL)));
ENDLOOP;
V_PROGRAM_POSITION:='END';
EXCEPTION
WHENFND_API.G_EXC_ERRORTHEN
X_ERR_CODE:=1;
LOG(X_ERR_MSG);
RAISE;
WHENOTHERSTHEN
X_ERR_CODE:=2;
X_ERR_MSG:=SQLERRM;
--NULL;
V_ERROR_CODE:=SQLCODE;
V_ERROR_MESSAGE:=SQLERRM;
LOG(SQLERRM);
RAISE;
DBMS_OUTPUT.PUT_LINE(V_ERROR_CODE ||'-' || V_ERROR_MESSAGE);
END XYG_INV_ITEM_MSG_RPT;
实际运行样例:
报表注册: