如何将逗号分隔值放入oracle中的列
Posted
技术标签:
【中文标题】如何将逗号分隔值放入oracle中的列【英文标题】:How to put comma separated values to a column in oracle 【发布时间】:2018-04-07 08:20:34 【问题描述】:我有一个 JSON 响应,处理响应后我的输出如下所示:
column_variable := 'col1,col2,col3';
data_clob :=
"2017-10-14,abc,1,
2019-10-13,abc,12,
2019-10-12,abc,,
"
;
由于原始响应包含 new line
的转义字符,data_clob
也已相应转换。
如何在 oracle 表中转换这个逗号分隔值:
我的输出应该是这样的:
col1 col2 col3
2017-10-14 abc 1
2019-10-13 abc 12
2019-10-12 abc null
我正在查看类似的问题,但我不想使用REGEXP_SUBSTR
,因为我不知道我会在回复中获得多少列。
例如:column_variable
可能有 'col1,col2,col3,col4,col5,col6'
;
我正在使用oracle 12.1.0.2.0
请帮忙!
【问题讨论】:
能否在问题中显示原始 JSON 响应?也许直接从 JSON 解析数据会更容易? 嗨 Krokodilko ..刚刚添加了有问题的原始 JSON 响应 这个 JSON 结构不好,如果它看起来像这样会更好更容易:data: [ "col1":"2017-10-14","col2":"abc","col3":1, "col1":"2019-10-13","col2":"abc","col3":12, "col1":"2019-10-12","col2":"abc" ]
。也许这个 API 的作者可以改变响应的格式?
是的,我同意它的结构不好,但我无法控制它
所以,您希望我们简单地编写一个查询/过程来神奇地 转换您的数据并为您提供一个数据库Table
,而无需事先了解数据类型、数量表将包含的列等?
【参考方案1】:
使用Polymorphic Table Functions
(Oracle 18c) 可以很简单地实现它:
Dynamic CSV to Columns Converter: Polymorphic Table Function Example:
create or replace package csv_pkg as
/* The describe function defines the new columns */
function describe (
tab in out dbms_tf.table_t,
col_names varchar2
) return dbms_tf.describe_t;
/* Fetch_rows sets the values for the new columns */
procedure fetch_rows (col_names varchar2);
end csv_pkg;
和身体:
create or replace package body csv_pkg as
function describe(
tab in out dbms_tf.table_t,
col_names varchar2
)
return dbms_tf.describe_t as
new_cols dbms_tf.columns_new_t;
col_id pls_integer := 2;
begin
/* Enable the source colun for reading */
tab.column(1).pass_through := FALSE;
tab.column(1).for_read := TRUE;
new_cols(1) := tab.column(1).description;
/* Extract the column names from the header string,
creating a new column for each
*/
for j in 1 .. ( length(col_names) - length(replace(col_names,',')) ) + 1 loop
new_cols(col_id) := dbms_tf.column_metadata_t(
name=>regexp_substr(col_names, '[^,]+', 1, j),--'c'||j,
type=>dbms_tf.type_varchar2
);
col_id := col_id + 1;
end loop;
return dbms_tf.describe_t( new_columns => new_cols );
end;
procedure fetch_rows (col_names varchar2) as
rowset dbms_tf.row_set_t;
row_count pls_integer;
begin
/* read the input data set */
dbms_tf.get_row_set(rowset, row_count => row_count);
/* Loop through the input rows... */
for i in 1 .. row_count loop
/* ...and the defined columns, extracting the relevant value
start from 2 to skip the input string
*/
for j in 2 .. ( length(col_names) - length(replace(col_names,',')) ) + 2 loop
rowset(j).tab_varchar2(i) :=
regexp_substr(rowset(1).tab_varchar2(i), '[^,]+', 1, j - 1);
end loop;
end loop;
/* Output the new columns and their values */
dbms_tf.put_row_set(rowset);
end;
end csv_pkg;
--function
create or replace function csv_to_columns(
tab table, col_names varchar2
) return table pipelined row polymorphic using csv_pkg;
然后你就简单地通过了:
select *
from csv_to_columns( data_clob, column_variable );
【讨论】:
嗨 lad2025 ,感谢您的回答!你知道是否有类似的东西可用于 oracle 12.1.0.2.0 @joe 也许你需要写一个自定义的。 甲骨文 18c?欢迎时间旅行者! ;-) @DavidFaber 是的,Oracle 18c。未来就是现在:)【参考方案2】:对于低于 18 和 12 的 Oracle 版本,这是一种可能的解决方案,不确定...这并不完美,并且会根据您提供的数据在最后创建一个空列 - 额外的空格、逗号等...这个也可以在“SELECT”和输出的第一列之间创建一个空格。所有这些都可以稍后手动删除或使用更多编码删除。我希望这至少在某些方面有所帮助:
SELECT 'SELECT '''||REPLACE(str, chr(10), ''' FROM dual'||chr(10)||'UNION ALL'||chr(10)||'SELECT ''')||''' FROM dual' str
FROM
(
SELECT TRIM(REPLACE(str, ',', ''''||', ''')) str FROM
(
SELECT TRIM(BOTH '"' FROM
'"2017-10-14,abc,1,
2019-10-13,abc,12,
2019-10-12,abc,,"') AS str FROM dual
)
)
/
这将构建可以手动或使用动态 SQL 清理和执行的 select 语句:
SELECT '2017-10-14' col, 'abc' col, '1' col, '' FROM dual
UNION ALL
SELECT '2019-10-13' col, 'abc' col, '12' col, '' FROM dual
UNION ALL
SELECT '2019-10-12' col, 'abc' col, '' col, '' FROM dual
上述select语句的输出:
COL COL_1 COL_2
2017-10-14 abc 1
2019-10-13 abc 12
2019-10-12 abc null
【讨论】:
以上是关于如何将逗号分隔值放入oracle中的列的主要内容,如果未能解决你的问题,请参考以下文章