Oracle sql query: pivot table with dynamicly filled for in (advanced)

谁都会走 提交于 2019-12-11 01:40:00

问题


I need to flatten an table, but here si the tricky part, the clomuns are dynamic and the query should work when new records are added containing a new ID.

This is my working query (simplified the IN this is really hundreds of values):

SELECT *
FROM   (select qv.respnr, sq.question_id, qv.question_value
from survey_question sq, question_values qv
where qv.question_id = sq.question_id
and sq.survey_id = 1
order by qv.respnr, page, ranked)
PIVOT (
MAX(question_value)        --<-- pivot_clause
FOR question_id          --<-- pivot_for_clause
IN (346 as c346,347 as c347)
)

I want to replace the IN (346 as c346,347 as c347) with something like this:

SELECT mq.question_id
  FROM meta_question mq, survey_question sq2
 WHERE sq2.survey_id = 1
   AND mq.question_id = sq2.question_id
 ORDER BY page, ranked

Any idea how to do this?

I've noticed the IN can't be simply fileld with a select statement, so this doesn't work:

IN (SELECT mq.question_id
      FROM meta_question mq, survey_question sq2
     WHERE sq2.survey_id = 1
       AND mq.question_id = sq2.question_id
     ORDER BY page, ranked)

回答1:


The name and type of all columns in SQL needs to be known at compile time, here you will have to use dynamic SQL since you want the number of columns to change depending upon the data.

If you use PL/SQL, you can use a ref cursor:

DECLARE
   l_rc            SYS_REFCURSOR;
   l_dynamic_query VARCHAR2(32000);
BEGIN
   FOR cc IN (SELECT mq.question_id
                FROM meta_question mq, survey_question sq2
               WHERE sq2.survey_id = 1
                 AND mq.question_id = sq2.question_id
               ORDER BY page, ranked) LOOP
      -- build dynamic query here
   END LOOP;
   OPEN l_rc FOR '
      SELECT *
        FROM (SELECT qv.respnr, sq.question_id, qv.question_value
                FROM survey_question sq, question_values qv
               WHERE qv.question_id = sq.question_id
                 AND sq.survey_id = 1
               ORDER BY qv.respnr, page, ranked) 
               PIVOT ( MAX (question_value) --<-- pivot_clause
                       FOR question_id --<-- pivot_for_clause
                        IN (' || l_dynamic_query || ')
                      )';
    -- process l_rc (LOOP..FETCH..CLOSE)
END;

You can also use DBMS_SQL.




回答2:


Since the columns in a pivot must be known, you will need to use dynamic SQL to generate the result. I typically generate a procedure using a ref_cursor to perform this task similar to this:

CREATE OR REPLACE procedure dynamic_pivot_q(p_cursor in out sys_refcursor)
as
    sql_query varchar2(8000) := 'select qv.respnr ';

    begin
        for x in (select distinct question_id, mname from meta_question order by 1)
        loop
            sql_query := sql_query ||
                ' , max(case when mq.question_id = '||x.question_id||' then qv.question_value else null end) as "c'||x.mname||'"';

                dbms_output.put_line(sql_query);
        end loop;

        sql_query := sql_query || ' from meta_question mq  
                                    left join survey_question sq 
                                      on mq.question_id = sq.question_id  
                                      and sq.survey_id = 1  
                                    inner join question_values qv 
                                      on qv.question_id = sq.question_id 
                                      and sq.survey_id = 1 
                                    group by qv.respnr';

        dbms_output.put_line(sql_query);

        open p_cursor for sql_query;
    end;
/

Then you can execute it the following way (note: this is the code I use in TOAD):

variable x refcursor
exec dynamic_pivot_q(:x)
print x


来源:https://stackoverflow.com/questions/15092625/oracle-sql-query-pivot-table-with-dynamicly-filled-for-in-advanced

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!