Oracle должен знать все столбцы в списке выбора на этапе PARSING.
Это имеет несколько последствий
Oracle не может изменить список столбцов запроса без его повторного анализа. Независимо от того, что должно на это повлиять - будь то отдельный список значений в каком-то столбце или что-то еще. Другими словами, вы не можете ожидать, что Oracle добавит новые столбцы для вывода, если вы добавили новое значение в столбец CCL в вашем примере.
В каждом запросе вы должны явно указать все столбцы в списке выбора, если вы не используете "*"
с псевдонимом таблицы. Если вы используете "*"
, то Oracle получает список столбцов из метаданных, а если вы изменяете метаданные (то есть запускаете DDL для таблицы), то Oracle повторно анализирует запрос.
Таким образом, лучший вариант работы с «Динамическим поворотом» - это поворот и форматирование результата в пользовательском интерфейсе. Тем не менее, в базе данных все еще есть варианты, которые вы можете рассмотреть.
Генерация XML с поворотным результатом и его анализ.
Сделайте pivot для XML, а затем проанализируйте результаты. В этом случае, в конце концов, вам придется указывать поворотные столбцы тем или иным способом.
create table tablea(id, ccl, flag) as
(
select 1, 'john', 'x' from dual
union all select 1, 'adam', 'x' from dual
union all select 1, 'terry', null from dual
union all select 1, 'rob', 'x' from dual
union all select 2, 'john', 'x' from dual
);
В приведенном ниже примере вам НЕ нужно указывать список значений для CCL, вы указываете только следующие литералы:
выражение поворота (FLAG) и столбец, используемый для поворота (CCL).
SQL> select id, x.*
2 from tablea t
3 pivot xml (max(flag) flag for ccl in(any))
4 -- parsing output
5 , xmltable('/PivotSet' passing ccl_xml
6 columns
7 name1 varchar2(30) path '/PivotSet/item[1]/column[@name="CCL"]/text()',
8 value1 varchar2(30) path '/PivotSet/item[1]/column[@name="FLAG"]/text()',
9 name2 varchar2(30) path '/PivotSet/item[2]/column[@name="CCL"]/text()',
10 value2 varchar2(30) path '/PivotSet/item[2]/column[@name="FLAG"]/text()',
11 name3 varchar2(30) path '/PivotSet/item[3]/column[@name="CCL"]/text()',
12 value3 varchar2(30) path '/PivotSet/item[3]/column[@name="FLAG"]/text()',
13 name4 varchar2(30) path '/PivotSet/item[4]/column[@name="CCL"]/text()',
14 value4 varchar2(30) path '/PivotSet/item[4]/column[@name="FLAG"]/text()') x;
ID NAME1 VALUE NAME2 VALUE NAME3 VALUE NAME4 VALUE
---------- ----- ----- ----- ----- ----- ----- ----- -----
1 adam x john x rob x terry
2 john x
Возможно, вы заметили 2 важные детали
Фактически каждый сводный столбец представлен двумя столбцами в результате - один для заголовка и один для значения
Имена упорядочены, поэтому вы не можете сохранить порядок, как в вашем примере («Джон», «Адам», «Терри», «Роб»),
кроме того, один столбец может представлять разные имена, например, NAME1 представляет значения для 'adam' в первой строке и 'john' во второй строке.
Можно использовать только индексы для получения одинакового вывода.
select id, x.*
from tablea
pivot xml (max(flag) flag for ccl in(any))
-- parsing output
, xmltable('/PivotSet' passing ccl_xml
columns
name1 varchar2(30) path '/PivotSet/item[1]/column[1]',
value1 varchar2(30) path '/PivotSet/item[1]/column[2]',
name2 varchar2(30) path '/PivotSet/item[2]/column[1]',
value2 varchar2(30) path '/PivotSet/item[2]/column[2]',
name3 varchar2(30) path '/PivotSet/item[3]/column[1]',
value3 varchar2(30) path '/PivotSet/item[3]/column[2]',
name4 varchar2(30) path '/PivotSet/item[4]/column[1]',
value4 varchar2(30) path '/PivotSet/item[4]/column[2]') x;
Но все же есть два столбца для каждого поворотного столбца в выводе.
Запрос ниже возвращает те же данные, что и в вашем примере
SQL> select id, x.*
2 from tablea
3 pivot xml (max(flag) flag for ccl in(any))
4 -- parsing output
5 , xmltable('/PivotSet' passing ccl_xml
6 columns
7 john varchar2(30) path '/PivotSet/item[column="john"]/column[2]',
8 adam varchar2(30) path '/PivotSet/item[column="adam"]/column[2]',
9 terry varchar2(30) path '/PivotSet/item[column="terry"]/column[2]',
10 rob varchar2(30) path '/PivotSet/item[column="rob"]/column[2]') x;
ID JOHN ADAM TERRY ROB
---------- ----- ----- ----- -----
1 x x x
2 x
Но подождите ... все значения для CCL указаны в запросе. Это связано с тем, что заголовок столбца не может зависеть от данных в таблице. Так какой смысл в повороте для XML, если бы вы могли просто закодировать все значения в предложении for с одинаковым успехом? Одна из идей заключается в том, что движок Oracle SQL транспонирует результат запроса, а инструмент, отображающий вывод, просто должен правильно анализировать XML. Таким образом, вы разделяете поворотную логику на два слоя. Синтаксический анализ XML может выполняться вне SQL, например, в вашем приложении.
Интерфейс таблицы ODCI
В другом ответе уже есть ссылка на Решение Антона .
Вы также можете проверить пример здесь .
И, конечно, это подробно объясняется в документации Oracle.
Полиморфные табличные функции
В Oracle 18 была введена еще одна передовая технология - Полиморфные табличные функции .
Но опять же, вы не должны ожидать, что список столбцов вашего запроса изменится после добавления нового значения в CCL. Это может измениться только после повторного разбора. Существует способ принудительного разбора перед каждым извинением, но это уже другая тема.
Динамический SQL
Наконец, как уже отмечалось в комментариях, вы можете использовать старый добрый DSQL.
Первый шаг - сгенерировать оператор SQL на основе содержимого таблицы. Второй шаг - выполнить его.
SQL> var rc refcursor
SQL> declare
2 tmp clob;
3 sql_str clob := 'select * from tablea pivot (max(flag) for ccl in ([dynamic_list]))';
4 begin
5 select listagg('''' || ccl || ''' as ' || ccl, ',') within group(order by max(ccl))
6 into tmp
7 from tablea
8 group by ccl;
9 open :rc for replace(sql_str, '[dynamic_list]', tmp);
10 end;
11 /
PL/SQL procedure successfully completed.
SQL> print rc
ID ADAM JOHN ROB TERRY
---------- ----- ----- ----- -----
1 x x x
2 x