2009年3月7日星期六

From .csv To Derby

对于Web应用来说什么最重要?数据。因此这次我们来探讨一种比较山寨的数据迁移方式。
没有数据毫无疑问是痛苦的,人们YY数据的时候一般习惯在 Exel 或者 WPS 的电子表单里把表格填写好,然后呢?怎么把表格放到数据库里呢?又或者,我要把表格的Ci、Cj、Ck列放到数据库里某个表(关系)的Ai、Aj、Ak列(属性)呢?

简单地寻觅一下,你就可以发现 xls 格式的文件可以转为 csv 格式,后者的全称是 Comma Separated Value,言下之意就是(若干)用(英文)逗号分开的值,本质是一种文本格式,Exel 和 WPS 都可以把电子表单直接另存为这种格式,并且让人振奋的是,很多数据库都提供了内嵌过程从 csv 文件导入数据,或者把数据导出到 csv 文件,从而提供了不同数据库间数据迁移的一种可能。

当然事情没那么简单,有两个地方是相当头疼的。

第一,既然是用逗号分隔表单的列、用成对双引号指明字符串,那如果原始数据中就有逗号呢?如果字符串中也有引号呢?不幸的是,Exel 和 WPS 并没有支持配置这两种分隔符,并且让我颇感意外的是,数据库也没有支持通过转义字符的方式解决此类问题,至少我正在使用的 Derby 是没有的。
第二,如果使用的是轻量级的数据库比如 Derby,那么很可能它对内嵌过程的支持比较差,所谓比较差可能仅仅是错误提示不靠谱,但这都一个新手来说可能是致命的...

第一个问题的解决办法有三种:
A. 一种看上去比较不靠谱的方法,并且需要修改操作系统设置,很不美。
B. 适合数据规模比较小的情况,换句话说,如下操作是可以完成而不会让你的机器卡上半天的:打开电子表单-->选中需要导入的区域-->把该区域内容复制到记事本中并保存为 csv 格式。这样做的原因,是操作系统会在列与列之间自动补一个用于分隔的制表符(\t),相对于逗号,它引起错误识别的概率就小很多了
C. database@yssy 上有一位热心水友提供了如下解决方案(引用他的原话):
写一个批处理:
@echo off
setlocal ENABLEDELAYEDEXPANSION
rem InputFile:输入文件 OrgDelimiter:要修改的的分隔符 NewDelimiter:替换后的分隔

set /p "InputFile=Input the filename to be processed:"
set "OutputFile=Modfied_%InputFile%"
set "OrgDelimiter=;"
set "NewDelimiter= "
set "TempLine="
echo Processing the file!!!
for /f "usebackq tokens=*" %%c in ("%InputFile%") do (
set TempLine=%%c
set TempLine=!TempLine:%OrgDelimiter%=%NewDelimiter%!
echo !TempLine!>>%OutputFile%
)
:End
echo Finish Processing,The result file was saved as %OutputFile% in the same d
irectory as %InputFile%.

复制上面一段到*.txt文件中,存成和你已经导出的csv文件同目录下的*.bat文件双击运行*.bat输入要修改的csv文件。可以得到一个新文件。
可以支持大规模,并且不一定非把逗号替换成制表符,而是任意的,我想最好应该是不可打印字符,这样最不可能导致错误,但注意,只能是字符,而不能替换成字符串,这是因为 Derby 的内嵌过程把分隔符的类型定义为了 char(1)

下面讨论第二个问题:
在 Derby 中,数据迁移的内嵌函数主要有3个:
具体的参数定义就不细说了,读官方文档即可,罗列一下我觉得需要注意的几个地方:
  • 如果有恰好有两个用户都建立了同名 Schema ,Derby不会报重名错,而是会使用默认,并报出完全不着边际的错误信息
  • .csv 文件中,EOF不能跟在最后一行数据后,而要另起一行
  • 在参数表中分隔符的表示方法是 ' ' 而不是 '\t'
  • Derby的错误信息真的很烂 - - b
  • 设定目标文件参数时注意相对路径(或者干脆用绝对路径)
  • 虽然理论上应该是大小写不敏感的,但这几条内嵌过程中,表名却不接受小写...
最后附上一个 Sample : 把 data.csv 中的数据迁移到 TEST 表中
建立 TEST 表:
create table TEST (foo int, bar varchar(100), foobar varchar(100));
data.csv 中的文本:
1 hello world!
2 goodbye cruel world!

调用 Derby 的内嵌过程:
CALL SYSCS_UTIL.SYSCS_IMPORT_TABLE(null,'TEST','data.csv',' ',null,null,0);

PS : 区分字符串的双引号问题还有待解决,Plz contact me if u know a non-ugly solution :P

没有评论: