Сочетание всех значений (Oracle) - PullRequest
1 голос
/ 10 октября 2019

Я хотел бы получить все возможные комбинации из данного набора данных, но даже с пустыми элементами (то есть с переменным числом элементов)

Например, у нас есть данные и простой запрос, который их отображает:

with t as (   select 1 as COL1, 2 as COL2, 3 as COL3 from dual )  
select * from t;

или лучший пример: (возможно, простое решение)

select 1 as col from dual 
union all 
select 2 from dual 
union all 
select 3 from dual

Можно ли создать запрос, отображающий следующий результат:

1     -     -
1     2     -
1     2     3
1     3     -
1     3     2

2     -     -
2     1     -
2     1     3
2     3     -
2     3     1

3     -     -
3     1     -
3     1     2
3     2     -
3     2     1

1 Ответ

1 голос
/ 10 октября 2019

Одна трудность здесь в том, что у вас есть входные данные в трех столбцах, а вы хотите получить результаты в трех столбцах. Если вы не хотите использовать динамический SQL (который является отдельной темой, продвинутой техникой, которая в большинстве случаев также является плохой практикой и не имеет ничего общего с вашим вопросом о комбинаторике), вам придется жестко кодировать число (иимена) столбцов в запросе.

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

row_num  col_num  val
-------  -------  ---
      1        1    1
      1        2    -
      1        3    -

(это имитирует только ваш первый выводстрока) - и аналогично для всех других строк - и тогда вам НЕ нужно будет жестко кодировать количество столбцов в запросе;затем вы можете легко адаптировать приведенный ниже код для решения этой более общей проблемы.

Я использую SYS_CONNECT_BY_PATH ниже, что ограничивает количество столбцов (и длину значений в каждом столбце во входных данных);этого можно избежать, но написание запроса таким способом - весело.

Я предположил, что тире - в желаемом выводе означает NULL;если вы действительно хотите показывать тире, используйте NVL(..., -) в последнем предложении SELECT.

with
  t (col1, col2, col3) as (
    select 1 as col1, 2 as col2, 3 as col3 from dual
  )
, prep (pth) as (
    select  sys_connect_by_path(val, '/') || '/' 
    from    t 
    unpivot (val for col in (col1, col2, col3))
    connect by nocycle prior col is not null
  )
select to_number(substr(pth, instr(pth, '/', 1, 1) + 1, 
                        instr(pth, '/', 1, 2) - instr(pth, '/', 1, 1) - 1)) col1,
       to_number(substr(pth, instr(pth, '/', 1, 2) + 1, 
                        instr(pth, '/', 1, 3) - instr(pth, '/', 1, 2) - 1)) col2,
       to_number(substr(pth, instr(pth, '/', 1, 3) + 1, 
                        instr(pth, '/', 1, 4) - instr(pth, '/', 1, 3) - 1)) col3
from   prep
order  by col1 nulls first, col2 nulls first, col3 nulls first
;

Вывод:

 COL1  COL2  COL3
----- ----- -----
    1            
    1     2      
    1     2     3
    1     3      
    1     3     2
    2            
    2     1      
    2     1     3
    2     3      
    2     3     1
    3            
    3     1      
    3     1     2
    3     2      
    3     2     1
...