C#中通过SQLLoader导入csv文件到Oracle以及第一行错误的解决

实现的功能为:

1:从csv文件中的第一行提取列名生成数据库表
2:将原始的csv文件转换成sqlloder能识别的csv(增加列),根据时间列生成一个整形主键列
3:生成ctl控制文件
4:启动Sqlloder并导入

这里做记录主要是导入时出现了莫名其妙的问题

第一行第一列总是会报错误
第一列为数字列,则SQLLoader报 ORA-01722
第一列调整成时间列并将数字列放到最后,则SQLLoader报时间列转换错误。
参考各种文章不能解决,参考的解决方案包括
1:在ctl文件中指定字段类型
https://blog.csdn.net/qq_25798961/article/details/88534104

CHAR                         字符型
INTEGER EXTERNAL    整型
DECIMAL EXTERNAL    浮点型
DATE 'YYYYMMDD'      日期型

数字列增加 选项 INTEGER EXTERNAL 没有效果
2:数据列做转换
https://community.oracle.com/tech/developers/discussion/3684710/ora-01722-invalid-number-in-sql-loader

Options(errors=10000,skip=3)
LOAD DATA
INFILE '/home/appltest/Sheet1.csv'
BADFILE 'sample.bad'
DISCARDFILE 'sample.dsc'
REPLACE
INTO TABLE sampl_table
fields terminated by ','
optionally enclosed by '"'  
trailing nullcols
(
customer_name  CHAR NULLIF customer_name = BLANKS "RTRIM(:customer_name)",
customer_no  CHAR NULLIF customer_no = BLANKS "RTRIM(:customer_no)",
item_no  CHAR NULLIF item_no = BLANKS "RTRIM(:item_no )",
field1      FILLER,
field2      FILLER,
valid_amount_month1    "to_number(:valid_amount_month1 , '999,999,999.99')"  ,
valid_units_month1      "to_number(substr(:valid_units_month1,instr(:valid_units_month1,'$')+1),'999,999,999.99')",
field3      FILLER,
field4      FILLER,
valid_budget_units_month1    "to_number(:valid_budget_units_month1, '999,999,999.99')"                                      ,
valid_budget_amount_month1  "to_number(trim(substr(:valid_budget_amount_month1,instr(:valid_budget_amount_month1,'$')+1)),'999,999,999.99')"    ,
valid_rate    CHAR NULLIF valid_rate = BLANKS "RTRIM(:valid_rate)"
)

3:ctl文件增加 "str '\r\n'" 项没有效果
3:ctl文件第一列加上"replace(replace(:CHANNELKEY, CHR(13), ''), CHR(10), '')" 配置项,没有效果

最终通过以下方案解决

1:设置生成的csv中包含一行头信息
2:ctl中包含一个OPTIONS(skip=1)配置,跳过头信息行的导入
究其根本仍不知道是 文件编码 还是 windows下换行 等等原因导致的。

核心代码如下:

  public void Process()
        {
            ClearFiles();
            ReBuildDataFile(); //解析-1:将输入文件转换成Sqlloder可识别的文件,拼接时间,生成主键
            CreateDataTable(); //解析-2:生成表结构
            BuildCtlFile();    //解析-3:生成Sqlloder控制文件
            StartSqlloader();  //解析-4:启动Sqlloder,执行导入
        }


        //生成sqloader可识别的控制文件
        private string BuildCtlFile()
        {
            string strError = string.Empty;
            string strColumns = AttachKeyColumn();      
            strColumns = strColumns.Replace("DATETIMES", "DATETIMES \"to_timestamp(:DATETIMES,'yyyy-mm-dd hh24:mi:ss.ff')\"");
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("OPTIONS(skip=1)");
            sb.AppendLine("load data");
            sb.AppendLine(string.Format("infile '{0}' \"str '\\r\\n'\"", GetCsvFileFullName()));
            sb.AppendLine(string.Format("append into table {0}", GetTableName()));
            sb.AppendLine(string.Format("fields terminated by '{0}'", this.splitChar.ToString()));
            sb.AppendLine("optionally enclosed by '\"'");
            sb.AppendLine("trailing nullcols");
            sb.AppendLine(string.Format("({0})", strColumns));
            GenerateFile(GetCtlFileFullName(), sb.ToString());
            return strError;
        }

        private string StartSqlloader()
        {
            string strError = string.Empty;
            string output = string.Empty;
            bool processOk = true;
            using (System.Diagnostics.Process p = new System.Diagnostics.Process())
            {
                p.StartInfo.FileName = this.SqlLoaderConfig;
                p.StartInfo.Arguments = string.Format(" {0} control={1} log={2}", this.OracleDBConfig, GetCtlFileFullName(), GetCtlFileFullName().Replace("ctl", "log"));
                p.StartInfo.CreateNoWindow = true; //不再新窗口启动
                p.StartInfo.UseShellExecute = false; //不使用操作系统的shell启动
                p.StartInfo.RedirectStandardOutput = true; //输出重定向
                p.StartInfo.RedirectStandardError = true;
                p.StartInfo.RedirectStandardInput = true;
                //p.Exited += P_Exited;
                p.Start();
                output = p.StandardOutput.ReadToEnd();
                //ExitCode 11g 12c和9i不同
                if (p.ExitCode == 1) //Ex_Fail
                {
                    processOk = false;
                }
                if (p.ExitCode == 2) //Ex_Warn
                {
                    processOk = false;
                }
                if (p.ExitCode == 4) //Ex_FTL
                {
                    processOk = false;
                }
            }
            if (!processOk)
            {
                throw new IOException(output);
            }

            return strError;
        }

原始CSV文件为:

DATETIMES,CD_000_0,CD_000_1,CD_000_2
2022-09-19 16:11:17.720,100,200,300
2022-09-19 16:11:17.721,101,201,301
2022-09-19 16:11:17.722,102,202,302
2022-09-19 16:11:17.723,103,203,303
2022-09-19 16:11:17.724,104,204,304
2022-09-19 16:11:17.725,105,205,305
2022-09-19 16:11:17.726,106,206,306
2022-09-19 16:11:17.727,107,207,307
2022-09-19 16:11:17.728,108,208,308
2022-09-19 16:11:17.729,109,209,309
2022-09-19 16:11:17.730,110,210,310
2022-09-19 16:11:17.731,111,211,311
2022-09-19 16:11:17.732,112,212,312
2022-09-19 16:11:17.733,113,213,313
2022-09-19 16:11:17.734,114,214,314
2022-09-19 16:11:17.735,115,215,315
2022-09-19 16:11:17.736,116,216,316
2022-09-19 16:11:17.737,117,217,317
2022-09-19 16:11:17.738,118,218,318
2022-09-19 16:11:17.739,119,219,319
2022-09-19 16:11:17.740,120,220,320

转换成的CSV文件为:

CHANNELKEY INTEGER EXTERNAL,DATETIMES,CD_000_0,CD_000_1,CD_000_2
4295477720,2022-09-19 17:11:17.720,100,200,300
4295477721,2022-09-19 17:11:17.721,101,201,301
4295477722,2022-09-19 17:11:17.722,102,202,302
4295477723,2022-09-19 17:11:17.723,103,203,303
4295477724,2022-09-19 17:11:17.724,104,204,304
4295477725,2022-09-19 17:11:17.725,105,205,305
4295477726,2022-09-19 17:11:17.726,106,206,306
4295477727,2022-09-19 17:11:17.727,107,207,307
4295477728,2022-09-19 17:11:17.728,108,208,308
4295477729,2022-09-19 17:11:17.729,109,209,309
4295477730,2022-09-19 17:11:17.730,110,210,310
4295477731,2022-09-19 17:11:17.731,111,211,311
4295477732,2022-09-19 17:11:17.732,112,212,312
4295477733,2022-09-19 17:11:17.733,113,213,313
4295477734,2022-09-19 17:11:17.734,114,214,314
4295477735,2022-09-19 17:11:17.735,115,215,315
4295477736,2022-09-19 17:11:17.736,116,216,316
4295477737,2022-09-19 17:11:17.737,117,217,317
4295477738,2022-09-19 17:11:17.738,118,218,318
4295477739,2022-09-19 17:11:17.739,119,219,319
4295477740,2022-09-19 17:11:17.740,120,220,320

生成的ctl文件为:

OPTIONS(skip=1)
load data
infile 'D:\******\TemplateData\1cf37b73-6727-4618-b636-73a9292ef33d.csv' "str '\r\n'"
append into table FZ_41657_01
fields terminated by ','
optionally enclosed by '"'
trailing nullcols
(CHANNELKEY INTEGER EXTERNAL,DATETIMES "to_timestamp(:DATETIMES,'yyyy-mm-dd hh24:mi:ss.ff')",CD_000_0,CD_000_1,CD_000_2)

附ctl文件说明:

https://blog.csdn.net/demonson/article/details/79712207

OPTIONS (skip=1,rows=128) -- sqlldr 命令显示的 选项可以写到这里边来,skip=1 用来跳过数据中的第一行  
LOAD DATA  
INFILE "users_data.csv" --指定外部数据文件,可以写多 个 INFILE "another_data_file.csv" 指定多个数据文件  
--这里还可以使 用 BADFILE、DISCARDFILE 来指定坏数据和丢弃数据的文件,  
truncate --操作类型,用 truncate table 来清除表中原有 记录  
INTO TABLE users -- 要插入记录的表  
Fields terminated by "," -- 数据中每行记录用 "," 分隔  
Optionally enclosed by '"' -- 数据中每个字段用 '"' 框起,比如字段中有 "," 分隔符时  
trailing nullcols --表的字段没有对应的值时允 许为空  
(  
  virtual_column FILLER, --这是一个虚拟字段,用来跳 过由 PL/SQL Developer 生成的第一列序号  
  user_id number, --字段可以指定类型,否则认 为是 CHARACTER 类型, log 文件中有显示  
  user_name,  
  login_times,  
  last_login DATE "YYYY-MM-DD HH24:MI:SS" -- 指定接受日期的格式,相当用 to_date() 函数转换  
)  

主键列 CHANNELKEY,生成规则为:

 private string FixKeyValue(DateTime time)
        {
            TimeSpan toNow = time.Subtract((new DateTime(time.Year, time.Month - 1, 1)));
            //return (toNow.Ticks / 10000).ToString().Substring(3);
            return (toNow.Ticks / 10000).ToString();
        }
?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,128评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,316评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事?!?“怎么了?”我有些...
    开封第一讲书人阅读 159,737评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,283评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,384评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,458评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,467评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,251评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,688评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,980评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,155评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,818评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,492评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,142评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,382评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,020评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,044评论 2 352

推荐阅读更多精彩内容