第45回 「動的SQL (1行返す問い合わせの場合)」
2013.01.07
こんにちは。インストラクターの蓑島です。
年末年始はいかがお過ごしになりましたか。
私は実家がある北海道に帰省し、家族とゆっくり過ごすことができました。
本年もどうぞよろしくお願いいたします。
前回は、動的SQL(文字列のSQL)を実行するための基本構文をご紹介しました。
次のような構文でしたね。軽く復習しましょう。
<動的SQLの実行基本構文>
EXECUTE IMMEDIATE 動的SQL;
きわめて簡単ですね。
しかしこれだけでは不十分な場合があります。それは、問い合わせ(SELECT)の場合です。
問い合わせの場合は、結果が1行でも複数行でも、問い合わせた結果を変数に戻さなければ後続のステップがその結果を利用することができないので、必ず変数に結果を戻す必要があります。
動的SQLではない(つまり埋め込みSQL)の場合の問い合わせの基本については、本メルマガの第5回~第9回でご紹介しました。
簡単に言えば、1行のみ結果を返す問い合わせの場合は SELECT INTO文を用いる、また複数行を返す問い合わせの場合は明示カーソルを使う、ということです。
動的SQLでも1行のみ結果を返す問い合わせと、複数行の問い合わせではやり方が違います。
今回は、1行のみ結果を返す問い合わせを動的SQLで行う方法を説明します。
まず、動的SQLではなく、埋め込みSQLで1行結果を返す問い合わせを復習してみましょう。
すなわちSELECT INTO文です。
以下の例は、指定された社員の名前をSQL*Plusなどの画面に表示する処理です。
SQL> SET SERVEROUTPUT ON -- SQL*PLUS、SQL*Developerの画面出力を有効
SQL> DECLARE
2 V_ENAME EMP.ENAME%TYPE; -- 問い合わせ結果を格納するための変数
3 BEGIN
4 SELECT ENAME INTO V_ENAME FROM EMP WHERE EMPNO = 7934;
5 DBMS_OUTPUT.PUT_LINE('取得した社員名 = ' || V_ENAME);
6 END;
7 /
取得した社員名 = MILLER
PL/SQLプロシージャが正常に完了しました。
簡単に解説します。
●2行目・・・問い合わせ結果を格納するための変数を宣言しておいて、
●4行目・・・SELECT INTO文で、その変数に結果を代入し、
●5行目・・・その変数を画面に表示しているわけですね。
では、4行目のSELECT INTO文をシングルクォートで囲って文字列にして、EXECUTE IMMEDIATEで実行してみるとどうなるでしょうか?
SQL> DECLARE
2 V_ENAME EMP.ENAME%TYPE;
3 BEGIN
4 EXECUTE IMMEDIATE
5 'SELECT ENAME INTO V_ENAME FROM EMP WHERE EMPNO = 7934';
6 DBMS_OUTPUT.PUT_LINE('取得した社員名 = ' || V_ENAME);
7 END;
8 /
DECLARE
*
行1でエラーが発生しました。:
ORA-00905: キーワードがありません。
ORA-06512: 行4
ご覧のように、エラーとなります。
結論を言うと、SELECT INTO文をそのまま文字列にしてもダメなのです。
どこに問題があるかというと、INTO句です。
INTO句は動的SQL(文字列のSQL)の外でなければなりません。
以下のようにすれば、今度はOKです。
SQL> DECLARE
2 V_ENAME EMP.ENAME%TYPE;
3 BEGIN
4 EXECUTE IMMEDIATE
5 'SELECT ENAME FROM EMP WHERE EMPNO = 7934' INTO V_ENAME;
6 DBMS_OUTPUT.PUT_LINE('取得した社員名 = ' || V_ENAME);
7 END;
8 /
取得した社員名 = MILLER
PL/SQLプロシージャが正常に完了しました。
もうわかりましたね。
1行結果を返すSELECT文を動的SQLで実行する場合の構文は以下の通りです。
<1行返す動的SQの基本構文>
EXECUTE IMMEDIATE 'SELECT 列1,列2,・・' INTO 変数1,変数2,・・;
ポイントは動的SQL(文字列のSQL)の記述にはINTO句は含めず、文字列の外でINTO句を使い変数に値を代入することです。
もちろん、複数列を返す場合は、変数を指定する代わりにレコード型変数でもOKです。
INTO 変数1, 変数2,・・・
または、
INTO レコード変数
埋め込みのSELECT INTO 文と同じですね。ただINTO句の場所が違うだけです。
また結果として0行だったり、
複数行の場合は、エラー(それぞれ、NO_DATA_FOUND例外, TOO_MANY_ROWS例外)なので、エラーが起き得る場合は例外処理が必要な点も、まったく同じです。
ではここまでとします。
今回は1行の問い合わせでしたが、次回は複数行の問い合わせを動的SQLで行う方法を紹介します。
実は複数行の問い合わせの場合だけ、例外的に EXECUTE IMMEDIATE を用いません。
そのかわり、カーソル変数というもの使用することで、動的SQLを可能にしてます。
次回を楽しみにしていてください。
皆さん、今年もはりきってPL/SQLを勉強していきましょう!