Отображение нескольких значений в столбец - PullRequest
1 голос
/ 25 июня 2019

У меня есть таблица ниже.

Как мне сопоставить несколько тегов в одной строке и превратить их в описание тегов?Есть тысячи комбинаций.

Таблица: TAG_SEARCH

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_MAP

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

Ожидаемые результаты:

DATE       ID   TAGS
6/25/2019  101   Clothing:Grocery
6/25/2019  102   Clothing
6/25/2019  103   Clothing:Grocery:Hardware:Home_Decor:Electric:Plumbing
6/25/2019  104   Clothing:Grocery:Plumbing

Ответы [ 3 ]

3 голосов
/ 25 июня 2019

Давайте рассмотрим, как это сделать в нормализованном порядке, чтобы данные можно было искать и суммировать и, в основном, было полезно.

Предположим, вы застряли с первыми двумя столами:

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 здесь

1 голос
/ 25 июня 2019

Вы можете использовать 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

Демо

1 голос
/ 25 июня 2019

Согласитесь, что дизайн анти-реляционный.Это просто для того, чтобы показать, что 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  
...