将多个值映射到列中

时间:2019-06-25 18:47:51

标签: sql oracle pivot

我有下表。

如何映射一行上的多个标签并将其转换为标签说明?有成千上万种组合。

表格:TAG_SEARCH

https://domain/API

表TAG_MAP

DATE        ID     TAGS
6/25/2019   101    1251:1306
6/25/2019   102    1251
6/25/2019   103    1251:1306:1274:1446:1452:1586
6/25/2019   104    1251:1306:1586

预期结果:

TAG_TYPE    TAG_DESC
  1251        Clothing
  1306        Grocery
  1274        Hardware
  1446        Home_Decor
  1452        Electric
  1586        Plumbing

3 个答案:

答案 0 :(得分:3)

让我们考虑如何以规范化的方式进行操作,以便数据可搜索和可累加,并且基本上是有用的。

让我们假设您被困在前两个表中

CREATE TABLE TAG_SEARCH
  (TAG_DT         DATE,
   TAG_ID         NUMBER,
   TAGS           VARCHAR2(4000));

CREATE TABLE TAG_MAP
  (TAG_TYPE       NUMBER,
   TAG_DESC       VARCHAR2(100));

,您正在将数据存储在输出表中:

CREATE TABLE TAGS_OUT
  (TAG_DT         DATE,
   TAG_ID         NUMBER,
   TAG_DESC       VARCHAR2(100));

现在,如果我们将表填充为

MERGE INTO TAG_SEARCH ts
  USING (SELECT TO_DATE('6/25/2019', 'MM/DD/YYYY') AS TAG_DT, 101 AS TAG_ID, '1251:1306' AS TAGS FROM DUAL UNION ALL
         SELECT TO_DATE('6/25/2019', 'MM/DD/YYYY') AS TAG_DT, 102 AS TAG_ID, '1251' AS TAGS FROM DUAL UNION ALL
         SELECT TO_DATE('6/25/2019', 'MM/DD/YYYY') AS TAG_DT, 103 AS TAG_ID, '1251:1306:1274:1446:1452:1586' AS TAGS FROM DUAL UNION ALL
         SELECT TO_DATE('6/25/2019', 'MM/DD/YYYY') AS TAG_DT, 104 AS TAG_ID, '1251:1306:1586' AS TAGS FROM DUAL) d
    ON (d.TAG_ID = ts.TAG_ID)
WHEN NOT MATCHED THEN
  INSERT (TAG_DT, TAG_ID, TAGS) VALUES (d.TAG_DT, d.TAG_ID, d.TAGS);

MERGE INTO TAG_MAP tm
  USING (SELECT 1251 AS TAG_TYPE, 'Clothing' AS TAG_DESC FROM DUAL UNION ALL
         SELECT 1306 AS TAG_TYPE, 'Grocery' AS TAG_DESC FROM DUAL UNION ALL
         SELECT 1274 AS TAG_TYPE, 'Hardware' AS TAG_DESC FROM DUAL UNION ALL
         SELECT 1446 AS TAG_TYPE, 'Home_Decor' AS TAG_DESC FROM DUAL UNION ALL
         SELECT 1452 AS TAG_TYPE, 'Electric' AS TAG_DESC FROM DUAL UNION ALL
         SELECT 1586 AS TAG_TYPE, 'Plumbing' AS TAG_DESC FROM DUAL) d
    ON (d.TAG_TYPE = tm.TAG_TYPE)
WHEN NOT MATCHED THEN
  INSERT (TAG_TYPE, TAG_DESC) VALUES (d.TAG_TYPE, d.TAG_DESC);

我们可以稍微滥用分层查询以使其吐出各个标签:

SELECT DISTINCT TAG_ID, LEVEL, TAG_DT, REGEXP_SUBSTR(TAGS,'[^:]+', 1, LEVEL) AS TAG_TYPE
  FROM TAG_SEARCH
  CONNECT BY REGEXP_SUBSTR(TAGS,'[^:]+', 1, LEVEL) IS NOT NULL
  ORDER BY TAG_ID, LEVEL

一旦我们将TAG_TYPE更改为TAG_DESC,只需简单地将其适当地加入TAG_MAP表即可:

SELECT q.TAG_ID, q.LVL, q.TAG_DT, tm.TAG_DESC
  FROM (SELECT DISTINCT TAG_ID, LEVEL AS LVL, TAG_DT, REGEXP_SUBSTR(TAGS,'[^:]+', 1, LEVEL) AS TAG_TYPE
          FROM TAG_SEARCH
          CONNECT BY REGEXP_SUBSTR(TAGS,'[^:]+', 1, LEVEL) IS NOT NULL
          ORDER BY TAG_ID, LEVEL) q
  INNER JOIN TAG_MAP tm
    ON tm.TAG_TYPE = q.TAG_TYPE
  ORDER BY q.TAG_ID, q.LVL

dbfiddle here

答案 1 :(得分:1)

同意该设计是反关系的。这只是为了表明SQL可以做到。警告:LISTAGG需要数据库版本11.2或更高版本。

更新:我忘记了标记描述的顺序应与源表中标记类型的顺序相匹配。此解决方案不遵守该顺序。 regexp_substr答案是。

create table tAG_SEARCH(DTE,ID,TAGS) as
select to_date('6/25/2019','mm/dd/yyyy'),   101,    '1251:1306' from dual union all
select to_date('6/25/2019','mm/dd/yyyy'),   102,    '1251' from dual union all
select to_date('6/25/2019','mm/dd/yyyy'),   103,    '1251:1306:1274:1446:1452:1586' from dual union all
select to_date('6/25/2019','mm/dd/yyyy'),   104,    '1251:1306:1586' from dual;

create Table TAG_MAP(TAG_TYPE,TAG_DESC) as
select  1251,'Clothing' from dual union all
select  1306,'Grocery' from dual union all
select  1274,'Hardware' from dual union all
select  1446,'Home_Decor' from dual union all
select  1452,'Electric' from dual union all
select  1586,'Plumbing' from dual;

select dte, id,
  listagg(tag_desc, ':') within group(order by tag_type) tags
from tag_search ts 
join tag_map tm on instr(tags, tag_type) > 0
group by dte, id
order by dte, id;

DTE         ID   TAGS                                                     
2019-06-25  101  Clothing:Grocery                                          
2019-06-25  102  Clothing                                                  
2019-06-25  103  Clothing:Hardware:Grocery:Home_Decor:Electric:Plumbing    
2019-06-25  104  Clothing:Grocery:Plumbing  

答案 2 :(得分:1)

您可以先使用regexp_substr()窗口分析功能进行拆分,然后使用listagg()函数将查找表(tag_map)中的解释连接起来

with tag_search( "Date", id, tags ) as
(
 select date'2019-06-25',101,'1251:1306'                     from dual union all
 select date'2019-06-25',102,'1251'                          from dual union all
 select date'2019-06-25',103,'1251:1306:1274:1446:1452:1586' from dual union all
 select date'2019-06-25',104,'1251:1306:1586'                from dual
), tag_map( tag_type, tag_desc) as
(
 select 1251, 'Clothing'   from dual union all
 select 1306, 'Grocery'    from dual union all
 select 1274, 'Hardware'   from dual union all
 select 1446, 'Home_Decor' from dual union all
 select 1452, 'Electric'   from dual union all
 select 1586, 'Plumbing'   from dual
), t as
(
select "Date", id, regexp_substr(tags,'[^:]+',1,level) as tags,
       row_number() over (order by id, level) as lvl
  from tag_search
 connect by level <= regexp_count(tags,':') + 1
    and prior id = id
    and prior sys_guid() is not null
)
select "Date", id, listagg(tag_desc,':') within group (order by id, lvl) as tags   
  from t
  join tag_map on tag_type = tags
 group by "Date", id;

 Date       ID  TAGS
 ---------  --- -------------------------------------------------------
 25-JUN-19  101 Clothing:Grocery
 25-JUN-19  102 Clothing
 25-JUN-19  103 Clothing:Grocery:Hardware:Home_Decor:Electric:Plumbing
 25-JUN-19  104 Clothing:Grocery:Plumbing

Demo