Поворот с несколькими входами

#sql #oracle #pivot

Вопрос:

У меня есть приведенная ниже таблица t1:

A B C V
A1 В1 C1 V1
A2 В1 C2 V2
A3 B2 C1 V3
А4 B2 C2 V4
A1 В1 C1 V10
A1 В1 C2 V11

Я хотел бы получить следующий результат:

A B1_C1 B1_C2 B2_C1 B2_C2
A1 V1 V11 нулевой нулевой
A2 нулевой V2 нулевой нулевой
A3 нулевой нулевой V3 нулевой
А4 нулевой нулевой нулевой V4

Я хотел бы, чтобы минимум V группировался по A, будучи столбцами комбинаций B, C

Ответ №1:

Если вы знаете нужные столбцы, используйте условную агрегацию:

 select a,
       max(case when b = 'B1' and c = 'C1' then v end) as b1_c1,
       max(case when b = 'B1' and c = 'C2' then v end) as b1_c2,
       max(case when b = 'B2' and c = 'C1' then v end) as b2_c1,
       max(case when b = 'B2' and c = 'C2' then v end) as b2_c2
from t
group by a;
 

Комментарии:

1. На самом деле, я не знаю столбцов, мне нужен динамический способ 🙁

Ответ №2:

В основном вам необходимо объединить b столбцы и c столбцы, а затем выполнить поворот по этим столбцам, группируя их по столбцу a , например

 WITH t2 AS
(
SELECT b||'_'||c AS bc, a, v
  FROM t1
)
SELECT a,
       MIN(DECODE(bc,'B1_C1',v)) AS b1_c1,
       MIN(DECODE(bc,'B1_C2',v)) AS b1_c2,
       MIN(DECODE(bc,'B2_C1',v)) AS b2_c1,
       MIN(DECODE(bc,'B2_C2',v)) AS b2_c2
  FROM t2 
 GROUP BY a 
 ORDER BY a
 

Вы можете создать такую сохраненную функцию, которая возвращает SYS_REFCURSOR результат типа, используя приведенную выше инструкцию select в качестве основного запроса вместе со вспомогательным запросом для создания агрегированных условных выражений, чтобы выполнить это динамически ;

 CREATE OR REPLACE FUNCTION Get_Pivoted_Values RETURN SYS_REFCURSOR IS
  v_recordset SYS_REFCURSOR;
  v_sql       VARCHAR2(32767);
  v_cols      VARCHAR2(32767);
BEGIN 
  SELECT LISTAGG( 'MIN(DECODE(bc,'''||bc||''',v)) AS '||bc , ',' ) 
          WITHIN GROUP ( ORDER BY bc )        
    INTO v_cols
    FROM ( SELECT b||'_'||c AS bc
             FROM t1
            GROUP BY b||'_'||c
            ORDER BY bc ); 

  v_sql :='WITH t2 AS
           (
            SELECT b||''_''||c AS bc, t.*
              FROM t1
           )
           SELECT a,'|| v_cols ||'
             FROM t2 
            GROUP BY a 
            ORDER BY a';            

  OPEN v_recordset FOR v_sql;
  RETURN v_recordset;
END;
/
 

Затем запустите приведенный ниже код ;

 VAR rc REFCURSOR
EXEC :rc := Get_Pivoted_Values;
PRINT rc
 

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

Ответ №3:

Это можно сделать «динамически», но, например PIVOT , с фиксированным максимальным количеством столбцов :

 WITH t1 AS (SELECT 'A1' A , 'B1' B, 'C1' C,  'V1' V FROM DUAL
            UNION ALL SELECT 'A2', 'B1', 'C2', 'V2' FROM DUAL
            UNION ALL SELECT 'A3', 'B2', 'C1', 'V3' FROM DUAL
            UNION ALL SELECT 'A4', 'B2', 'C2', 'V4' FROM DUAL
            UNION ALL SELECT 'A1', 'B1', 'C1', 'V10' FROM DUAL
            UNION ALL SELECT 'A1', 'B1', 'C2', 'V11' FROM DUAL), 
     t2 AS (SELECT t1.A, t1.B||'_'||t1.C col, t1.V
              FROM t1),
     t3 AS (SELECT ROWNUM colnum, col FROM (SELECT DISTINCT t2.col FROM t2 ORDER BY t2.col))       
 SELECT NULL A, 
       (SELECT t3.col FROM t3 WHERE colnum = 1) col1,
       (SELECT t3.col FROM t3 WHERE colnum = 2) col2,
       (SELECT t3.col FROM t3 WHERE colnum = 3) col3,
       (SELECT t3.col FROM t3 WHERE colnum = 4) col4,
       (SELECT t3.col FROM t3 WHERE colnum = 5) col5,
       (SELECT t3.col FROM t3 WHERE colnum = 6) col6,
       (SELECT t3.col FROM t3 WHERE colnum = 7) col7,
       (SELECT t3.col FROM t3 WHERE colnum = 8) col8
  FROM DUAL
 UNION ALL
SELECT *
  FROM (
SELECT A, MAX(col1_v) col1, MAX(col2_v) col2, MAX(col3_v) col3, MAX(col4_v) col4, 
          MAX(col5_v) col5, MAX(col6_v) col6, MAX(col7_v) col7, MAX(col8_v) col8
  FROM (SELECT * 
          FROM (SELECT t2.A, t2.V, t3.col, t3.colnum 
                  FROM t2 JOIN t3 ON t3.col = t2.col)
         PIVOT (
           MIN(V) AS V
           FOR colnum IN (1 AS col1, 2 AS col2, 3 AS col3, 4 AS col4, 5 AS col5, 6 AS col6, 7 AS col7, 8 AS col8)
         ) )
  GROUP BY A
  ORDER BY A
) 
 

Здесь для 8 различных комбинаций B и C макс. первая строка выходных данных описывает, в каком столбце какая комбинация B и C, следующие строки-ваши ожидаемые результаты. Вы можете адаптировать его с максимальным количеством возможных комбинаций B и C, просто добавьте столбцы.

Предупреждение: Если количество комбинаций B и C превышает фиксированное количество столбцов, запрос будет выполнен, но вывод будет ограничен количеством комбинаций, разрешенных текущим количеством столбцов.

Скрипка, если вы хотите ее протестировать.

Комментарии:

1. Также возможно иметь динамическое количество столбцов и динамическое именование для них с помощью полиморфных функций Oracle , но для этого требуется написать код PLSQL в вашей БД.

2. Или макросы SQL тоже (спасибо @WilliamRobertson за идею) .