我有简单的任务要做。 1)我用XML生成Cognos报告,看起来像这样:
<?xml version="1.0" encoding="utf-8"?>
<dataset xmlns="http://developer.cognos.com/schemas/xmldata/1/" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance">
<!--
<dataset xmlns="http://developer.cognos.com/schemas/xmldata/1/" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:schemaLocation="http://developer.cognos.com/schemas/xmldata/1/ xmldata.xsd">
-->
<metadata>
<item name="OID" type="xs:string" length="32"/>
<item name="NAME" type="xs:string" length="522"/>
<item name="TYPE" type="xs:string" length="32"/>
<item name="O_STATUS" type="xs:string" length="52"/>
<item name="O_REGION" type="xs:string" length="52"/>
<item name="O_DEV_IN_COUNTRY" type="xs:string" length="12"/>
<item name="O_START" type="xs:date"/>
<item name="O_REQ_IMPL" type="xs:date"/>
<item name="O_B_INIT_APPR" type="xs:string" length="50"/>
<item name="O_B_FIN_APPR" type="xs:date"/>
<item name="O_REL_Y" type="xs:string" length="12"/>
<item name="O_REL_M" type="xs:string" length="12"/>
<item name="O_REL_T" type="xs:string" length="4"/>
<item name="O_SIZING" type="xs:date"/>
<item name="O_BPM" type="xs:string" length="592"/>
<item name="O_BS" type="xs:string" length="22"/>
<item name="O_MAINT_ASSESSOR" type="xs:string" length="22"/>
<item name="O_P_ASSIGN_MGR" type="xs:string" length="32"/>
<item name="O_FUNC_AREA" type="xs:string" length="102"/>
<item name="O_APS" type="xs:string" length="22"/>
<item name="O_INV_TYPE" type="xs:string" length="32"/>
<item name="O_INV_CAT" type="xs:string" length="92"/>
<item name="O_INV_SUB_CAT" type="xs:string" length="82"/>
<item name="O_PRIM_AREA_OF_IMPACT" type="xs:string" length="92"/>
<item name="O_MAND_REQ_TYP" type="xs:string" length="72"/>
<item name="O_M_COUNTRY" type="xs:string" length="42"/>
<item name="O_IMP_COUN_1" type="xs:string" length="42"/>
<item name="O_IMP_COUN_2" type="xs:string" length="42"/>
<item name="O_IMP_COUN_3" type="xs:string" length="42"/>
<item name="O_IMP_COUN_4" type="xs:string" length="42"/>
<item name="O_IMP_COUN_5" type="xs:string" length="42"/>
<item name="O_IMP_COUN_6" type="xs:string" length="42"/>
<item name="O_IMP_COUN_7" type="xs:string" length="42"/>
<item name="O_IMP_COUN_8" type="xs:string" length="32"/>
<item name="O_IMP_COUN_9" type="xs:string" length="42"/>
<item name="O_IMP_COUN_10" type="xs:string" length="32"/>
</metadata>
<data>
<row>
<value>X-1234567</value>
<value>SOME TEXT</value>
<value>Project</value>
<value>New</value>
<value>AAAA</value>
<value>Yes</value>
<value>2015-01-01</value>
<value>2015-02-01</value>
<value xs:nil="true" />
<value xs:nil="true" />
<value xs:nil="true" />
<value xs:nil="true" />
<value xs:nil="true" />
<value xs:nil="true" />
<value xs:nil="true" />
<value>AA12345</value>
<value xs:nil="true" />
<value xs:nil="true" />
<value>SOME TEXT</value>
<value>SOME TEXT</value>
<value>SOME TEXT</value>
<value>SOME TEXT</value>
<value>SOME TEXT</value>
<value>SOME TEXT</value>
<value xs:nil="true" />
<value>SOME TEXT</value>
<value>SOME TEXT</value>
<value xs:nil="true" />
<value xs:nil="true" />
<value xs:nil="true" />
<value xs:nil="true" />
<value xs:nil="true" />
<value xs:nil="true" />
<value xs:nil="true" />
<value xs:nil="true" />
<value xs:nil="true" />
</row>
<row>
...
</row>
</data>
</dataset>
2)此类文件将发送到映射为名为IMPDIR
的Oracle目录的文件共享3)我可以从SQL
访问此文件我想要做的是将此文件中的数据导入到一个表格中,该表格包含元数据中的字段,而记录将由节点中的数据填充。
我通过Stack和Goolgle进行搜索,我发现的大多数情况都与XML有关,这些XML具有可以通过路径区分的字段的清晰路径。
知道怎么分配这样的文件吗? 它自己的文件大约是10MB。
答案 0 :(得分:1)
您还没有说过如何访问该文件 - 作为外部表,您可以使用utl_file
读取,或作为CLOB或类似内容导入到真实表中。如果您可以将整个文件放入CLOB,那么您可以将其转换为XMLType,或者随时转换它。然后,您需要将通用布局转换为关系布局。
此示例假设您有一个名为t42
的表,其中有一个名为xml
的列,它是XMLType并包含您的文件。您可以将XMLTable与FLWOR XQuery表达式(read more in the documentation)一起使用,将每个通用“项目”节点转换为使用相应元数据条目命名的节点:
select x.*
from t42
cross join xmltable(
xmlnamespaces('http://www.w3.org/2001/XMLSchema-instance' as "xs",
default 'http://developer.cognos.com/schemas/xmldata/1/'),
'for $row in /dataset/data/row
return element row {
for $col_num in (1 to count(/dataset/metadata/item))
return element {/dataset/metadata/item[$col_num]/@name} {
$row/value[$col_num]/text()
}
}'
passing t42.xml
columns oid varchar2(32) path '/row/OID',
o_dev_in_country varchar2(12) path '/row/O_DEV_IN_COUNTRY',
o_start date path '/row/O_START'
) x;
OID O_DEV_IN_COU O_START
-------------------------------- ------------ ---------
X-1234567 Yes 01-JAN-15
X-1234568 No 14-NOV-15
我在您的示例中添加了第二个row
节点,以使结果稍微有用,我刚刚展示了三个示例列。由于您事先知道元数据,因此您只需要为所有列创建更多columns
条目以及相应的类型和路径,或者只为您感兴趣的列创建。{/ p>
XMLTable首先定义您使用的名称空间,其中一个名为"xs"
,另一个名称为默认名称。这样可以识别所有节点;虽然你真的不需要"xs"
来提取数据,但它仍然是一个内置的 - 在这种情况下你只需要默认值。然后,它会迭代数据中的每个row
,并构造一个新的row
元素。然后,对于每一行,它计算元数据项的数量,并为每个元素项创建一个“列”元素,以col_num
元元素项的name
属性命名,并使用col_num
行item
值。
您可以使用该结果插入真实表格insert into ... select x.oid, x.o_start from ...
。
如果您正在使用utl_file
读取数据,那么您可以在一个PL / SQL块中执行相同的操作,例如:
declare
l_clob clob;
begin
-- populate l_clob with utl_file
insert into your_real_table (oid, o_dev_in_country, o_start) -- add more columns
select oid, o_dev_in_country, o_start -- add more columns
from xmltable(
xmlnamespaces('http://www.w3.org/2001/XMLSchema-instance' as "xs",
default 'http://developer.cognos.com/schemas/xmldata/1/'),
'for $row in /dataset/data/row
return element row {
for $col_num in (1 to count(/dataset/metadata/item))
return element {/dataset/metadata/item[$col_num]/@name} {
$row/value[$col_num]/text()
}
}'
passing xmltype(l_clob)
columns oid varchar2(32) path '/row/OID',
o_dev_in_country varchar2(12) path '/row/O_DEV_IN_COUNTRY',
o_start date path '/row/O_START'
-- add more columns...
) x;
end;
/
这会将文件放入CLOB(怎么做是偏离主题的,但听起来你已经覆盖了那部分?),然后将CLOB直接传递给XMLTable表达式,并在{{1 }}
你也可以进行XLST转换,但这不是我已经涉足的内容。
您不需要完全动态的解决方案,但只是为了好玩,您可以通过从元数据中提取XMLType()
子句成员并使用上面显示的相同基本转换执行它来动态构建查询。你甚至可以进一步动态创建表格 - 当然不是我在运行时推荐的东西,但正如我所说,只是为了好玩......
columns
这将创建declare
l_clob clob;
l_sql varchar2(32767);
begin
-- populate l_clob with utl_file
-- dynamic create table statement from metadata
l_sql := 'create table t42 (';
for col in (
select *
from xmltable(
xmlnamespaces(default 'http://developer.cognos.com/schemas/xmldata/1/'),
'/dataset/metadata/item'
passing xmltype(l_clob)
columns position for ordinality,
column_name varchar2(30) path '@name',
data_type varchar2(30) path '@type',
data_length varchar2(30) path '@length'
) x) loop
l_sql := l_sql || case when col.position > 1 then ', ' end
|| col.column_name
|| ' ' || case col.data_type
when 'xs:string' then 'varchar2'
when 'xs:date' then 'date'
-- add any other data types used
end
|| case when col.data_type = 'xs:string'
then '(' || col.data_length || ')' end;
end loop;
l_sql := l_sql || ')';
dbms_output.put_line(l_sql);
execute immediate l_sql;
-- dynamic insert/select creating the `columns` clause from metadata
l_sql := q'!insert into t42 select *
from xmltable(
xmlnamespaces(default 'http://developer.cognos.com/schemas/xmldata/1/'),
'for $row in /dataset/data/row
return element row {
for $col_num in (1 to count(/dataset/metadata/item))
return element column {
attribute name {/dataset/metadata/item[$col_num]/@name},
$row/value[$col_num]/text()
}
}'
passing xmltype(:l_clob)
columns !';
for col in (
select *
from xmltable(
xmlnamespaces(default 'http://developer.cognos.com/schemas/xmldata/1/'),
'/dataset/metadata/item'
passing xmltype(l_clob)
columns position for ordinality,
column_name varchar2(30) path '@name',
data_type varchar2(30) path '@type',
data_length varchar2(30) path '@length'
) x) loop
l_sql := l_sql || case when col.position > 1 then ', ' end
|| col.column_name
|| ' ' || case col.data_type
when 'xs:string' then 'varchar2'
when 'xs:date' then 'date'
-- add any other data types used
end
|| case when col.data_type = 'xs:string'
then '(' || col.data_length || ')' end
|| q'! path '//column[@name="!' || col.column_name || q'!"]'!';
end loop;
l_sql := l_sql || ')';
dbms_output.put_line(l_sql);
execute immediate l_sql using l_clob;
end;
/
表,然后从数据行填充它。它是一个单独的插入,因此提取每个数据行并逐个插入它们的效率更高。
您还可以将列名和数据类型读入本地集合中,这样您就不必将元数据部分循环两次,这样可以使其更具可读性和可维护性,但我认为它不会对绩效有很大影响。
答案 1 :(得分:0)
通用方法是使用动态SQL,因为列名是XML的一部分。下面的查询选择每行的列名和值,并返回存储数据的insert语句。
我认为XML位于TST
列
xml
中
WITH colnames AS
(SELECT pos,
column_name
FROM tst t,
XMLTable( XMLNAMESPACES (DEFAULT 'http://developer.cognos.com/schemas/xmldata/1/' ), 'for $i in /dataset/metadata/item
return $i' passing (t.xml) columns
pos FOR ORDINALITY,
column_name VARCHAR2(30) path '@name' ) x
),
vals AS
(SELECT vals ,
rn
FROM tst t,
XMLTable( XMLNAMESPACES (DEFAULT 'http://developer.cognos.com/schemas/xmldata/1/' ), 'for $i in /dataset/data/row
return $i' passing (t.xml) columns
rn FOR ORDINALITY,
vals XMLType path 'value' ) x
) ,
vals2 AS
(SELECT rn,
pos,
val
FROM vals,
XMLTable( XMLNAMESPACES (DEFAULT 'http://developer.cognos.com/schemas/xmldata/1/' ), 'for $i in /value
return $i' passing (vals.vals) columns
pos FOR ORDINALITY,
val VARCHAR2(4000) path 'text()' ) x
)
SELECT rn,
'insert into TAB ('
||
(SELECT listagg(column_name,', ') within GROUP (ORDER BY pos) FROM colnames
)
||') values ('
/* quote and escape the string */
|| listagg(case when val is NULL then 'null' else ''''||replace(val,'''','''''')||'''' end,', ') within GROUP (ORDER BY pos)
||');' AS insert_txt
FROM vals2
GROUP BY rn
;
一个示例输出 - 使用execute immediate
INSERT INTO TAB
(
OID,
NAME,
TYPE,
O_STATUS,
O_REGION,
O_DEV_IN_COUNTRY,
O_START,
O_REQ_IMPL,
O_B_INIT_APPR,
O_B_FIN_APPR,
O_REL_Y,
O_REL_M,
O_REL_T,
O_SIZING,
O_BPM,
O_BS,
O_MAINT_ASSESSOR,
O_P_ASSIGN_MGR,
O_FUNC_AREA,
O_APS,
O_INV_TYPE,
O_INV_CAT,
O_INV_SUB_CAT,
O_PRIM_AREA_OF_IMPACT,
O_MAND_REQ_TYP,
O_M_COUNTRY,
O_IMP_COUN_1,
O_IMP_COUN_2,
O_IMP_COUN_3,
O_IMP_COUN_4,
O_IMP_COUN_5,
O_IMP_COUN_6,
O_IMP_COUN_7,
O_IMP_COUN_8,
O_IMP_COUN_9,
O_IMP_COUN_10
)
VALUES
(
'X-1234567',
'SOME TEXT',
'Project',
'New',
'AAAA',
'Yes',
'2015-01-01',
'2015-02-01',
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
'AA12345',
NULL,
NULL,
'SOME TEXT',
'SOME TEXT',
'SOME TEXT',
'SOME TEXT',
'SOME TEXT',
'SOME TEXT',
NULL,
'SOME TEXT',
'SOME TEXT',
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
);
请注意,对于大量列,动态选择会更好地扩展,但不会处理可能需要的类型转换,这可以在静态中更好地完成选择。