#plsql #oracle11g #procedure #listagg #dbvisualizer
#Oracle #plsql #oracle11g #сводный #динамический-сводный
Вопрос:
У меня есть следующая таблица TEMP
Я хочу создать сводное представление с использованием SQL, упорядоченное по CATEGORY
ASC, по LEVEL
DESC и SET
ASC и заполнить value
.
Ожидаемый результат:
Я попробовал следующий код, но не смог найти обходной путь для агрегированной части, которая выдает ошибку:
SELECT *
FROM
(SELECT
SET, LEVEL, CATEGORY, VALUE
FROM
TEMP
ORDER BY
CATEGORY ASC, LEVEL DESC, SET ASC) x
PIVOT
(value(VALUE) FOR RISK_LEVEL IN ('X','Y','Z') AND CATEGORY IN ('ABC', 'DEF', 'GHI', 'JKL')) p
Кроме того, я хочу знать, может ли существовать какой-либо метод для динамического добавления столбцов и получения этого представления для любой таблицы с одинаковыми столбцами (чтобы можно было избежать жесткого кодирования).
Я знаю, что мы можем сделать это в Excel и перенести его, но я хочу, чтобы данные хранились в БД в этом формате.
Ответ №1:
Для создания SQL для динамического поворота может быть создана хранимая функция (или процедура), а результирующий набор загружается в переменную типа SYS_REFCURSOR
:
CREATE OR REPLACE FUNCTION Get_Categories_RS RETURN SYS_REFCURSOR IS
v_recordset SYS_REFCURSOR;
v_sql VARCHAR2(32767);
v_cols_1 VARCHAR2(32767);
v_cols_2 VARCHAR2(32767);
BEGIN
SELECT LISTAGG( ''''||"level"||''' AS "'||"level"||'"' , ',' )
WITHIN GROUP ( ORDER BY "level" DESC )
INTO v_cols_1
FROM (
SELECT DISTINCT "level"
FROM temp
);
SELECT LISTAGG( 'MAX(CASE WHEN category = '''||category||''' THEN "'||"level"||'" END) AS "'||"level"||'_'||category||'"' , ',' )
WITHIN GROUP ( ORDER BY category, "level" DESC )
INTO v_cols_2
FROM (
SELECT DISTINCT "level", category
FROM temp
);
v_sql :=
'SELECT "set", '|| v_cols_2 ||'
FROM
(
SELECT *
FROM temp
PIVOT
(
MAX(value) FOR "level" IN ( '|| v_cols_1 ||' )
)
)
GROUP BY "set"
ORDER BY "set"';
OPEN v_recordset FOR v_sql;
RETURN v_recordset;
END;
в котором я использовал два уровня поворота: первый находится внутри предложения, включающего внутренний PIVOT
запрос, а второй — во внешнем запросе, имеющем логику условного агрегирования. Обратите внимание, что порядок уровней должен быть в порядке убывания ( Z
, Y
, X
) в пределах ожидаемого результата, соответствующего описанию.
А затем вызвать
VAR rc REFCURSOR
EXEC :rc := Get_Categories_RS;
PRINT rc
из командной строки разработчика SQL, чтобы получить набор результатов
Кстати, избегайте использования зарезервированных ключевых слов, таких как set
и level
как в вашем случае. Мне нужно было указать их, чтобы иметь возможность использовать.
Комментарии:
1. Можем ли мы создать это как
procedure
, а не как функцию ..? Далее у меня есть 30 разных таблиц с разными именами и одинаковыми столбцами. Я думаю, что с помощью этого метода мне нужно будет создать такое количество функций?2. Также не могли бы вы рассказать мне или показать, что
t3
такое таблица? Разве мы не должны передавать таблицу в качестве параметра вfunction
orprocedure
.3. Привет @Scope , как я упоминал в процедуре создания ответа, также возможно, если вам нужно вернуть более одного параметра. Например
v_recordset
, может быть перемещен в список параметров процедуры, например,CREATE OR REPLACE PROCEDURE Get_Categories_RS( .... , v_recordset OUT SYS_REFCURSOR, ... ) IS
поскольку тип остается неизменным. Тем не менее, вам не нужно создавать функцию (или процедуру), вы можете просто найти свою работу в блоке кода вместе с простым циклом, который принимает значения из таблицы или массива БД. Остальное зависит от вас . Кстати, я забыл использовать ваше исходное имя таблицыtemp
(t3
)..4. Спасибо за ваш ответ. Я попытался запустить его в commander (вне функции или процедуры), во втором запросе я получил ошибку «Результат слишком длинной конкатенации строк
LISTAGG
«. Есть ли обходной путь для этого? Как вам удалось его выполнить5. Другие источники предложили
SELECT RTRIM(XMLAGG(XMLELEMENT(E,colname,',').EXTRACT('//text()') ORDER BY colname).GetClobVal(),',') AS LIST
обходной путь, но я не знаю, работает ли он с Oracle 11g, поскольку выдает ошибку. Из-за этого я не могу запустить код