Oracle Query - 使用逗号分隔的数据加入

时间:2017-07-24 10:50:11

标签: string oracle oracle12c

表名:crm_mrdetails

 id | mr_name | me_email     | mr_mobile  | mr_doctor|
 -----------------------------------------------------
 1  | John    |abc@gmail.com | 1234555555 | ,1,2,3   |

表名:crm_mr_doctor

id | dr_name     | specialization|  
----------------------------------  
1  | Abhishek    | cordiologist  |
2  | Krishnan    | Physician     |
3  | Krishnan    | Nurse         |

mrdetails.mr_doctor中的连接值是mr_doctor.id的外键。我需要加入它们来产生这样的输出:

 id | mr_name | me_email     |Doctor_Specialization|
 -------------------------------------------------
 1  | John    |abc@gmail.com |cordiologist,Physician,Nurse|

我是Oracle新手,我正在使用Oracle 12C。任何帮助非常感谢。

5 个答案:

答案 0 :(得分:3)

首先,我们必须承认这是一个糟糕的数据模型。 mr_doctor列违反了First Normal Form。这不是一些深奥的理论观点。不在1NF中意味着我们必须编写更多代码来查找键的含义,而不是使用标准的SQL连接语法。这也意味着我们不能依赖包含有效ID的列:mr_doctor可以包含任何旧的废话,我们必须编写一个可以处理它的查询。

此解决方案使用正则表达式将mr_doctor列拆分为ID,然后将其连接到mr_doctor表;连接specialization列以生成所需的输出。

select mrdet.id, 
       mrdet.mr_name,
       mrdet.me_email,
       listagg(mrdoc.specialization, ',') 
                     within group (order by mrdoc.specialization) as doctor_specialization
from mr_details mrdet
     join (
        select distinct id, 
               regexp_substr(mr_doctor, '(,?)([0-9]+)(,?)', 1, level, null, 2) as dr_id
        from mr_details 
        connect by level <= regexp_count(mr_doctor, '(,?)([0-9]+)')
       ) as mrids
    on mrids.id = mrdet.id
    left outer join mr_doctor mrdoc
       on mrids.dr_id = mr_doc.id
group by mrdet.id, 
       mrdet.mr_name,
       mrdet.me_email
/

尽管数据模型很脆弱,但这种解决方案具有相当的弹性。如果字符串包含太多逗号或空格,它将返回结果。它将忽略字母或其他不是数字的值。如果提取的数字与mr_doctor表中的ID不匹配,则不会投掷。显然,由于这些原因,结果是不值得信任的,但这是一个简单的数据模型价格的一部分。

  

您能解释一下:(,?)([0-9]+)(,?)

模式匹配零或一个逗号,后跟一个或多个数字,后跟零或一个逗号。也许匹配模式中的(,?)并非严格必要。但是,如果没有它们,此字符串2 3 4将匹配与此字符串2,3,4相同的三个ID。也许这是正确的,也许它不是。当外键存储在CSV列中而不是通过适当的约束强制执行时,会纠正&#39;甚至意味着?

答案 1 :(得分:2)

您必须将mr_doctor列中的数据拆分为行,加入表crm_mrdoctor,然后使用listagg()。 如何拆分数据? Splitting string into multiple rows in Oracle

select t.id, max(mr_name) mr_name, 
       listagg(specialization, ', ') within group (order by rn) specs
  from (
    select id, mr_name, levels.column_value rn, 
           trim(regexp_substr(mr_doctor, '[^,]+', 1, levels.column_value)) as did
      from crm_mrdetails t,
           table(cast(multiset(select level 
                                 from dual 
                                 connect by level <= 
                                     length(regexp_replace(t.mr_doctor, '[^,]+')) + 1) 
                      as sys.odcinumberlist)) levels) t
  left join crm_mr_doctor d on t.did = d.id
  group by t.id

演示和结果:

with crm_mrdetails (id, mr_name, mr_doctor) as (
    select 1, 'John', ',1,2,3'   from dual union all
    select 2, 'Anne', ',4,2,6,5' from dual union all
    select 3, 'Dave', ',4'       from dual),
crm_mr_doctor (id, dr_name, specialization) as (
    select 1, 'Abhishek', 'cordiologist' from dual union all
    select 2, 'Krishnan', 'Physician'    from dual union all
    select 3, 'Krishnan', 'Nurse'        from dual union all
    select 4, 'Krishnan', 'Onkologist'   from dual union all
    select 5, 'Krishnan', 'Surgeon'      from dual union all
    select 6, 'Krishnan', 'Nurse'        from dual
    )
select t.id, max(mr_name) mr_name, 
       listagg(specialization, ', ') within group (order by rn) specs
  from (
    select id, mr_name, levels.column_value rn, 
           trim(regexp_substr(mr_doctor, '[^,]+', 1, levels.column_value)) as did
      from crm_mrdetails t,
           table(cast(multiset(select level 
                                 from dual 
                                 connect by level <= 
                                     length(regexp_replace(t.mr_doctor, '[^,]+')) + 1) 
                      as sys.odcinumberlist)) levels) t
  left join crm_mr_doctor d on t.did = d.id
  group by t.id

输出:

    ID MR_NAME SPECS
------ ------- -------------------------------------
     1 John    cordiologist, Physician, Nurse
     2 Anne    Onkologist, Physician, Nurse, Surgeon
     3 Dave    Onkologist

答案 2 :(得分:0)

请根据您的要求更改列名。

CREATE OR REPLACE Function ReplaceSpec
    (String_Inside IN Varchar2)
    Return Varchar2 Is

        outputString Varchar2(5000);
        tempOutputString crm_doc.specialization%TYPE;

    Begin

        FOR i in 1..(LENGTH(String_Inside)-LENGTH(REPLACE(String_Inside,',',''))+1)
        LOOP

            Select specialization into tempOutputString From crm_doc 
            Where id = PARSING_STRING(String_Inside,i);

            If i != 1 Then
                outputString := outputString || ',';
            end if;
            outputString := outputString || tempOutputString;

        END LOOP;

        Return outputString;


    End;
/

Parsing_String函数帮助分割逗号分隔值。

CREATE OR REPLACE Function PARSING_STRING
(String_Inside IN Varchar2, Position_No IN Number) 
Return Varchar2 Is
    OurEnd   Number; Beginn Number;
Begin

If Position_No < 1 Then 
Return Null; 
End If;

OurEnd := Instr(String_Inside, ',', 1, Position_No);

If OurEnd = 0 Then
    OurEnd := Length(String_Inside) + 1;
End If;

If Position_No = 1 Then
    Beginn := 1;
Else
    Beginn := Instr(String_Inside, ',', 1, Position_No-1) + 1;
End If;

Return Substr(String_Inside, Beginn, OurEnd-Beginn);

End;
/

请注意,我只提供了一个基本功能来获取输出。您可能需要添加一些例外等。

Eg. When the doc_id [mr_doctor] is empty, what to do.

用法

select t1.*,ReplaceSpec(doc_id) from crm_details t1

如果您的mr_doctor数据始终以逗号使用开头:

Select t1.*,ReplaceSpec(Substr(doc_id,2)) from crm_details t1

答案 3 :(得分:0)

您可以使用递归子查询和简单的字符串函数(这可能比使用正则表达式和相关的分层查询要快):

Oracle设置

dropdown-menu show

查询

CREATE TABLE crm_mrdetails (id, mr_name, mr_doctor) as
    select 1, 'John', ',1,2,3'   from dual union all
    select 2, 'Anne', ',4,2,6,5' from dual union all
    select 3, 'Dave', ',4'       from dual;

CREATE TABLE crm_mr_doctor (id, dr_name, specialization) as
    select 1, 'Abhishek', 'cordiologist' from dual union all
    select 2, 'Krishnan', 'Physician'    from dual union all
    select 3, 'Krishnan', 'Nurse'        from dual union all
    select 4, 'Krishnan', 'Onkologist'   from dual union all
    select 5, 'Krishnan', 'Surgeon'      from dual union all
    select 6, 'Krishnan', 'Nurse'        from dual;

输出

ID | MR_NAME | DOCTOR_SPECIALIZATION             
-: | :------ | :---------------------------------
 1 | John    | cordiologist,Physician,Nurse      
 2 | Anne    | Onkologist,Physician,Nurse,Surgeon
 3 | Dave    | Onkologist                        

db <>提琴here

答案 4 :(得分:0)

请通过https://oracle-base.com/articles/misc/string-aggregation-techniques 字符串聚合技术

SELECT deptno,        LTRIM(最大值(SYS_CONNECT_BY_PATH(ename,','))        保持(DENE_RANK最后一笔订单的当前金额),',')AS员工 从(选择deptno,                名字,                发生ROW_NUMBER()个(按deptno排序的订单或按ename排序的),                ROW_NUMBER()OVER(按deptno排序或按ename排序)-1 AS上一个         来自emp) GROUP BY DEPTNO CONNECT BY prev =优先级和deptno =优先级 从curr = 1开始

listagg和wm_concat也可以用作其他人使用的