Oracle PL / SQL Как сохранить и извлечь динамический c многостолбцовый запрос - PullRequest
1 голос
/ 18 марта 2020

Я стараюсь изо всех сил c PL / SQL вещь здесь. Мне не удается получить динамический столбец c Запрос. Я перебираю имя столбца, чтобы объединить полный запрос, чтобы выполнить его в другой таблице.

sql_req := 'select '; 
for c in  (SELECT name_col  from TAB_LISTCOL)
loop
  sql_req := sql_req || 'sum(' || c.name_col || '),'; 
end loop; 
sql_req := sql_req || ' from ANOTHER_TAB '; 

И когда я пытаюсь выполнить его с помощью команды EXECUTE IMMEDIATE или курсоров, или INTO / BULK COLLECT или просто получить, мне не удается перебрать результат. Я много пробовал. Можете ли вы помочь мне, плз? Или, может быть, это невозможно?

ps: я знаю, что кома неверна, но мой код более сложный, чем этот: я не хотел ставить больше вещей

Ответы [ 4 ]

3 голосов
/ 18 марта 2020

Если вы хотите получить только строковые столбцы, вы можете использовать listagg

select listagg(name_col, ',') WITHIN GROUP (ORDER BY null) from TAB_LISTCOL
1 голос
/ 19 марта 2020

Если я правильно понимаю, вы хотите создать запрос, выполнить его и вернуть результат другой функции или вызывающему приложению. Поскольку столбцы результирующего запроса на заметку ранее известны, я бы в этом случае вернул курсор ref:

create function get_sums return sys_refcur as
declare
  my_cursor sys_refcursor;
  v_query varchar2(32757);
begin
  select
    'select ' ||
    listagg('sum(' || name_col || ')', ', ') within group (order by name_col) ||
    ' from another_tab'
  into v_query
  from tab_listcol; 

  open my_cursor for v_query;

  return v_query;
end get_sums;
1 голос
/ 19 марта 2020

Пожалуйста, посмотрите, поможет ли это

В отсутствие фактической структуры и требований к таблицам, я создаю фиктивные таблицы и запрос для иллюстрации примера:

    SQL> create table another_tab
         as
         select 10 dummy_value1, 100 dummy_value2, 1000 dummy_value3 from dual union all
         select 11 dummy_value1, 101 dummy_value2, 1001 dummy_value3 from dual union all
         select 12 dummy_value1, 102 dummy_value2, 1003 dummy_value3 from dual
         ;

    Table created.

    SQL> create table tab_listcol
         as select column_name from dba_tab_cols where table_name = 'ANOTHER_TAB'
         ;

    Table created.



Чтобы уменьшить сложность в последнем блоке, я определяю функцию для генерации динамического c sql запроса. Это основано на вашем примере и потребует изменений в соответствии с вашими требованиями.

    SQL> create or replace function gen_col_based_query 
            return varchar2  
            as
                l_query varchar2(4000);
            begin
                l_query := 'select ';
                for cols in ( select column_name cname from tab_listcol )
                loop
                l_query := l_query || 'sum(' || cols.cname || '), ' ;
                end loop;
                l_query := rtrim(l_query,', ') || ' from another_tab';
                return l_query;
            end;
            /

    Function created.   


Пример вывода из функции будет следующим:

    SQL> select gen_col_based_query as query from dual;

    QUERY
    --------------------------------------------------------------------------------
    select sum(DUMMY_VALUE1), sum(DUMMY_VALUE2), sum(DUMMY_VALUE3) from another_tab     



Ниже приведен пример блока для выполнения Динамический курсор c с использованием СУБД_ SQL. Для вашего удобства я добавил комментарии, где это возможно. Подробнее здесь .

    SQL> set serveroutput on size unlimited

    SQL> declare

            sql_stmt   clob;
            src_cur    sys_refcursor;
            curid      number;
            desctab    dbms_sql.desc_tab;  -- collection type
            colcnt     number;
            namevar    varchar2 (50);
            numvar     number;
            datevar    date;
            l_header   varchar2 (4000);
            l_out_rows varchar2 (4000);

         begin

             /* Generate dynamic sql from the function defined earlier */
             select gen_col_based_query into sql_stmt from dual;


             /* Open cursor variable for this dynamic sql */
             open src_cur for sql_stmt;


             /* To fetch the data, however, you cannot use the cursor variable, since the number of elements fetched is unknown at complile time.
             Therefore you use DBMS_SQL.TO_CURSOR_NUMBER to convert a REF CURSOR variable to a SQL cursor number which you can then pass to DBMS_SQL subprograms
             */
             curid := dbms_sql.to_cursor_number (src_cur);


             /* Use DBMS_SQL.DESCRIBE_COLUMNS to describe columns of your dynamic cursor, returning information about each column in an associative array of records viz., desctab. The no. of columns is returned in colcnt variable.
             */
             dbms_sql.describe_columns (curid, colcnt, desctab);


             /* Define columns at runtime based on the data type (number, date or varchar2 - you may add to the list)
             */
             for indx in 1 .. colcnt
             loop
                if desctab (indx).col_type = 2  -- number data type
                then
                   dbms_sql.define_column (curid, indx, numvar);
                elsif desctab (indx).col_type = 12  -- date data type
                then
                   dbms_sql.define_column (curid, indx, datevar);
                else  -- assuming string
                   dbms_sql.define_column (curid, indx, namevar, 100);
                end if;
             end loop;


             /* Print header row */
                 for i in 1 .. desctab.count loop
                  l_header := l_header || ' | ' || rpad(desctab(i).col_name,20);
                 end loop;
                 l_header := l_header || ' | ' ;
                 dbms_output.put_line(l_header);


            /* Loop to retrieve each row of data identified by the dynamic cursor and print output rows
            */

             while dbms_sql.fetch_rows (curid) > 0
             loop
                for indx in 1 .. colcnt
                loop
                   if (desctab (indx).col_type = 2)  -- number data type
                   then
                      dbms_sql.column_value (curid, indx, numvar);
                      l_out_rows := l_out_rows || ' | ' || rpad(numvar,20);
                   elsif (desctab (indx).col_type = 12)  -- date data type
                   then
                      dbms_sql.column_value (curid, indx, datevar);
                      l_out_rows := l_out_rows || ' | ' || rpad(datevar,20);
                   elsif (desctab (indx).col_type = 1)  -- varchar2 data type
                      then
                      dbms_sql.column_value (curid, indx, namevar);
                      l_out_rows := l_out_rows || ' | ' || rpad(namevar,20);
                   end if;
                end loop;
                l_out_rows := l_out_rows || ' | ' ;
                dbms_output.put_line(l_out_rows);
             end loop;

             dbms_sql.close_cursor (curid);
        end;
        /

    PL/SQL procedure successfully completed.


Выход

    | SUM(DUMMY_VALUE1)    | SUM(DUMMY_VALUE2)    | SUM(DUMMY_VALUE3)    |
    | 33                   | 303                  | 3004                 |
1 голос
/ 18 марта 2020

Вы должны использовать EXECUTE IMMEDIATE с BULK COLLECT

Ниже приведен пример того же. За дополнительной информацией обращайтесь по этой ссылке

    DECLARE
       TYPE name_salary_rt IS RECORD (
          name     VARCHAR2 (1000),
          salary   NUMBER
       );

       TYPE name_salary_aat IS TABLE OF name_salary_rt
          INDEX BY PLS_INTEGER;

       l_employees   name_salary_aat;
    BEGIN
       EXECUTE IMMEDIATE
          q'[select first_name || ' ' || last_name, salary 
               from hr.employees
              order by salary desc]'
          BULK COLLECT INTO l_employees;

       FOR indx IN 1 .. l_employees.COUNT
       LOOP
          DBMS_OUTPUT.put_line (l_employees (indx).name);
       END LOOP;
    END;     
...