【问题标题】:Oracle PL/SQL How to store and fetch a dynamic multi column queryOracle PL/SQL 如何存储和获取动态多列查询
【发布时间】:2020-06-29 01:54:09
【问题描述】:

我在这里尝试动态 PL/SQL 的东西。 我无法获取列动态查询。 我正在迭代列的名称以连接完整的查询,以便在另一个表上执行。

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:我知道昏迷是错误的,但我的代码比这更复杂:我不想放更多东西

【问题讨论】:

    标签: sql oracle dynamic


    【解决方案1】:

    如果只想获取字符串列,可以使用listagg

    select listagg(name_col, ',') WITHIN GROUP (ORDER BY null) from TAB_LISTCOL
    

    【讨论】:

    • 我明白了! thx 的提示,但这不是这个问题的目的。一旦我建立了查询,我就想执行它。立即执行工作,但我无法存储结果并在列上进行迭代。
    【解决方案2】:

    看看有没有帮助

    在没有实际表结构和要求的情况下,我正在创建虚拟表和查询来说明一个示例:

        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.
    



    为了降低最后一个块的复杂性,我定义了一个函数来生成动态 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     
    



    下面是使用 DBMS_SQL 执行动态游标的示例块。为了便于理解,我尽可能添加了 cmets。更多信息here

        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                 |
    

    【讨论】:

    • 天啊,看起来你指出的正是我想要的!我会花一些时间来修改我的代码,如果需要我会回复你!!!非常感谢
    • 如果这符合您的要求,请随时accept 回答
    • 好的,我用 DBMS_SQL 实现了您的解决方案,它运行良好!多谢 !我也使用了 describe_columns 来取回列的名称,并且效果也很好!我真的很开心
    【解决方案3】:

    您必须将 EXECUTE IMMEDIATE 与 BULK COLLECT 一起使用

    下面是一个相同的例子。更多信息请参考link

        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;     
    

    【讨论】:

    • 我看到了那个帖子,我试过了,但它只有在你知道列名而我不知道的情况下才有效。这就是为什么我进入另一个表来获取列名的原因。在此示例中,创建了自定义类型 name_salary_rt,但我无法创建自定义动态类型
    • 您可以在同一链接中使用 DBMS_SQL 进行一些条件处理。如果您提供更多详细信息,我可以为您提供帮助
    【解决方案4】:

    如果我理解正确,您想创建一个查询并执行它并将结果返回给另一个函数或某个调用应用程序。由于结果查询的列是已知的,在这种情况下我会返回一个引用游标:

    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;
    

    【讨论】:

    • 感谢您的帮助!我会试试看,然后我会回来找你的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-26
    • 1970-01-01
    • 2023-02-23
    • 2017-01-31
    • 1970-01-01
    相关资源
    最近更新 更多