図1●今回取り上げるSQL文の注意点<BR>(1)WHERE句を間違えてもエラーにはならず,記述した通りに実行してしまう。(2)非手続き型の言語であるため1行が長くなりやすく,演算の優先順位に注意が欠かせない。(3)検索結果のレコードの順番は,指定しない限り不定となる――。今回はこの3つの注意点を説明する
図1●今回取り上げるSQL文の注意点<BR>(1)WHERE句を間違えてもエラーにはならず,記述した通りに実行してしまう。(2)非手続き型の言語であるため1行が長くなりやすく,演算の優先順位に注意が欠かせない。(3)検索結果のレコードの順番は,指定しない限り不定となる――。今回はこの3つの注意点を説明する
[画像のクリックで拡大表示]
図2●例題のテーブル&lt;BR&gt;社員テーブルと部門テーブルの2つのテーブルを想定する
図2●例題のテーブル<BR>社員テーブルと部門テーブルの2つのテーブルを想定する
[画像のクリックで拡大表示]
図3●WHERE句を間違えたSQL文の例&lt;BR&gt;(1)はDELETE文でWHERE句を指定しなかった例。WHERE句を指定しないと,すべてのレコードが選択されたことになり,全レコードが削除されてしまう。(2)はUPDATE文でWHERE句を間違えた例。WHERE句を間違えても指定した通りに実行し,更新してはいけない個所まで更新してしまう。(3)はSELECT文においてテーブル間の結合条件を忘れた例。この場合,2つのテーブルの全組み合わせが行われ,期待しない結果が出力される
図3●WHERE句を間違えたSQL文の例<BR>(1)はDELETE文でWHERE句を指定しなかった例。WHERE句を指定しないと,すべてのレコードが選択されたことになり,全レコードが削除されてしまう。(2)はUPDATE文でWHERE句を間違えた例。WHERE句を間違えても指定した通りに実行し,更新してはいけない個所まで更新してしまう。(3)はSELECT文においてテーブル間の結合条件を忘れた例。この場合,2つのテーブルの全組み合わせが行われ,期待しない結果が出力される
[画像のクリックで拡大表示]

リレーショナル・データベース管理システム(RDBMS)を利用する上で欠かせないのが「SQL」の習得である。SQLの習得はSEにとって必須であるが,SQLはC/C++やJavaなどの汎用プログラミング言語とは異なる特性を持つため,利用には注意すべき点がある。本セミナーの目的は,SQLを使い始める際に陥りやすい失敗や気をつけるべき点などを解説することである。第1回は,条件句(WHERE句),演算の優先順位,検索結果のレコードの順番にまつわる注意点を説明する。

 これまでリレーショナル・データベース管理システム(RDBMS)ではなくファイルを使ってシステム開発を行っていた場合,RDBMSに対し“難しいもの”や“面倒なもの”と感じている方もいるのではないだろうか。しかし企業システムでは,RDBMSの利用が当たり前となり,システム・エンジニア(SE)にとってRDBMSに対する知識は常識となりつつある。

 RDBMSを使ってシステムを構築するには,RDBMSにアクセスするための専用言語「SQL」を習得する必要がある。SQLは,C/C++やJavaなどと同じようなプログラミング言語と考えて扱うと失敗しやすい。汎用のプログラミング言語の多くは「手続き型」と呼ばれ,処理手順をプログラミングする言語である。一方のSQLは「非手続き型」と呼ばれる言語で,処理手順はプログラミングしない。この違いが時には落とし穴になったり,注意すべき点となったりするからだ。

 本連載では,SQLを一通り学習したのだが,実践での経験が少ない方を想定読者にしている。というのも,SQLに慣れていない場合に起こしやすい失敗などがあるからだ。言語特性をしっかり把握していないために起きるのだが,残念ながらそのような点をまとめた書籍は多くない。市販の書籍では,SQLの文法を解説した書,パフォーマンス・チューニング・テクニックを解説した書はあるが,SQLを実際に利用する際に注意すべき点を説明した書は少ない。

 そこで本連載では,筆者のこれまでの経験の中での失敗や,SQLの利用に際し注意してほしい点などを説明する。説明で取り上げるRDBMSは,筆者の利用経験の多いOracleをベースとする。実行結果などはすべてOracle上で稼働したものになるが,基本的にはほかのRDBMSでも同様である。

 今回は,(1)条件句(WHERE句)にまつわる注意点,(2)1行が長くなることによる,演算の優先順位にまつわる注意点,(3)検索結果の順番にまつわる注意点——について解説する(図1[拡大表示])。取り上げるSQL文は,(図2[拡大表示])のテーブルに対するものである。社員の名前や部門コード,給与,住所などを格納した「社員テーブル(SYAIN_TBL)」と,部門名を格納した「部門テーブル(BUMON_TBL)」の2つのテーブルがある。

(1)WHERE句にまつわる注意点
  無指定は全指定と解釈される

 前述したように,SQLは非手続き型の言語である。処理手順をプログラミングしないため,間違えてプログラミングした場合,その解釈は手続き型の言語とかなり違ってくる。SQL文では特にWHERE句の間違いによる問題が起きやすい。

ちょっとしたミスで全レコードが削除される

 SQLを勉強してある程度覚え,ブラインドタッチ感覚で利用できるようになると,大きな落とし穴に落ちてしまうことがある。特にOracleのSQL*Plusのような対話型ツールを使っている場合,慣れてくると,改行の前にセミコロン(;)を無意識のうちに入力してしまうことがある。SQL*Plusではセミコロンは文の終わり,かつ,文の実行を意味するため,セミコロンと改行を入力した時点で自動的に,未完成なSQL文が実行されてしまうことになる。

 SQL文の場合,最初にSELECTUPDATEDELETEなどを記し,次にテーブルを指定するFROM句を書く。FROM句の前にセミコロンを指定しても,テーブルが指定されていないためSQL文はエラーとなり実行されない。問題が起きやすいのは,FROM句の次に指定するWHERE句である。WHERE句が無くてもSQL文はエラーにならず,それなりに実行してしまうからだ。

 最悪のパターンは,DELETE文でWHERE句を忘れた場合だ(図3[拡大表示])(1)。

DELETE
FROM SYAIN_TBL
WHERE SYAIN_NO = 5 ;
(社員Noが5の社員を社員テーブルから削除する)
とするところ,FROM句の改行の前にセミコロンを入力し,
DELETE
FROM SYAIN_TBL ;
(社員テーブルの全レコードを削除する)
としてしまった場合,RDBMSはエラーとせず,実行する。WHERE句を忘れた場合,手続き型の発想では,「条件を指定しなかったため,何も選択されない」となるが,非手続き型のSQLでは全く逆に解釈される。条件を指定しなかった場合,「条件を絞り込まなかった」と判断され,すべてのレコードが対象とみなされる。つまり,社員テーブル(SYAIN_TBL)の全レコードが削除されてしまうのだ。

 この例ではSQL*Plusを用いているため,COMMITコマンド,または,CREATE,ALTER,DROPなどのDDLコマンドを実行するまではトランザクションの途中として扱われ,ROLLBACKと入力すれば元の状態に戻すことができる。とはいえSQL*Plusのような対話型ツールを使ってRDBMSにアクセスしている場合,慎重にセミコロンを入力しよう。

 対策としては,SQL文をすべて記述してもセミコロンを入力せず,あえて改行してからセミコロンを入力する方法がある。例えば

DELETE
FROM SYAIN_TBL
WHERE SYAIN_NO = 5
;
とするのだ。このように入力する癖をつけておけば,不用意にセミコロンを入力するミスを減らすことができる。

WHERE句を間違えてもエラーにならない

 図3の(2)は,UPDATE文でWHERE句を間違えた例である。先ほどのDELETE文のように不用意にセミコロンを入力したため,WHERE句が不十分になってしまった。しかしこのSQL文はエラーにならず,指定した通りに実行する。この例の場合,本来は「基本給が25万円以上,かつ,手当てが5万円以上の社員」を選択するところ,「基本給が25万円以上の社員」を選択することになる。この場合,エラーにならず処理は正常に終了する。気付かずに見過ごす危険性が高い。

 SQL文は,論理的な間違いがなければエラーにならない。エラーになるのは,不等号の判定などにおいて,判定対象を忘れた場合などだ。SQL文がエラーにならなかったといって,安心していると思いもよらない処理が実行されることになりかねない。

 逆にSQL文のエラーで注意すべき点は,複数のテーブルで同じカラム名がある場合だ。RDBMSではテーブルが異なれば,同じカラム名をつけることができる。図2の社員テーブル(SYAIN_TBL)と部門テーブル(BUMON_TBL)には両方共,部門コード(BUMON_CD)のカラムがある。2つのテーブルを使う場合,どちらのテーブルの部門コード(BUMON_CD)なのかを指定しなければ不定になり,エラーとなる。

SELECT BUMON_CD, SYAIN_MEI
FROM SYAIN_TBL S, BUMON_TBL B
WHERE S.BUMON_CD = B.BUMON_CD ;
と記述した場合,SELECT句のすぐ後にある「BUMON_CD」は,SYAIN_TBLのカラムなのか,BUMON_TBLのカラムなのかの判断ができない。このSQL文はエラーとなる。正しくは,
SELECT B.BUMON_CD, SYAIN_MEI
FROM SYAIN_TBL S, BUMON_TBL B
WHERE S.BUMON_CD = B.BUMON_CD ;
などと記述し,どちらのテーブルのカラムなのかを明示しなければならない。

結合条件を忘れると全組み合わせとなる

 WHERE句にまつわる最後の注意点は,テーブルを結合する場合の結合条件である。結合条件はOracleではWHERE句で指定するのだが,結合条件を忘れてもエラーにならない。

 図3(3)は社員テーブルと部門テーブルを結合させて検索している。社員テーブルと部門テーブルは,部門コードを結合キーにしている。「部門コード10の社員を検索する」場合,

SELECT BUMON_MEI, SYAIN_MEI
FROM SYAIN_TBL S, BUMON_TBL B
WHERE S.BUMON_CD = 10
AND S.BUMON_CD = B.BUMON_CD ;
とするのが正しい。このうち,「S.BUMON_CD = B.BUMON_CD」の部分が結合条件である。もしこの結合条件を忘れ,
SELECT BUMON_MEI, SYAIN_MEI
FROM SYAIN_TBL S, BUMON_TBL B
WHERE S.BUMON_CD = 10 ;
としてしまった場合,エラーにならず,誤った結果を返す。結合条件を忘れた場合,部門コードをキーに結合されないため,2つのテーブルの全組み合わせを指定したことになる。全組み合わせの中から「BUMON_CD = 10」の条件に合う検索を行うことになり,正しくない結果が出力される*1


玉川 敏一(たまがわ としいち)
シーズ・ラボ ITソリューション部 セクションマネージャー

1980年代後半からリレーショナル・データベースを利用している。当初は汎用機上のRDBMSであったが,1994年(株)シーズ・ラボ入社以来,Oracle一筋になる。同社にてシステム開発,研修インストラクタ,サポート窓口,技術支援業務を行い,Oracleと深くかかわっている。Oracle Master Platinum資格を保有。