1、SQLLOADER的CONTROL文件

//********************************************************************************//
基本格式:
LOAD DATA 
INFILE 'T.DAT' // 要导入的数据文件(格式1) 
//INFILE 'TT.DAT' // 导入多个文件(可以和格式1并列使用) 
//INFILE * // 要导入的内容就在CONTROL文件里 下面的BEGINDATA后面就是导入的内容(和格式1互斥使用)

APPEND INTO TABLE TABLE_NAME // 指定装入的表(这里有几种加载方式)

//以下是4种装入表的方式 
//APPEND // 原先的表有数据就加在后面 
// INSERT // 装载空表,如果原先的表有数据SQLLOADER会停止默认值 
// REPLACE // 原先的表有数据 原先的数据会全部删除 
// TRUNCATE // 指定的内容和REPLACE的相同 会用TRUNCATE语句删除现存数据

BADFILE 'C:BAD.TXT' // 指定坏文件地址

FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' 
// 装载这种数据: "10","20","30","40","50" 
// TERMINATED BY X'09' // 以十六进制格式'09'表示文本文件用TAB键分隔
// 示例文本数据: "10" "20" "30" "40" "50" 
// TERMINATED BY WHITESPACE // 装载这种数据: "10" "lg" "lg"

TRAILING NULLCOLS ************* 表的字段没有对应的值时允许为空

************* 下面是表的字段 
(COL_1 , COL_2 ,COL_FILLER FILLER // FILLER 关键字 此列的数值不会被装载)

//指定的TERMINATED可以在表的开头 也可在表的内部字段部分
// 当没声明FIELDS TERMINATED BY ',' 时也可以逐个字段来声明 
// (
// COL_1 [INTERGER EXTERNAL] TERMINATED BY ',' , 
// COL_2 [DATE "DD-MON-YYY"] TERMINATED BY ',' , 
// COL_3 [CHAR] TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' 
// ) 
// 当没声明FIELDS TERMINATED BY ','用位置告诉字段装载数据 
// ( 
// COL_1 POSITION(1:2), 
// COL_2 POSITION(3:10), 
// COL_3 POSITION(*:16), // 这个字段的开始位置在前一字段的结束位置 
// COL_4 POSITION(3:10) CHAR(8), // 指定字段的类型 
// COL_5 POSITION(3:10) "TRIM(:COL_5)", // 挤压两端空格
// COL_6 POSITION(3:10) "SEQ.NEXTVAL", // 取SEQUENCE值
// )

BEGINDATA // 对应开始的 INFILE * 要导入的内容就在CONTROL文件里 
10,20,30 
20,30,40 
//********************************************************************************//
CONTROL文件示例:
//注意BEGINDATA后的数值前面不能有空格

1 ***** 普通装载 
LOAD DATA 
INFILE * 
REPLACE INTO TABLE DEPT 
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' 
(DEPTNO, 
DNAME, 
LOC 

BEGINDATA 
10,SALES,"""USA""" 
20,ACCOUNTING,"VIRGINIA,USA" 
30,CONSULTING,VIRGINIA 
40,FINANCE,VIRGINIA 
50,"FINANCE","",VIRGINIA // LOC 列将为空 
60,"FINANCE",,VIRGINIA // LOC 列将为空

2 ***** FIELDS TERMINATED BY WHITESPACE 和 FIELDS TERMINATED BY X'09' 的情况 
LOAD DATA 
INFILE * 
REPLACE INTO TABLE DEPT 
FIELDS TERMINATED BY WHITESPACE 
-- FIELDS TERMINATED BY X'09' 
(DEPTNO, 
DNAME, 
LOC 

BEGINDATA 
10 Sales Virginia

3 ***** 指定不装载那一列 
LOAD DATA 
INFILE * 
REPLACE INTO TABLE DEPT 
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' 
( DEPTNO, 
FILLER_1 FILLER, // 下面的 "Something Not To Be Loaded" 将不会被装载 
DNAME, 
LOC 

BEGINDATA 
20,Something Not To Be Loaded,Accounting,"Virginia,USA"

4 ***** POSITION的列子 
LOAD DATA 
INFILE * 
REPLACE INTO TABLE DEPT 
( DEPTNO POSITION(1:2), 
DNAME POSITION(*:16), // 这个字段的开始位置在前一字段的结束位置 
LOC POSITION(*:29), 
ENTIRE_LINE POSITION(1:29) 

BEGINDATA 
10ACCOUNTING VIRGINIA,USA

5 ***** 使用函数日期的一种表达 TRAILING NULLCOLS的使用 
LOAD DATA 
INFILE * 
REPLACE INTO TABLE DEPT 
FIELDS TERMINATED BY ',' 
TRAILING NULLCOLS // 其实下面的ENTIRE_LINE在BEGINDATA后面的数据中是没有直接对应 
// 的列的值 如果第一行改为 10,Sales,Virginia,1/5/2000,, 就不用TRAILING NULLCOLS了 
(DEPTNO, 
DNAME "UPPER(NAME)", // 使用函数 
LOC "UPPER(:LOC)", 
LAST_UPDATED DATE 'DD/MM/YYYY', // 日期的一种表达方式 还有'DD-MON-YYYY' 等 
ENTIRE_LINE "EPTNO||NAME||:LOC||:LAST_UPDATED" 

BEGINDATA 
10,Sales,Virginia,1/5/2000 
20,Accounting,Virginia,21/6/1999 
30,Consulting,Virginia,5/1/2000 
40,Finance,Virginia,15/3/2001

6 ***** 使用自定义的函数 // 解决的时间问题 
CREATE OR REPLACE 
FUNCTION MY_TO_DATE( P_STRING IN VARCHAR2 ) RETURN DATE 
AS 
TYPE FMTARRAY IS TABLE OF VARCHAR2(25);

L_FMTS FMTARRAY := FMTARRAY( 'DD-MON-YYYY', 'DD-MONTH-YYYY', 
'DD/MM/YYYY', 
'DD/MM/YYYY HH24:MI:SS' ); 
L_RETURN DATE; 
BEGIN 
FOR I IN 1 .. L_FMTS.COUNT 
LOOP 
BEGIN 
L_RETURN := TO_DATE( P_STRING, L_FMTS(I) ); 
EXCEPTION 
WHEN OTHERS THEN NULL; 
END; 
EXIT WHEN L_RETURN IS NOT NULL; 
END LOOP;

IF ( L_RETURN IS NULL ) 
THEN 
L_RETURN := 
NEW_TIME( TO_DATE('01011970','DDMMYYYY') + 1/24/60/60 * 
P_STRING, 'GMT', 'EST' ); 
END IF;

RETURN L_RETURN; 
END; 
/

LOAD DATA 
INFILE * 
REPLACE INTO TABLE DEPT 
FIELDS TERMINATED BY ',' 
TRAILING NULLCOLS 
(DEPTNO, 
DNAME "UPPER(NAME)", 
LOC "UPPER(:LOC)", 
LAST_UPDATED "MY_TO_DATE( :LAST_UPDATED )" // 使用自定义的函数 

BEGINDATA 
10,Sales,Virginia,01-april-2001 
20,Accounting,Virginia,13/04/2001 
30,Consulting,Virginia,14/04/2001 12:02:02 
40,Finance,Virginia,987268297 
50,Finance,Virginia,02-apr-2001 
60,Finance,Virginia,Not a date

7 ***** 合并多行记录为一行记录 
LOAD DATA 
INFILE * 
CONCATENATE 3 // 通过关键字CONCATENATE 把几行的记录看成一行记录 
INTO TABLE DEPT 
REPLACE //注意这个例子格式与前边有些不同 
FIELDS TERMINATED BY ',' 
(DEPTNO, 
DNAME "UPPER(NAME)", 
LOC "UPPER(:LOC)", 
LAST_UPDATED DATE 'DD/MM/YYYY' 

BEGINDATA 
10,Sales, // 其实这3行看成一行 10,Sales,Virginia,1/5/2000 
Virginia, 
1/5/2000 
// 这列子用 CONTINUEIF LIST="," 也可以 
告诉SQLLDR在每行的末尾找逗号 找到逗号就把下一行附加到上一行

LOAD DATA 
INFILE * 
CONTINUEIF THIS(1:1) = '-' // 找每行的开始是否有连接字符 - 有就把下一行连接为一行 
// 如 -10,Sales,Virginia, 
// 1/5/2000 就是一行 10,Sales,Virginia,1/5/2000 
// 其中1:1 表示从第一行开始 并在第一行结束 还有CONTINUEIF NEXT 但CONTINUEIF LIST最理想 
INTO TABLE DEPT 
REPLACE 
FIELDS TERMINATED BY ',' 
(DEPTNO, 
DNAME "upper(:dname)", 
LOC "upper(:loc)", 
LAST_UPDATED date 'dd/mm/yyyy' 

BEGINDATA // 但是好象不能象右面的那样使用 
-10,Sales,Virginia, -10,Sales,Virginia, 
1/5/2000 1/5/2000 
-40, 40,Finance,Virginia,13/04/2001 
Finance,Virginia,13/04/2001

8 ***** 载入每行的行号 
LOAD DATA 
INFILE * 
INTO TABLE T 
REPLACE 
( SEQNO RECNUM //载入每行的行号 
TEXT POSITION(1:1024)) 
BEGINDATA 
fsdfasj //自动分配一行号给载入 表t 的seqno字段 此行为 1 
fasdjfasdfl // 此行为 2 ...

9 ***** 载入有换行符的数据 
注意: UNIX 和 WINDOWS 不同 n & /n 
< 1 > 使用一个非换行符的字符 
LOAD DATA 
INFILE * 
INTO TABLE DEPT 
REPLACE 
FIELDS TERMINATED BY ',' 
TRAILING NULLCOLS 
(DEPTNO, 
DNAME "UPPER(NAME)", 
LOC "UPPER(:LOC)", 
LAST_UPDATED "MY_TO_DATE( :LAST_UPDATED )", 
COMMENTS "REPLACE(:COMMENTS,'N',CHR(10))" // REPLACE 的使用帮助转换换行符 

BEGINDATA 
10,Sales,Virginia,01-april-2001,This is the SalesnOffice in Virginia 
20,Accounting,Virginia,13/04/2001,This is the AccountingnOffice in Virginia 
30,Consulting,Virginia,14/04/2001 12:02:02,This is the ConsultingnOffice in Virginia 
40,Finance,Virginia,987268297,This is the FinancenOffice in Virginia

< 2 > 使用fix属性 
LOAD DATA 
INFILE DEMO17.DAT "FIX 101" 
INTO TABLE DEPT 
REPLACE 
FIELDS TERMINATED BY ',' 
TRAILING NULLCOLS 
(DEPTNO, 
DNAME "UPPER(NAME)", 
LOC "UPPER(:LOC)", 
LAST_UPDATED "MY_TO_DATE( :LAST_UPDATED )", 
COMMENTS 

BEGINDATA 
10,Sales,Virginia,01-april-2001,This is the Sales 
Office in Virginia 
20,Accounting,Virginia,13/04/2001,This is the Accounting 
Office in Virginia 
30,Consulting,Virginia,14/04/2001 12:02:02,This is the Consulting 
Office in Virginia 
40,Finance,Virginia,987268297,This is the Finance 
Office in Virginia

// 这样装载会把换行符装入数据库,下面的方法就不会,但要求数据的格式不同

LOAD DATA 
INFILE DEMO18.DAT "FIX 101" 
INTO TABLE DEPT 
REPLACE 
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' 
TRAILING NULLCOLS 
(DEPTNO, 
DNAME "UPPER(NAME)", 
LOC "UPPER(:LOC)", 
LAST_UPDATED "MY_TO_DATE( :LAST_UPDATED )", 
COMMENTS 

BEGINDATA 
10,Sales,Virginia,01-april-2001,"This is the Sales 
Office in Virginia" 
20,Accounting,Virginia,13/04/2001,"This is the Accounting 
Office in Virginia" 
30,Consulting,Virginia,14/04/2001 12:02:02,"This is the Consulting 
Office in Virginia" 
40,Finance,Virginia,987268297,"This is the Finance 
Office in Virginia"

< 3 > 使用var属性 
LOAD DATA 
INFILE DEMO19.DAT "VAR 3" 
// 3 告诉每个记录的前3个字节表示记录的长度 如第一个记录的 071 表示此记录有 71 个字节 
INTO TABLE DEPT 
REPLACE 
FIELDS TERMINATED BY ',' 
TRAILING NULLCOLS 
(DEPTNO, 
DNAME "UPPER(NAME)", 
LOC "UPPER(:LOC)", 
LAST_UPDATED "MY_TO_DATE( :LAST_UPDATED )", 
COMMENTS 

BEGINDATA 
07110,Sales,Virginia,01-april-2001,This is the Sales 
Office in Virginia 
07820,Accounting,Virginia,13/04/2001,This is the Accounting 
Office in Virginia 
08730,Consulting,Virginia,14/04/2001 12:02:02,This is the Consulting 
Office in Virginia 
07140,Finance,Virginia,987268297,This is the Finance 
Office in Virginia

< 4 > 使用STR属性 
// 最灵活的一中 可定义一个新的行结尾符 WIN 回车换行 : CHR(13)||CHR(10)

此列中记录是以 A|RN 结束的 
SELECT UTL_RAW.CAST_TO_RAW('|'||CHR(13)||CHR(10)) FROM DUAL; 
结果 7C0D0A

LOAD DATA 
INFILE DEMO20.DAT "STR X'7C0D0A'" 
INTO TABLE DEPT 
REPLACE 
FIELDS TERMINATED BY ',' 
TRAILING NULLCOLS 
(DEPTNO, 
DNAME "UPPER(NAME)", 
LOC "UPPER(:LOC)", 
LAST_UPDATED "MY_TO_DATE( :LAST_UPDATED )", 
COMMENTS 

BEGINDATA 
10,Sales,Virginia,01-april-2001,This is the Sales 
Office in Virginia| 
20,Accounting,Virginia,13/04/2001,This is the Accounting 
Office in Virginia| 
30,Consulting,Virginia,14/04/2001 12:02:02,This is the Consulting 
Office in Virginia| 
40,Finance,Virginia,987268297,This is the Finance 
Office in Virginia|

10 *****象这样的数据 用 nullif 子句

10-jan-200002350Flipper seemed unusually hungry today. 
10510-jan-200009945Spread over three meals.

ID POSITION(1:3) NULLIF ID=BLANKS // 这里可以是BLANKS 或者别的表达式 
// 下面是另一个列子 第一行的 1 在数据库中将成为 NULL 
LOAD DATA 
INFILE * 
INTO TABLE T 
REPLACE 
(N POSITION(1:2) INTEGER EXTERNAL NULLIF N='1', 
V POSITION(3:8) 

BEGINDATA 
1 10 
20lg 
//********************************************************************************// 
SQLLOADER的命令:
SQLLDR USERID=SYS/SYS@DB_SERVICE CONTROL=XXXX.CTL LOG=XXXX.LOG BINDSIZE=1048576 ROWS=100 
ERRORS=10000 READSIZE=2097152 SILENT=(HEADER,FEEDBACK)
关于这些参数的帮助在命令行直接执行SQLLDR可以得到,这里指出BINDSIZE不应该大于READSIZE的值。

关于SPOOL导出文本数据的一些格式建议:
SQL*PLUS环境设置SET NEWPAGE NONE HEADING OFF SPACE 0 PAGESIZE 0 TRIMOUT ON TRIMSPOOL ON LINESIZE 2500
注:LINESIZE要稍微设置大些,免得数据被截断,它应和相应的TRIMSPOOL结合使用防止导出的文本有太多的尾部空格。
但是如果LINESIZE设置太大,会大大降低导出的速度,另外在WINDOWS下导出最好不要用PLSQL导出,速度比较慢,
直接用COMMEND下的SQLPLUS命令最小化窗口执行。
对于SPOOL数据的SQL,最好要自己定义格式,以方便我们的导入,如例子如下:
SELECT
JBSJ.JSJDM||CHR(9)|| 
JBSJ.ZZJGDM||CHR(9)||
JBSJ.YYZZH||CHR(9)||
REPLACE(REPLACE(JBSJ.NSRMC,CHR(10)),CHR(13))||CHR(9)||
QYRY.ZJLXDM||CHR(9)||
REPLACE(REPLACE(QYRY.ZJHM,CHR(10)),CHR(13))||CHR(9)||
REPLACE(REPLACE(QYRY.XM,CHR(10)),CHR(13))||CHR(9)||
REPLACE(REPLACE(JBSJ.ZCDZ,CHR(10)),CHR(13))||CHR(9)||
REPLACE(REPLACE(JBSJ.JYDZ,CHR(10)),CHR(13))||CHR(9)||
REPLACE(REPLACE(JBSJ.JYFW,CHR(10)),CHR(13))||CHR(9)||
REPLACE(REPLACE(JBSJ.JYDZYB,CHR(10)),CHR(13))||CHR(9)||
ZCLX.DJZCLXDM||CHR(9)||
TO_CHAR(JBSJ.KYDJRQ,'YYYY-MM-DD')||CHR(9)||
JBSJ.SWJGZZJGDM||CHR(9)||
REPLACE(REPLACE(SWJGZZJG.SWJGZZJGMC,CHR(10)),CHR(13))||CHR(9)||
JBSJ.ZCZBJE||CHR(9)||
JBSJ.NSRZT||CHR(9)||
NSRZT.NSRZTMC
FROM DJDB.DJ_JL_JBSJ JBSJ,
DJDB.DJ_JL_QYRY QYRY,
DMDB.GY_DM_SWJGZZJG SWJGZZJG,
DMDB.DJ_DM_DJZCLX ZCLX,
DMDB.DJ_DM_NSRZT NSRZT
WHERE JBSJ.DJZCLXDM=ZCLX.DJZCLXDM
AND JBSJ.JSJDM=QYRY.JSJDM
AND QYRY.ZWDM='01'
AND JBSJ.SWJGZZJGDM=SWJGZZJG.SWJGZZJGDM
AND JBSJ.NSRZT=NSRZT.NSRZTDM
AND JBSJ.NSRZT!=90
AND JBSJ.KYDJRQ < TO_DATE('20040701','YYYYMMDD')
对于字段内包含很多回车换行符的应该给与过滤,形成比较规矩的文本文件。

通常情况下,我们使用SPOOL方法,将数据库中的表导出为文本文件的时候会采用两种方法,如下述: 
方法一:采用以下格式脚本  
set colsep '' ------设置列分隔符 
  set trimspool on 
  set linesize 120 
  set pagesize 2000 
  set newpage 1 
  set heading off 
  set term off 
  spool 路径+文件名 
  select * from tablename; 
  spool off 
方法二:采用以下脚本 
set trimspool on 
  set linesize 120 
  set pagesize 2000 
  set newpage 1 
  set heading off 
  set term off 
  spool 路径+文件名 
  select col1||','||col2||','||col3||','||col4||'..' from tablename; 
  spool off 
比较以上方法,即方法一采用设定分隔符然后由sqlplus自己使用设定的分隔符对字段进行分割,方法二将分隔符拼接在SELECT语句中,即手工控制输出格式。 
在实践中,我发现通过方法一导出来的数据具有很大的不确定性,这种方法导出来的数据再由sql ldr导入的时候出错的可能性在95%以上,尤其对大批量的数据表,如100万条记录的表更是如此,而且导出的数据文件狂大。 
而方法二导出的数据文件格式很规整,数据文件的大小可能是方法一的1/4左右。经这种方法导出来的数据文件再由sqlldr导入时,出错的可能性很小,基本都可以导入成功。 
因此,实践中我建议大家使用方法二手工去控制spool文件的格式,这样可以减小出错的可能性,避免走很多弯路。

SPOOL、SQLLOADER数据导出导入的一点小总结的更多相关文章

  1. MySQL数据导出导入【转】

    MySQL基础 关于MySQL数据导出导入的文章,目的有二: 1.备忘 2.供开发人员测试 工具 mysqlmysqldump 应用举例 导出 导出全库备份到本地的目录 mysqldump -u$US ...

  2. 不同版本的SQL Server之间数据导出导入的方法及性能比较

    原文:不同版本的SQL Server之间数据导出导入的方法及性能比较 工作中有段时间常常涉及到不同版本的数据库间导出导入数据的问题,索性整理一下,并简单比较下性能,有所遗漏的方法也欢迎讨论.补充. 0 ...

  3. 涂抹mysql笔记-数据导出导入

    数据导出导入<>利用CSV存储引擎加载数据:CSV存储引擎基于CSV格式文件存储数据,CSV格式是纯文本格式的文件,以逗号分隔取值.CSV引擎表的所有列值不能为空.Excel可以直接打开有 ...

  4. SQL Server批量数据导出导入BCP&Bulk使用

    数据导出导入,首先考虑使用什么技术实现导出与导入利用BCP结合Bulk技术实现数据的导出与导入 1.bcp数据导出(这里是命令行方式),导出的数据需是格式化的,有两种方式可选 a.对传输的数据格式要求 ...

  5. php做EXCEL数据导出导入开发的一些小问题

    前两天刚刚做开发CRM系统项目,在做要做EXCEL导出导入功能,因为以前做.NET开发用的是NPOI,但可是没找到PHP版本的,所以就网搜找了个国外的开源PHPEXCEL , 一开始只是做了简单的导入 ...

  6. 浅谈hbase表中数据导出导入(也就是备份)

    转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=23916356&id=3321832 最近因为生产环境hbase ...

  7. oracel数据导出导入

    一.导出模式(三种模式)及命令格式 1. 全库模式 exp 用户名/密码@网络服务名 full=y file=路径\文件名.dmp log=路径\文件名.log 2. 用户模式(一般情况下采用此模式) ...

  8. Oracle如何实现创建数据库、备份数据库及数据导出导入的一条龙操作

    Oracle中对数据对象和数据的管理,无疑都是使用PL/SQL Developer来进行管理,该工具也提供给我们很多方便.快捷的操作,使得我们不再为Oracle本身丑陋.难用的UI而抱怨.由于我们一般 ...

  9. [转]Oracle如何实现创建数据库、备份数据库及数据导出导入的一条龙操作

    本文转自:http://www.cnblogs.com/wuhuacong/archive/2012/03/09/2387680.html Oracle中对数据对象和数据的管理,无疑都是使用PL/SQ ...

随机推荐

  1. OpenGL学习笔记:拾取与选择

    转自:OpenGL学习笔记:拾取与选择 在开发OpenGL程序时,一个重要的问题就是互动,假设一个场景里面有很多元素,当用鼠标点击不同元素时,期待作出不同的反应,那么在OpenGL里面,是怎么知道我当 ...

  2. mysql开发中使用存储过程

    在mysql开发中使用存储过程的理由: 当希望在不同的应用程序或平台上执行相同的函数,或者封装特定功能时,存储过程是非常有用的 mysql 执行语句是要先编译,然后再执行的.这样如果查询并发大的时候. ...

  3. ava获得当前文件路径

    第一种: File f = new File(this.getClass().getResource("/").getPath()); System.out.println(f); ...

  4. winston日志管理2

    上次讲到 Exceptions  例外 Handling Uncaught Exceptions with winston 使用winston处理未捕获的异常(这个如果对于异步,我不是很喜欢用) 使用 ...

  5. cocos2dx 3.x(一张背景图利用定时器实现循环轮播)

    // // MainScene.hpp // helloworld // // Created by apple on 16/9/19. // // #ifndef MainScene_hpp #de ...

  6. nodejs weixin 笔记

    http://www.oschina.net/code/snippet_218887_25870 好文章: http://codelife.me/blog/2013/04/23/developing- ...

  7. SQL Server 2012大数据导入Oracle的解决方案

    在实际工作中需要把SQL Server中的表导入Oracle.之前尝试过直接用SQL Server的DTS进行都没问题.但这次因为数据量在千万级所以报虚拟内存不足.最后通过SQL Server导出tx ...

  8. 理解运算符 || 和 && 及方法

    || 前面为true的话直接返回前面的值,前面为false的话返回后面的值.如下: console.log(0 || 1); console.log(1 || 0); console.log(1 || ...

  9. mysql查找字符串出现位置

    MySQL中的LOCATE和POSITION函数使用方法 FIND_IN_SET(str,strlist) 假如字符串str 在由N 子链组成的字符串列表strlist 中,则返回值的范围在 1 到 ...

  10. 通达信:显示K线图日期

    INFO_A:=STRCAT('INFO_A=', STRCAT(CON2STR(REF(MONTH, REF_BAR_A), 0), STRCAT('-', STRCAT(CON2STR(REF(D ...