Дедуплицирующий список Oracle xmlagg - PullRequest
3 голосов
/ 09 января 2020

Используя Oracle 11.2 я пытаюсь выбрать 2 сгруппированных списка из таблиц ниже, вот мой код:

CREATE  TABLE tmp_main AS (
  SELECT rownum col1, 'txt_' || to_char(rownum) Col2 FROM dual 
    CONNECT BY rownum<=2
);

CREATE TABLE tmp_keys AS (
  SELECT DECODE(rownum,1,1,2,1,3,1,4,2,5,2,6,2) col1, 'key_' || to_char(rownum) key1 , rownum seq FROM dual 
     CONNECT BY rownum<=6
);

CREATE TABLE tmp_line AS (
  SELECT DECODE(rownum,1,1,2,1,3,1,4,1,5,2,6,2,7,2,8,2) col1, 'line_' || DECODE(rownum,2,1,3,1,4,2,5,3,7,3,8,4) line1 , rownum seq   FROM dual 
     CONNECT BY rownum<=8
);

update tmp_line set line1=null where line1='line_';
update tmp_keys set seq=null where col1=1;

tmp_keys.seq может быть нулевым, поэтому мне нужно сначала упорядочить по seq, а затем key1 Вот что я пробовал:

SELECT  m.col1,m.col2,
RTRIM(XMLAGG(XMLELEMENT(E,k.key1 , ',').EXTRACT('//text()') ORDER BY k.seq,k.key1 ).GetClobVal(),',') as key_list ,
RTRIM(XMLAGG(XMLELEMENT(E,l.line1 || ',').EXTRACT('//text()') ORDER BY l.seq ).GetClobVal(),',')  line_list
FROM tmp_main m
JOIN tmp_keys k
ON m.col1=k.col1
JOIN tmp_line l
ON m.col1=l.col1
group by m.col1,col2;

Что дает:

col1 col2   key_list                                                                    line_list
1   txt_1   key_1,key_1,key_1,key_1,key_2,key_2,key_2,key_2,key_3,key_3,key_3,key_3     ,,,line_1,line_1,line_1,line_1,line_1,line_1,line_2,line_2,line_2
2   txt_2   key_4,key_4,key_4,key_4,key_5,key_5,key_5,key_5,key_6,key_6,key_6,key_6     line_3,line_3,line_3,,,,line_3,line_3,line_3,line_4,line_4,line_4

ie дубликатов.

То, что я хотел бы, это:

col1 col2   key_list                    line_list
1   txt_1   key_1,key_2,key_3           ,line_1,line_1,line_2
2   txt_2   key_3,key_4,key_5           line_3,,line_3,line_4

ie сохраняются нулевые значения line1.

Предостережения:

  • Реальный запрос намного больше, поэтому сканирование 1 таблицы было бы неплохо, так как важна скорость.
  • 2 списка могут быть> 4000 символов, поэтому нельзя использовать listagg или аналогичные функции (поэтому я использовал xmlagg и GetClobVal ())

Любая помощь приветствуется

Ответы [ 3 ]

2 голосов
/ 09 января 2020

Сначала назначьте row_number для значений ключа и строки, начиная с 1 для каждой клавиши или строки seq.

Таким образом, вы partition by получите окончательные значения группировки и соответствующий результат. Затем выполните сортировку по значениям ключа / строки:

row_number() over ( 
  partition by m.col1,col2,k.seq
  order by key1
) rnk, 
row_number() over ( 
  partition by m.col1,col2,l.seq
  order by line1
) rnl

Затем агрегируйте только те строки, где этот номер строки = 1:

with rws as (
  select 
    m.*, line1, key1,
    l.seq seql,k.seq seqk,
    row_number() over ( 
      partition by m.col1,col2,k.seq
      order by key1
    ) rnk, 
    row_number() over ( 
      partition by m.col1,col2,l.seq
      order by line1
    ) rnl
  from tmp_main m
  join tmp_keys k
  on   m.col1=k.col1
  join tmp_line l
  on   m.col1=l.col1
)
  select 
    col1,col2,
    rtrim(
      xmlagg(
        xmlelement(
          e,
          case when rnk = 1 then key1 || ',' end 
        ).extract('//text()') 
        order by seqk
      ).getclobval(),','
    ) key_list ,
    rtrim(
      xmlagg(
        xmlelement(
          e,
          case when rnl = 1 then line1 || ',' end 
        ).extract('//text()') 
        order by seql 
      ).getclobval(),','
    ) line_list
  from   rws
  group  by col1,col2;

COL1    COL2     KEY_LIST             LINE_LIST               
      1 txt_1    key_1,key_2,key_3    ,line_1,line_1,line_2    
      2 txt_2    key_4,key_5,key_6    line_3,,line_3,line_4 
1 голос
/ 09 января 2020

Дублируйте и агрегируйте строки перед присоединением к таблицам:

SELECT  m.col1,
        m.col2,
        SUBSTR( k.key_list, 1, LENGTH( k.key_list ) - 1 ) AS key_list,
        SUBSTR( l.line_list, 1, LENGTH( l.line_list ) - 1 ) AS line_list
FROM    tmp_main m
        JOIN (
          SELECT col1,
                 XMLAGG(
                   XMLELEMENT(E,key1 , ',').EXTRACT('//text()')
                   ORDER BY seq
                 ).GetClobVal() as key_list
          FROM   (
            SELECT k.*,
                   ROW_NUMBER() OVER ( PARTITION BY col1, key1 ORDER BY seq ) AS rn
            FROM   tmp_keys k
          )
          WHERE  rn = 1
          GROUP BY col1
        ) k
        ON m.col1=k.col1
        JOIN (
          SELECT col1,
                 XMLAGG(
                   XMLELEMENT(E,line1 , ',').EXTRACT('//text()')
                   ORDER BY seq
                 ).GetClobVal() as line_list
          FROM   (
            SELECT l.*,
                   ROW_NUMBER() OVER ( PARTITION BY col1, line1 ORDER BY seq ) AS rn
            FROM   tmp_line l
          )
          WHERE  rn = 1
          GROUP BY col1
        ) l
        ON m.col1=l.col1;

Кроме того, не используйте RTRIM, если вы ожидаете, что в вашем списке есть пустые строки между разделителями, как если бы эта пустая строка происходит как конечный элемент списка, тогда он будет удален RTRIM; вместо этого просто удалите последний разделитель.

Выходы:

COL1 | COL2  | KEY_LIST          | LINE_LIST     
---: | :---- | :---------------- | :-------------
   1 | txt_1 | key_1,key_2,key_3 | ,line_1,line_2
   2 | txt_2 | key_4,key_5,key_6 | line_3,,line_4

дБ <> скрипка здесь

0 голосов
/ 13 января 2020

В реальном запросе таблицы намного больше, и мне нужно было добавить предикат:

Where m.another_col='xxx'

Когда я добавил этот предикат (после последней строки) в решение из @ MT0, я получил "Нет осталось место на устройстве ", я подозреваю, что это произошло потому, что встроенные запросы выполнялись первыми для всех таблиц tmp_keys и tmp_line и слишком много строк было выбрано до запуска предиката. Поэтому я пошел с решением, предоставленным @chris_saxon, с новым предикатом, добавленным в конец коэффициента подзапроса WITH, после изменения:

 row_number() over (  partition by m.col1,col2,k.seq order by key1 ) rnk

на:

row_number() over (  partition by m.col1,col2,key1 order by k.seq,key1 ) rnk

для решения проблема с k.seq, возможно, является нулевой.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...