【问题标题】:Converting coma separated values in multiple column to rows将多列中的逗号分隔值转换为行
【发布时间】:2016-10-21 01:44:16
【问题描述】:

我正在尝试从表中创建一个视图,该视图会将多列中的逗号分隔值转换为 Oracle 中的行。

我已经成功地为一栏做到了这一点。但我无法为两三列做到这一点。

我使用以下脚本成功运行了一列。

Create VIEW MULTITESET AS 
SELECT rownum AS ID1,Tagging.COMMENTS,Tagging.category,Tagging.STATUS,
        trim(regexp_substr(Tagging.OBJ_ID, '[^,]+', 1, lines.column_value)) OBJ_ID
    FROM Tagging,
      TABLE (CAST (MULTISET
      (SELECT LEVEL FROM dual
              CONNECT BY instr(Tagging.OBJ_ID, ',', 1, LEVEL - 1) > 0
      ) AS sys.odciNumberList ) ) lines
    ORDER BY id, lines.column_value

现在我需要对名为 OBJname 的第二列执行相同的操作,就像对列 OBJ_ID 一样。

所以我尝试了一些愚蠢的方法,例如以下不起作用。

Create VIEW MULTITESET AS 
SELECT rownum AS ID1,Tagging.COMMENTS,Tagging.category,Tagging.STATUS,
        trim(regexp_substr(Tagging.OBJ_ID, '[^,]+', 1, lines.column_value)) OBJ_ID
    FROM Tagging,
      TABLE (CAST (MULTISET
      (SELECT LEVEL FROM dual
              CONNECT BY instr(Tagging.OBJ_ID, ',', 1, LEVEL - 1) > 0
      ) AS sys.odciNumberList ) ) lines
    ORDER BY id, lines.column_value ,
     trim(regexp_substr(Tagging.OBJname , '[^,]+', 1, lines.column_value)) OBJname 
    FROM Tagging,
      TABLE (CAST (MULTISET
      (SELECT LEVEL FROM dual
              CONNECT BY instr(Tagging.OBJname , ',', 1, LEVEL - 1) > 0
      ) AS sys.odciNumberList ) ) lines
    ORDER BY id, lines.column_value

简而言之,我希望将下图中的 table-A 转换为 table-B。我该怎么做?

我的实际视图查询是:

SELECT rownum AS TRACKID2, LEAPFROG_TAGGING.ID, LEAPFROG_TAGGING.CREATED_DATE,
  LEAPFROG_TAGGING.CREATED_BY, LEAPFROG_TAGGING.COMMENTS,
  leapfrog_tagging.category, LEAPFROG_TAGGING.STATUS, LEAPFROG_TAGGING.OBJ_NAME,
  trim(regexp_substr(LEAPFROG_TAGGING.OBJ_ID, '[^,]+', 1, lines.column_value)) OBJ_ID
FROM LEAPFROG_TAGGING,
TABLE (CAST (MULTISET (
    SELECT LEVEL FROM dual
    CONNECT BY instr(LEAPFROG_TAGGING.OBJ_ID, ',', 1, LEVEL - 1) > 0
  ) AS sys.odciNumberList ) ) lines
ORDER BY id, lines.column_value;

【问题讨论】:

    标签: sql oracle oracle11g


    【解决方案1】:

    假设逗号分隔值的数量始终在两列中匹配(因此每个 ID 恰好有一个名称),并且您使用的是 11gR2,您可以使用recursive subquery factoring:

    create view multiteset as
    with t (obj_id, obj_name, status, other_columns, rn, orig_obj_id, orig_obj_name) as (
      select regexp_substr(obj_id, '[^,]+', 1, 1),
        regexp_substr(obj_name, '[^,]+', 1, 1),
        status, other_columns, 1, obj_id, obj_name
      from tagging
      union all
      select regexp_substr(orig_obj_id, '[^,]+', 1, rn + 1),
        regexp_substr(orig_obj_name, '[^,]+', 1, rn + 1),
        status, other_columns, rn + 1, orig_obj_id, orig_obj_name
      from t
      where rn < regexp_count(orig_obj_id, '[^,]+')
    )
    select obj_id, obj_name, status, other_columns
    from t;
    
    select * from multiteset;
    
    OBJ_ID                       OBJ_NAME STATUS OTHER_
    ---------------------------- -------- ------ ------
    1                            a        open   value1
    1                            a        closed value2
    4                            hj       na     value3
    2                            s        open   value1
    2                            s        closed value2
    5                            hj       na     value3
    3                            d        open   value1
    6                            hj       na     value3
    4                            f        open   value1
    7                            hj       na     value3
    
    10 rows selected. 
    

    您的问题中的列名有点不一致,因此您必须根据真实姓名进行调整。

    锚成员从每个列表中获取第一个元素,并保留原始列表的副本,以及查看我们在哪个元素上的计数器(从 1 开始)。

    然后递归成员在每个列表中查找下一个 (rn +) 元素,除了计数器之外,其余的值保持不变。

    然后,针对 CTE 的最终查询仅获取生成的值,忽略 CTE 正在跟踪的临时值(rn、orig_obj_id 等)

    根据您在 cmets 中添加的新原始视图定义,您可能真的想要:

    create view multiteset as
    with t (id, created_date, created_by, comments, category, status,
      obj_name, obj_id, rn, orig_obj_name, orig_obj_id)
    as (
      select id, created_date, created_by, comments, category, status,
        regexp_substr(obj_name, '[^,]+', 1, 1),
        regexp_substr(obj_id, '[^,]+', 1, 1),
        1, obj_name, obj_id
      from leapfrog_tagging
      union all
      select id, created_date, created_by, comments, category, status,
        regexp_substr(orig_obj_name, '[^,]+', 1, rn + 1),
        regexp_substr(orig_obj_id, '[^,]+', 1, rn + 1),
        rn + 1, orig_obj_name, orig_obj_id
      from t
      where rn < regexp_count(orig_obj_id, '[^,]+')
    )
    select id, created_date, created_by, comments, category, status,
      obj_name, obj_id
    from t;
    

    还要注意没有order by 子句;这在视图定义中没有真正意义,应该在您查询视图时应用:

    select * from multiteset order by id, obj_id;
    

    如果您想保留位置标记,以便视图结果也可以按此排序,则在最终选择列表中包含 rn(或更有意义的名称!),例如:

    ...
    select id, rn as trackid2, created_date, created_by, comments, category, status,
      obj_name, obj_id
    from t;
    

    这会让你:

    select * from multiteset order by id, trackid2;
    

    【讨论】:

    • 感谢亚历克斯。我试过这个。但我收到以下错误“02000.00000 - “缺少 %s 关键字””
    • 我的实际视图是 CREATE VIEW AS SELECT rownum AS TRACKID2, LEAPFROG_TAGGING.ID, LEAPFROG_TAGGING.CREATED_DATE, LEAPFROG_TAGGING.CREATED_BY, LEAPFROG_TAGGING.COMMENTS, jumpfrog_tagging.category, LEAPFROG_TAGGING.STATUS, LEAPFROG_NAME,trim( regexp_substr(LEAPFROG_TAGGING.OBJ_ID, '[^,]+', 1, lines.column_value)) OBJ_ID FROM LEAPFROG_TAGGING, TABLE (CAST (MULTISET (SELECT LEVEL FROM dual CONNECT BY instr(LEAPFROG_TAGGING.OBJ_ID, ',', 1, LEVEL - 1) > 0 ) AS sys.odciNumberList ) ) 行 ORDER BY id,lines.column_value;
    • CREATE VIEW AS SELECT 缺少新视图的名称。但是请不要在 cmets 中转储代码,它真的很难阅读。如果相关,请编辑问题...该代码是否获得缺少的关键字,或者在我的答案中运行代码?您可能不得不对其进行大量修改。
    • @Juliekaren - 我已经根据您在评论中的实际视图更新了答案。如果仍然出现错误,请显示整个错误堆栈,这将提供更多上下文,包括行号。
    • 哦..对不起,实际上我修剪了评论以适应它。我是第一次使用它,所以不知道再次编辑它。
    猜你喜欢
    • 1970-01-01
    • 2012-12-02
    • 2021-09-06
    • 1970-01-01
    • 2020-04-07
    • 1970-01-01
    • 1970-01-01
    • 2021-10-21
    • 1970-01-01
    相关资源
    最近更新 更多