Oracle 12:加入以逗号分隔的列表?

时间:2018-01-18 12:27:12

标签: sql oracle

假设我有一个名为'公司'的视图,我无法修改:

+------------+--------------------+--------+--------+------------------------+
| company_id | company_name       | ceo    | cfo    | legal_contacts         |
+------------+--------------------+--------+--------+------------------------+
| 1          | johnson and son    | pid111 | pid333 | pid444, pid567, pid999 |
| 2          | Pepperville apples | pid777 |        | pid345                 |
| 3          | Cats LTD           | pid123 | pid321 |                        |
+------------+--------------------+--------+--------+------------------------+ 

还有一个名为'contacts'的视图:

+-----------+-------+----------------------+
| person_id | name  | email                |
+-----------+-------+----------------------+
| pid111    | john  | john@gmail.com       |
| pid333    | steve | funkylover@mail.com  |
| pid444    | mary  | mar123@ymail.com     |
| pid999    | joe   | joe.bloggs@gmail.com |
| pid777    | louis |                      |
| pid345    | carol | carol@carolssite.com |
| pid321    | ellen | ellen.deg@gmail.com  |
+-----------+-------+----------------------+

最终目标是让我编写一个交叉引用人员ID并显示电子邮件和公司的查询:

+---------+----------------+-----------------------------------------+
| company | ceo            | legal_contacts                          |
+---------+----------------+-----------------------------------------+
| 1       | john@gmail.com | mary123@ymail.com, joe.bloggs@gmail.com |
| 2       |                | carol@carossite.com                     |
| 3       |                |                                         |
+---------+----------------+-----------------------------------------+

在没有编写函数或进程的情况下,我有没有办法在查询中加入或处理这个以逗号分隔的标识符列表?

您可以假设“合法联系人”最多包含25个标识符,始终采用相同格式,始终以逗号分隔

4 个答案:

答案 0 :(得分:1)

这是一种糟糕的数据格式,但您似乎知道这一点。这是一种方法:

select c.company, c.ceo,
       listagg(co.email, ', ') within group (order by co.person_id) as emails
from companies c join
     contacts co
     on ', ' || legal_contacts || ', ' like '%, ' || co.person_id || ',%'
group by c.company, c.ceo ;

答案 1 :(得分:1)

您可以使用正则表达式拆分companies.legal_contacts列表,然后将结果集与联系人一起加入以获取电子邮件地址(加入两次以获取ceo邮件)然后重新连接电子邮件使用listagg函数:

SELECT co.company_id, p1.email, LISTAGG(p2.email, ', ') WITHIN GROUP (ORDER BY p2.email)
  FROM (
        SELECT DISTINCT company_id, ceo, REGEXP_SUBSTR(legal_contacts, '[^, ]+', 1, LEVEL) AS single_contact   
          FROM COMPANIES
       CONNECT BY REGEXP_SUBSTR(legal_contacts, '[^, ]+', 1, LEVEL) IS NOT NULL) co
  LEFT JOIN CONTACTS p1 ON co.ceo = p1.person_id
  LEFT JOIN CONTACTS p2 ON co.single_contact = p2.person_id
 GROUP BY co.company_id, p1.email;

如果companies.legal_contacts可以包含许多值,则出于性能原因,正则表达式的使用会发生一些变化,您必须使用MULTISET。

答案 2 :(得分:0)

您还可以编写一个帮助您规范化数据的函数(pehpaps创建您自己的视图)

CREATE OR REPLACE TYPE VARCHAR_TABLE_TYPE AS TABLE OF VARCHAR2(1000);


CREATE OR REPLACE FUNCTION SplitString(LIST IN VARCHAR2, Separator IN VARCHAR2) RETURN VARCHAR_TABLE_TYPE IS
    OutTable VARCHAR_TABLE_TYPE;    
BEGIN    
    SELECT TRIM(REGEXP_SUBSTR(LIST, '[^'||Separator||']+', 1, LEVEL)) 
    BULK COLLECT INTO OutTable
    FROM dual                           
    CONNECT BY REGEXP_SUBSTR(LIST, '[^'||Separator||']+', 1, LEVEL) IS NOT NULL;
    RETURN OutTable;    
END SplitString;

然后查询将是:

WITH com AS 
    (SELECT company_id, company_name, ceo, cfo, 
        imp_util.SplitString(legal_contacts, ', ') AS legal_contacts
    FROM companies)
SELECT company_id, ceo.email AS ceo, 
    LISTAGG(legal.email, ', ') WITHIN GROUP (ORDER BY legal.person_id) AS emails
FROM com
    LEFT OUTER JOIN contacts legal ON legal.person_id MEMBER OF legal_contacts
    LEFT OUTER JOIN contacts ceo ON ceo.person_id = ceo
GROUP BY company_id, ceo.email;

答案 3 :(得分:-1)

SQL> WITH companies (company_id, ceo, legal_contacts)
  2       AS (SELECT 1, 'pid111', 'pid444, pid567, pid999' FROM DUAL
  3           UNION
  4           SELECT 2, 'pid777', 'pid345' FROM DUAL
  5           UNION
  6           SELECT 3, 'pid123', NULL FROM DUAL),
  7       contacts (person_id, email)
  8       AS (SELECT 'pid111', 'john@gmail.com' FROM DUAL
  9           UNION
 10           SELECT 'pid333', 'funkylover@mail.com' FROM DUAL
 11           UNION
 12           SELECT 'pid444', 'mar123@ymail.com' FROM DUAL
 13           UNION
 14           SELECT 'pid999', 'joe.bloggs@gmail.com' FROM DUAL
 15           UNION
 16           SELECT 'pid777', NULL FROM DUAL
 17           UNION
 18           SELECT 'pid345', 'carol@carolssite.com' FROM DUAL
 19           UNION
 20           SELECT 'pid321', 'ellen.deg@gmail.com' FROM DUAL),
 21       sco
 22       AS (SELECT company_id,
 23                  ceo,
 24                  REGEXP_SUBSTR (REPLACE (legal_contacts, ' ', ''),
 25                                 '[^,]+',
 26                                 1,
 27                                 x.COLUMN_VALUE)
 28                     person_id
 29             FROM companies,
 30                  TABLE (
 31                     CAST (
 32                        MULTISET (
 33                               SELECT LEVEL
 34                                 FROM DUAL
 35                           CONNECT BY LEVEL <=
 36                                         REGEXP_COUNT (legal_contacts, ',') + 1) AS SYS.odcivarchar2list)) x)
 37    SELECT s.company_id,
 38           c1.email ceo,
 39           LISTAGG (c2.email, ', ') WITHIN GROUP (ORDER BY c2.email)
 40              legal_contacts
 41      FROM sco s
 42           LEFT OUTER JOIN contacts c1 ON c1.person_id = s.ceo
 43           LEFT OUTER JOIN contacts c2 ON c2.person_id = s.person_id
 44  GROUP BY s.company_id, c1.email;

COMPANY_ID CEO                  LEGAL_CONTACTS
---------- -------------------- ----------------------------------------
         1 john@gmail.com       joe.bloggs@gmail.com, mar123@ymail.com
         2                      carol@carolssite.com
         3

SQL>