原需求
修改一期的8条线的所有全量比对表的当前分区的end_dt为“30000101”
现象
执行了批量修改end_dt的脚本后,检查account表当前分区的end_dt是否修改过来的时候发现,当前分区的数据丢失了。
ACCOUNT表数据总量:260528503
当前分区数据量:15507310
执行脚本修改end_dt后的数据量:245021193
原因
sqlplus调用的sql太长,文件每一行的长度不得大于2500。
分析
确保create table as 对应的字段顺序正确。
补全account表的当前数据
怀疑account表的数据量太大,oracle一下子不能提交这么多的数据,于是把account表单独拿出来执行,执行完后发现当前
的数据又没有了。在account
表当前分区补充100条数据,再重复前面的动作,account表的当前分区数据还是没有了。整个
过程没有任何异常提示(下面解释了没有提示的原因)。
补全account表的当前数据
下一步检查脚本发现,在sql脚本中的结尾没有写commit,猜测oracle不能自动提交大量数据,针对这一猜测,在sql脚本的
后面加上了commit,再执行核心脚本,当前分区数据丢失。
补全account表的当前数据,由于这个数据量比较大,一下操作补数只补100条数据。
临时表insert到正式表的sql中加错地方,insert的时候没有压缩insert,可能是因为这个原因导致了表空间被
撑满。(由于没有找到验证的方法,这个假设并没有去验证)
把account表的处理过程拿出来单独到PL/SQL中执行,成功创建临时表并导入当前分区数据。由于单独禁索引重建索引比较麻
烦,因此忽略掉这一步,直接把临时表的数据insert到正式表,考虑到索引会影响到性能,只insert部分数据,这一步也成
功。这里证明了脚本写的是没有问题的。
数据insert到临时表出错,这一步的sql太长了,sqlplus允许的字符串有限制。
把create table as
这句很长的sql(长度3540)写到一个文件a.sql,然后在客户端执行
sqlplus $DBUSER/$DBPASS@$DBNAME
@a.sql ,执行完后报如下错误:
SP2-0027: Input is too long (>
2499 characters) - line ignored,很明显是输入的字符串太长。
这样似乎有了方向,把a.sql这条sql加上回车,每行不得大于2500,然后数据库补上当前数据再执行脚本,成功。
原因找到。
在前面提到的,account表当前分区数据丢失而没有报出任何异常信息,而在sqlplus
$DBUSER/$DBPASS@$DBNAME
@a.sql
报出异常,对比了程序中用到的sqlplus写法:sqlplus -S $DBUSER/$DBPASS@$DBNAME。加了-S忽略了执行脚本时的输出
信息。
解决方法
只要是create as select
包括其它sql,只要是出现sql过长的,都是没有执行成功的。找出所有sql长度
大于2500的,找出相应的表名,给sql加上回车,然后整理脚本修改end_dt.
建议:以后只要出现大量的sql语句的脚本,都要做一个长度的检查。
在truncate正式表数据前保证前面的操作正确,做到如下两点:
检查临时表是否建成功
检查临时表的数据有没有insert成功
检查处理前的数据量和处理后的数据量是否一致
独立drop临时表这一步操作,防止临时表insert到正式表出错数据丢失
总结
出现数据没有导入成功,数据丢失,数据导入不完整等这一类错误的时候,最重要的是看有没有报错信息和报错日志,像以上的
问题,如果一开始就想到了sqlplus
-S覆盖了错误信息,那么问题就简单了。如果确实找不到报错信息,也没有生成错误日志,
构造测试数据取少量,单独执行脚本中的每一步操作,定位到哪一步出错,然后去验证出错的可能性。
加载中,请稍候......