如何将多列中的数据分离并解析成单独的行(Oracle)
Posted
技术标签:
【中文标题】如何将多列中的数据分离并解析成单独的行(Oracle)【英文标题】:How do I separate and parse out data from multiple columns into separate rows (Oracle) 【发布时间】:2017-10-31 16:23:03 【问题描述】:我在每列和每行中有多个值由逗号分隔的列。我试图将它们分成单独的行。如果其中一个值为空值(如下所示),只要该特定行的其他值之一仍然存在,我仍将包含空值。
我得到了什么
First_Name (John, ,Phil)
Last_Name (Smith,No, )
Location (CA,GA,NY)
我想要什么
(John, Smith, CA)
( , No, GA)
(Phil, ,NY)
我已经尝试使用 regexp_substr 方法,但它没有返回任何在上面列出的 3 列中的任何一列中具有空值的行。
【问题讨论】:
显示您尝试过的查询。 选择 trim(regexp_substr(first_name,'[^,]+', 1, 1)) first_name, trim(regexp_substr(last_name,'[^,]+',1, 2)) 最后,name, trim(regexp_substr(location,'[^,]+', 1, 3)) location from table_name connect by regexp_substr(first_name,'[^,]+', 1, 1 is not null 那么,在First_Name
输入中,逗号之间是什么?它看起来像两个空格。理想情况下,如果名字“缺失”,逗号之间应该有 nothing,甚至没有 one 空格。 'John,,Phil'
。同样,在Last_Name
中,第二个逗号应该在字符串的末尾(在末尾标记一个 NULL),它后面不应该有空格。字符串真的是这样的吗?
【参考方案1】:
with
inputs ( id, first_name, last_name, location ) as (
select 101, 'John,,Phil' , 'Smith,No,' , 'CA,GA,NY' from dual union all
select 102, 'Jo,Al,Ed,Li', 'Ng,Tso,,Roth', ',ZZ,,BB' from dual
)
-- End of simulated inputs (for testing only, not part of the solution).
-- SQL query begins BELOW THIS LINE. Use your actual table and column names.
select id,
regexp_substr(first_name, '([^,]*)(,|$)', 1, level, null, 1) as first_name,
regexp_substr(last_name , '([^,]*)(,|$)', 1, level, null, 1) as last_name,
regexp_substr(location , '([^,]*)(,|$)', 1, level, null, 1) as location
from inputs
connect by level <= regexp_count(first_name, ',') + 1
and prior id = id
and prior sys_guid() is not null
;
ID FIRST_NAME LAST_NAME LOCATION
---- ----------- ------------ --------
101 John Smith CA
101 No GA
101 Phil NY
102 Jo Ng
102 Al Tso ZZ
102 Ed
102 Li Roth BB
【讨论】:
如果我们不插入数据而是从“暂存”表中逐列选择它们,解决方案会改变吗? @icerabbit - 我不明白这个问题。我提供的解决方案没有插入数据。你是说 WITH 子句吗?我非常清楚地标记它仅用于测试目的;您只需要select id, ...
中的代码,但您必须使用暂存表的名称和其中的列。或者你问如何存储这个查询的结果?如果是这样,请在此 SELECT 查询中使用 INSERT 语句。
抱歉,我误读了您的解决方案,并将您的解决方案与另一个解决方案混淆了。如果我想用回车代替逗号,我该怎么做?有专门的代码吗?
@icerabbit - 回车的代码是chr(13)
。但又一次,我不明白。数据是否在单列文本中,但文本本身(虽然从外部视图看是“单个值”)实际上延伸了几行,每行一个值?那里要非常小心;如果输入来自 Unix/Linux,那么只有换行符(不是回车符!) - 那是 chr(10)
,但如果它来自 Windows,它是回车符和换行符。但是,您使用什么来进行导入?所有这些都应该在导入数据时处理,而不是事后处理。
所以看起来给我的数据已经加载到表中了。发生的情况是,对于每个唯一标识符,我都有几个名字、姓氏和位置,它们都由一些我不确定的字符分隔。我可以在单个列中收到的 first_names 示例是:CarlosMonta【参考方案2】:
你可以试试这样的。
SET SERVEROUTPUT ON;
DECLARE
TYPE etype IS TABLE OF VARCHAR2(100);
erec etype;
BEGIN
for rec IN ( SELECT first_name,last_name,location FROM Table1 )
LOOP
WITH fname
AS (SELECT LEVEL lvl,
REGEXP_SUBSTR(rec.first_name, '[^,]+', 1, LEVEL)First_name
FROM DUAL
CONNECT BY REGEXP_SUBSTR(rec.first_name, '[^,]+', 1, LEVEL) IS NOT NULL),
lname
AS (SELECT LEVEL lvl,
REGEXP_SUBSTR(rec.last_name, '[^,]+', 1, LEVEL)Last_Name
FROM DUAL
CONNECT BY REGEXP_SUBSTR(rec.last_name, '[^,]+', 1, LEVEL) IS NOT NULL),
loc
AS (SELECT LEVEL lvl,
REGEXP_SUBSTR(rec.location, '[^,]+', 1, LEVEL)Location
FROM DUAL
CONNECT BY REGEXP_SUBSTR(rec.location, '[^,]+', 1, LEVEL) IS NOT NULL)
SELECT first_name
||','
|| last_name
||','
|| location BULK COLLECT INTO erec
FROM fname fn
FULL OUTER join lname ln
ON fn.lvl = ln.lvl
FULL OUTER join loc lo
ON ln.lvl = lo.lvl;
FOR i IN 1..erec.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE(erec(i));
END LOOP;
END LOOP;
END;
/
【讨论】:
当我将它用于更大的数据集时,我会返回多个不应该相互关联的名字、姓氏等(可能是由于完全外部连接? ) 例如我现在要返回 : (John, Smith, CA) (John, Smith, NY) (John, Smith, LA), etc. 不,它适用于您的原始问题。你修改了问题。 太棒了,那么我该如何处理更大的数据集呢? 欢迎。如果对您有用,请点赞并标记为最佳答案。谢谢! 假设我在我的设计中添加了一个唯一标识符(例如 ID),这会改变什么吗?这对性能有帮助吗?您不必回答,因为这会使原始问题进一步复杂化,但认为问起来不会有什么坏处。无论哪种方式,您都会得到我认为的“最佳答案”以上是关于如何将多列中的数据分离并解析成单独的行(Oracle)的主要内容,如果未能解决你的问题,请参考以下文章