问题
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