我有一个文件,其中包含主要以管道分隔的信息。但是一些领域在内部进一步划分。我想你可以称它们嵌套分隔?这就是我文件中的行的外观。
FieldA|FieldB|[FieldC111~FieldC112~FieldC113][FieldC121~FieldC122~FieldC123]|[FieldD111~FieldD112~FieldD113][FieldD121~FieldD122~FieldD123]|FieldE|FieldF
FieldA|FieldB|[FieldC111~FieldC112~FieldC113][FieldC121~FieldC122~FieldC123][FieldC131~FieldC132~FieldC133]|[FieldD111~FieldD112~FieldD113][FieldD121~FieldD122~FieldD123]|FieldE|FieldF
FieldA|FieldB|[FieldC111~FieldC112~FieldC113][FieldC121~FieldC122~FieldC123][FieldC131~FieldC132~FieldC133]|[FieldD111~FieldD112~FieldD113][FieldD121~FieldD122~FieldD123][FieldD131~FieldD132~FieldD133]|FieldE|FieldF
FieldA|FieldB|[FieldC111~FieldC112~FieldC113][FieldC121~FieldC122~FieldC123]|[FieldD111~FieldD112~FieldD113][FieldD121~FieldD122~FieldD123][FieldD131~FieldD132~FieldD133]|FieldE|FieldF
基本上字段C和字段D,封装X组字段,其中X的范围为1-10。每个集合由square []括号封装。没有。方括号内的字段保持不变(由波浪号〜分隔)。
我在Oracle中的表应该如下所示
FieldA|FieldB|FieldC111|FieldC112|FieldC113|FieldC121|FieldC122|FieldC123|FieldC131|FieldC132|FieldC133|FieldD111|FieldD112|FieldD113|FieldD121|FieldD122|FieldD123|FieldD131|FieldD132|FieldD133|
我在UNIX上不是很强大(基本上是谷歌每一个命令)但是我已经提出了以下命令来帮助我划分文件(忽略波形符问题,直到我整理出封装的字段问题,因为我可以使用sql函数在波形符号之间读取)
sed 's/\]\[/|/g' final.txt > final1.txt -- replaces all ][ with |
sed 's/\]|\[/|/g' final1.txt > final2.txt -- replaces all ]|[ with |
sed 's/|\[/|/g' final2.txt > final3.txt -- replaces all |[ with |
sed 's/\]|/|/g' final3.txt > final4.txt -- replaces all ]| with |
这为我提供了完全制表符分隔格式的文件。但是,当我加载文件时,问题出现了,分隔符的数量变化(因为字段C和D中的封装字段的数量变化),因此数据将以先来先服务的方式加载。
例如,如果我在字段C中封装了1个字段,并且在字段D中封装了3个字段,那么我的表将导致字段C中的所有3列和字段D中包含数据的一列。我想要发生的是,如果在字段C中只有1个字段被封装(但是为字段C提供了3个字段),则字段C的其他2个字段应该是空白的,并且字段D中封装的字段应该到它们各自的D领域。
我还有其他一些解决方案,比如拆分文件,将其加载到3-4个不同的表中,然后组合所有3个表。但这将是一项额外的工作。想知道是否有更简单的方法来做到这一点
我希望我已经解释清楚了。对我来说,把它写成文字非常复杂:(
有关我正在处理的数据文件的一些其他信息。
答案 0 :(得分:2)
如果文件位于数据库服务器上,并且您可以将其放入与Oracle directory
对象对应的位置,则可以将初始管道分隔加载为外部表,如:
create table my_external_table (
A varchar2(10),
B varchar2(10),
C varchar2(4000),
D varchar2(4000),
E varchar2(10),
F varchar2(10)
)
organization external (
type oracle_loader
default directory my_directory
access parameters (
records delimited by newline
fields terminated by '|'
)
location ('my_file.dat')
);
蛮力地分两步提取嵌套分隔的数据,并将其插入真实表中:
insert into my_table (a, b,
c0101, c0102, c0103, c0201, c0202, c0203, c0301, c0302, c0303,
d0101, d0102, d0103, d0201, d0202, d0203, d0301, d0302, d0303,
e, f)
with t as (
select a, b,
regexp_replace(regexp_substr(c, '\[.*?\]', 1, 1), '[][]') as c01,
regexp_replace(regexp_substr(c, '\[.*?\]', 1, 2), '[][]') as c02,
regexp_replace(regexp_substr(c, '\[.*?\]', 1, 3), '[][]') as c03,
regexp_replace(regexp_substr(d, '\[.*?\]', 1, 1), '[][]') as d01,
regexp_replace(regexp_substr(d, '\[.*?\]', 1, 2), '[][]') as d02,
regexp_replace(regexp_substr(d, '\[.*?\]', 1, 3), '[][]') as d03,
e, f
from my_external_table
)
select a, b,
regexp_substr(c01, '[^~]+', 1, 1) as c0101,
regexp_substr(c01, '[^~]+', 1, 2) as c0102,
regexp_substr(c01, '[^~]+', 1, 3) as c0103,
regexp_substr(c02, '[^~]+', 1, 1) as c0201,
regexp_substr(c02, '[^~]+', 1, 2) as c0202,
regexp_substr(c02, '[^~]+', 1, 3) as c0203,
regexp_substr(c03, '[^~]+', 1, 1) as c0301,
regexp_substr(c03, '[^~]+', 1, 2) as c0302,
regexp_substr(c03, '[^~]+', 1, 3) as c0303,
regexp_substr(d01, '[^~]+', 1, 1) as d0101,
regexp_substr(d01, '[^~]+', 1, 2) as d0102,
regexp_substr(d01, '[^~]+', 1, 3) as d0103,
regexp_substr(d02, '[^~]+', 1, 1) as d0201,
regexp_substr(d02, '[^~]+', 1, 2) as d0202,
regexp_substr(d02, '[^~]+', 1, 3) as d0203,
regexp_substr(d03, '[^~]+', 1, 1) as d0301,
regexp_substr(d03, '[^~]+', 1, 2) as d0302,
regexp_substr(d03, '[^~]+', 1, 3) as d0303,
e, f
from t;
CTE拆分单独的C字段,然后主选择根据波浪号将每个字段拆分出来。我只向相同的空间显示了三个C / D级别,但您必须在CTE和主查询中重复这些行。这将使它成为一个非常长的声明。它涉及大量的剪切和粘贴,并且如果数字不完全正确则会引入错误;但是你可以从shell脚本(或动态SQL)生成语句,以避免这两个问题。
这也假设特定线路上的C / D字段的总长度不超过4k;如果它可能会使事情变得更复杂。
无论如何,对于你展示的数据,决赛桌会有(向那些非常不喜欢滚动的人道歉):
select * from my_table;
A B C0101 C0102 C0103 C0201 C0202 C0203 C0301 C0302 C0303 D0101 D0102 D0103 D0201 D0202 D0203 D0301 D0302 D0303 E F
---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
FieldA FieldB FieldC111 FieldC112 FieldC113 FieldC121 FieldC122 FieldC123 FieldD111 FieldD112 FieldD113 FieldD121 FieldD122 FieldD123 FieldE FieldF
FieldA FieldB FieldC111 FieldC112 FieldC113 FieldC121 FieldC122 FieldC123 FieldC131 FieldC132 FieldC133 FieldD111 FieldD112 FieldD113 FieldD121 FieldD122 FieldD123 FieldE FieldF
FieldA FieldB FieldC111 FieldC112 FieldC113 FieldC121 FieldC122 FieldC123 FieldC131 FieldC132 FieldC133 FieldD111 FieldD112 FieldD113 FieldD121 FieldD122 FieldD123 FieldD131 FieldD132 FieldD133 FieldE FieldF
FieldA FieldB FieldC111 FieldC112 FieldC113 FieldC121 FieldC122 FieldC123 FieldD111 FieldD112 FieldD113 FieldD121 FieldD122 FieldD123 FieldD131 FieldD132 FieldD133 FieldE FieldF
如果你想动态生成插入,你可以这样做,这只是重新创建上面的手册版本 - 但是对于10组C / D字段:
declare
stmt varchar2(32767);
begin
stmt := 'insert into my_table (a, b, ';
for i in 1..10 loop -- 10 sets of C/D records
for j in 1..3 loop -- 3 tilde-delimited values for each
stmt := stmt || 'c' || lpad(i, 2, '0') || lpad(j, 2, '0') || ', ';
stmt := stmt || 'd' || lpad(i, 2, '0') || lpad(j, 2, '0') || ', ';
end loop;
end loop;
stmt := stmt || 'e, f) with t as (select a, b, ';
for i in 1..10 loop -- 10 sets of C/D records
stmt := stmt || 'regexp_replace(regexp_substr(c, ''\[.*?\]'', 1, ' || i
|| '), ''[][]'') as c' || lpad(i, 2, '0') || ', ';
stmt := stmt || 'regexp_replace(regexp_substr(d, ''\[.*?\]'', 1, ' || i
|| '), ''[][]'') as d' || lpad(i, 2, '0') || ', ';
end loop;
stmt := stmt || 'e, f from my_external_table) select a, b, ';
for i in 1..10 loop -- 10 sets of C/D records
for j in 1..3 loop -- 3 tilde-delimited values for each
stmt := stmt || 'regexp_substr(c' || lpad(i, 2, '0')
|| ', ''[^~]+'', 1, ' || j || '), ';
stmt := stmt || 'regexp_substr(d' || lpad(i, 2, '0')
|| ', ''[^~]+'', 1, ' || j || '), ';
end loop;
end loop;
stmt := stmt || 'e, f from t';
-- uncomment to see/debug the actual statement being executed
-- dbms_output.put_line(stmt);
execute immediate stmt;
end;
/
运行它会在真实表格中创建相同的记录。