Oracle 10g PL/SQLのコンテンツに基づいて行を列にピボットします
質問
データベースにテーブルがありますが、 user_answers
一連の質問に対するユーザーの回答を行で保存します。 user_id
, question_id
, answer_id
そして text_entry
. 。質問テキストと回答テキスト (存在する場合) はルックアップ テーブルに保存されます。質問には、単一回答質問、複数回答質問、テキスト入力回答質問の 3 種類があります。したがって、1 人のユーザーが次のようなエントリを持つ可能性があります。 user_answers
テーブル:
user_id question_id answer_id text_entry
------- ----------- --------- ----------
123 100 1010 (null)
123 200 2010 (null)
123 200 2030 (null)
123 300 3000 "code 789"
言ってみましょう questions_text
テーブルには次のものがあります:
question_id text
----------- -------------
100 "Gender"
200 "Interests"
300 "Your code"
そしてその answers_text
テーブルには次のものがあります:
answer_id text
--------- -----------
1010 "Female"
1020 "Male"
2010 "Sports"
2020 "Computers"
2030 "Movies"
3000 (null)
次のような、user_id ごとに 1 行の回答を示すデータを CSV に抽出したいと考えています。
User,Gender,Sports,Computers,Movies,Code
123,Female,1,0,1,code 789
SQLPlus 経由で CSV ファイルを生成する方法は知っていますが (どうしようもない理由で SQLPlus 経由でしか DB にアクセスできません...)、PL/SQL ステートメントを生成する方法はわかりません。
PL/SQL では、次のようにすることで性別の質問のピボットを生成できることがわかりました。
SELECT
user_id || ',' ||
MIN(DECODE(question_id, '100', (SELECT text FROM answers_text where answer_id = answer_text.answer_id)))
FROM user_answers
GROUP BY user_id
ORDER BY user_id
;
(私は SQL の専門家ではないので、これはインターネットからコピーしたものです!)
このコードは (少なくとも私のテストによると) 単一回答の質問には適していますが、複数回答やテキスト入力タイプの質問には機能しません。
の使用についてネットでいくつか見たのですが、 case
PL/SQL のステートメントは次のようになります。
MIN(CASE WHEN question_id = '200' AND answer_id = '2010' THEN '1' ELSE '0' END)
...しかし、回答を列に入れる方法がわかりません。そして、私が見つけた関連する可能性のあるSOの質問はすべてSQLサーバー固有のものです。
単一のPL/SQL文から目的の出力を生成する方法はありますか?これを実行する必要があるデータベースが多数あるため、テーブル内のデータに依存しない方法で記述することが望ましいです。
解決
あなたが探して(そして、このデータに固有のものではない)されているものを達成するために、私はあなたのテーブル内のいくつかの余分なフィールドを必要としていると信じています。たとえば、データを見ることなく、単一回答、マルチ回答、およびテキスト入力である質問知っている必要があります。また、データによってリンクさせることなく、あなたのマルチアンサー質問のために可能である回答を知っている必要があります。そこから、各質問/回答の組み合わせに関するメタ情報をループし、自分でそれがあなたの希望のフォーマットでデータを返します実行するクエリを構築することができます。ような何かます:
/* Create Tables with Data - Note 2 new columns added to questions_text */
create table user_answers
as
select 123 user_id, 100 question_id, 1010 answer_id, null text_entry from dual
union all
select 123 user_id, 200 question_id, 2010 answer_id, null text_entry from dual
union all
select 123 user_id, 200 question_id, 2030 answer_id, null text_entry from dual
union all
select 123 user_id, 300 question_id, 3000 answer_id, 'code 789' text_entry from dual;
create table questions_text
as
select 100 question_id, 'Gender' question_text, 'S' question_type, 1000 answer_set_id from dual
union all
select 200 question_id, 'Interests' question_text, 'M' question_type, 2000 answer_set_id from dual
union all
select 300 question_id, 'Your code' question_text, 'T' question_type, 3000 answer_set_id from dual;
create table answers_text
as
select 1010 answer_id, 'Female' text, 1000 answer_set_id from dual
union all
select 1020 answer_id, 'Male' text, 1000 answer_set_id from dual
union all
select 2010 answer_id, 'Sports' text, 2000 answer_set_id from dual
union all
select 2020 answer_id, 'Computers' text, 2000 answer_set_id from dual
union all
select 2030 answer_id, 'Movies' text, 2000 answer_set_id from dual
union all
select 3000 answer_id, null text, 3000 answer_set_id from dual;
/* PL/SQL for creating SQL statement to return data in desired format */
declare
v_sql VARCHAR2(32767);
begin
v_sql := 'select ua.user_id "User",';
FOR question IN (
select question_id, question_text, question_type, answer_set_id
from questions_text
)
LOOP
IF question.question_type = 'M'
THEN
FOR answer IN (
select answer_id, text
from answers_text
where answer_set_id = question.answer_set_id
)
LOOP
v_sql := v_sql||chr(10)||'max(case when ua.question_id = '||question.question_id||' and ua.answer_id = '||answer.answer_id||' then 1 else 0 end) "'||answer.text||'",';
END LOOP;
ELSIF question.question_type = 'S'
THEN
v_sql := v_sql||chr(10)||'min(case when ua.question_id = '||question.question_id||' then at.text end) "'||question.question_text||'",';
ELSIF question.question_type = 'T'
THEN
v_sql := v_sql||chr(10)||'min(case when ua.question_id = '||question.question_id||' then ua.text_entry end) "'||question.question_text||'",';
END IF;
END LOOP;
v_sql := rtrim(v_sql,',');
v_sql := v_sql||' from
user_answers ua
inner join questions_text qt
on qt.question_id = ua.question_id
inner join answers_text at
on at.answer_id = ua.answer_id
group by
ua.user_id';
-- replace dbms_output with code to write file
dbms_output.put_line(v_sql);
END;
他のヒント
列数が不明なクエリは、よく言っても問題があります。データがどうなるか本当に分からないのでしょうか?これを見てみるのもいいかもしれません トムに聞いてください 必要な結果を得るのに役立つ可能性のあるパッケージの応答。