[SQL] SELECT文を使ったデータの操作
皆さん、お疲れ様です!! 仕事にプライベートに充実した日々を送っていますか? さて、前回の第1回目ではORACLE MASTERの資格と、学習用の環境の重要性についてご説明させていただきました。すでに構築した環境を使いたくて、ウズウズしていますよね(笑) 今回はHRスキーマのEMPLOYEES表を使用します。第1回目の内容を見て環境を構築した方は、以下を参考にHRユーザーのパスワード再設定を行っておいてください。
c:\> sqlplus / as sysdba SQL> alter user hr identified by hr account unlock; ユーザーが変更されました。
ではさっそく今回を含め計4回、12c SQL 基礎(試験番号:1Z0-061)対策として問題を解きながら解説していきたいと思います。
データベース内のデータの取得、データの制限とソート
データの取得、制限、ソートを行うSELECT文の基本構文を押さえる
さて、突然ですが! 以下の条件を満たしたSELECT文をサッサッと書けますか?
以下の条件を満たしたSELECT文を記述してください。
HRスキーマのEMPLOYEES表からSALARYが13000以上の人のEMPLOYEES_IDとFIRST_NAME、SALARYを表示してください。結果はSALARYにて降順で表示してください。
この問題が????という人は、残念ながらSELECT文の構文が頭に入ってないということになります。基本構文が分からなければ、もちろん試験問題も解けません。今回使用するSELECT文の基本構文と問題1の答えを以下に示しますので、わからなかった方は実機で色々と確認しながら頭に叩き込みましょう!
SELECT 列名, 列名, 列名, …… FROM 表名 WHERE 制限する検索条件 ORDER BY 並び替えの列 [ASC|DESC]; (※1)
※1:ASCは昇順に並び替え、DESCは降順に並び替え。ASCおよびDESCは省略可で、省略すると昇順に並び替え
列名や表名、検索条件など必要な情報を入力して、実行します。
※2:コマンドプロンプトからSQL*PLUSを起動し、HRユーザーに接続(connect hr/hr)した状態で実行
ORACLE MASTERは、Goldまではすべて選択式となっており、記述式問題はありません。しかしSQL文の構文を理解していないと、正しい選択肢は選べません。実際にSQL文を実行しながらしっかり学習してくださいね。それでは問題を解いていきながら、基本構文に+αした知識をつけていきましょう!
データの取得に関する問題
従業員表(EMPLOYEES)から部門ID(DEPARTMENT_ID列)50番の職種ID(JOB_ID列)について調べた結果、以下が出力されました。
この結果を表示するSQL文はどれになりますか。2つ選択してください。
- (a)SELECT DISTINCT job_id "Job Name", department_id FROM employees WHERE department_id = 50;
- (b)SELECT DISTINCT job_id "Job Name", DISTINCT department_id FROM employees WHERE department_id = 50;
- (c)SELECT DISTINCT job_id 'Job Name', department_id FROM employees WHERE department_id = 50;
- (d)SELECT DISTINCT job_id AS "Job Name", department_id FROM employees WHERE department_id = 50;
- (e)SELECT DISTINCT job_id AS 'Job Name', department_id FROM employees WHERE department_id = 50;
- (f)SELECT DISTINCT job_id AS Job Name, department_id FROM employees WHERE department_id = 50;
たくさんのSQL文が並んでいるからといって「うゎ……」と思わないように(笑)。見るべきポイントを解説します。この問題で注目すべき点は、SELECTの結果の列見出しの部分と、重複行を排除するDISTINCTのキーワードです。先に列見出しから説明します。通常、SELECT結果に表示される列見出しは、列名が大文字で表示されます。この問題ではSELECT句で指定しているdepartment_idの列見出しが大文字で表示されていますね。この列見出しの表示を任意に変更したい場合に列別名を指定します。特に難しいことはなく、以下のように指定します。
・スペースを使って指定 SELECT 列名 列別名 FROM 表名; ・AS のキーワードを使って指定 SELECT 列名 AS 列別名 FROM 表名;
列別名にスペースが含まれている場合や、大文字・小文字を区別したい場合は、指定している列別名を2重引用符で囲めばOKです。さて、この知識でかなり選択肢が絞れると思いますので問題をもう一度見てみましょう。job_id の列別名としてJob Nameを指定していますが、大文字・小文字の区別がありスペースも入っています。つまり二重引用符が必要ということですね。これだけで (a), (b), (d) に絞り込むことができました。残りはDISTINCTです。DISTINCTは重複した値の行を排除して表示できるので、列にどのような種類があるかを調べる際に有効です。DISTINCTの使い方も難しくありません。
SELECT DISTINCT 列名[, 列名, 列名….] FROM 表;
DISTINCTはSELECT句の中で先頭に一度指定するので、(b)が誤りと分かります。以上の内容から、正解は(a)と(d)になります。
では次の問題にいきましょう。
次のSQL文の内、エラーとなるものをすべて選択してください。
- (a)SELECT employee_id, salary*12 "Annual Salary" FROM employees WHERE salary*12 > 150000 ORDER BY 2 DESC;
- (b)SELECT employee_id, salary*12 "Annual Salary" FROM employees WHERE salary*12 > 150000 ORDER BY Annual Salary DESC;
- (c)SELECT employee_id, salary*12 "Annual Salary" FROM employees WHERE salary*12 > 150000 ORDER BY salary*12 DESC;
- (d)SELECT employee_id, salary*12 "Annual Salary" FROM employees WHERE "Annual Salary" > 150000 ORDER BY salary*12 DESC;
- (e)SELECT employee_id, salary*12 "Annual Salary" FROM employees WHERE salary*12 > 150000 ORDER BY "Annual Salary" DESC;
- (f)SELECT employee_id, salary*12 "Annual Salary" FROM employees WHERE "Annual Salary" > 150000 ORDER BY "Annual Salary" DESC;
ここでも注目すべき点を見ていきましょう。すべてのSQL文に「salary*12」とありますが、SELECT句には四則演算の算術式が使えることを覚えておいてください。今回は乗算(*)したsalary列に対して、問題2で学習した列別名を指定しています。この問題は、列別名の指定に関する知識を問う内容となっています。
ではWHERE句の部分から見ていきます。WHERE句の構文として条件式には列別名を指定することはできません。それを押さえつつ選択肢を見てみると、(d)と(f)は条件式に列別名「Annual Salary」を指定しているので、この2つはエラーになりますね。
次にORDER BY句をみていきましょう。ORDER BY句は「列」以外に「式」や「列別名」も指定することができ、(a)のようにSELECT句で指定している列の順番を示す数字(この例では2)を入れることも可能です。選択肢を見てみるとORDER BY句の指定は(a)~(f)すべてにおいてよさそうに見えますが、構文的に間違っているのは(b)です。Annual Salaryは2重引用符で囲まれている列別名ですので、ORDER BY句の指定でも統一する必要があるからです。今回はエラーとなるSQL文をピックアップするので、正解は(b)と(d)と(f)ということになります。
どんどんいきましょう。次はNULLについての問題です。
従業員表(EMPLOYEES)から歩合(COMMISSION_PCT列)が設定されていない従業員の情報を確認したい。正しい結果が返ってくるものを選択してください。
- (a)SELECT employee_id, first_name || last_name, commission_pct FROM employees WHERE commission_pct = NULL;
- (b)SELECT employee_id, first_name || last_name, commission_pct FROM employees WHERE commission_pct <> NULL;
- (c)SELECT employee_id, first_name || last_name, commission_pct FROM employees WHERE commission_pct IS NULL;
- (d)NULLを条件式として指定することはできない。
まずNULLとは? ですが、「値が何もなく不明なもの」と考えてください。数値のゼロや空白とも異なります。何も示さないものですから、「=」「<」「>」といった等号や不等号で比較することはできません。この特殊なNULLに対しては、専用の演算子である「IS NULL」演算子を使うことで評価することが可能です。反対に「NULL値以外」という条件を指定したい場合は、否定を意味するNOT演算子を入れて「IS NOT NULL」と指定します。以上の内容から正解は(c)となります。なお、(a)や(b)を実行した場合、文自体はエラーとならず実行されますが、条件として正しく評価されないので行を取得することはできません。この点も合わせて覚えておきましょう。選択肢のSELECT句にある連結演算子(||)も実機にてぜひ動作確認しておいてくださいね。
それではもう1問、NULLに関する問題です。
従業員表(EMPLOYEES)から歩合(COMMISSION_PCT列)が設定されていない人の情報を結果の先頭に表示する必要があります。正しいSQL文をすべて選択してください。
- (a)SELECT employee_id, last_name, commission_pct FROM employees ORDER BY commission_pct;
- (b)SELECT employee_id, last_name, commission_pct FROM employees ORDER BY commission_pct ASC;
- (c)SELECT employee_id, last_name, commission_pct FROM employees ORDER BY commission_pct DESC;
- (d)SELECT employee_id, last_name, commission_pct FROM employees ORDER BY commission_pct NULLS LAST;
- (e)SELECT employee_id, last_name, commission_pct FROM employees ORDER BY commission_pct NULLS FIRST;
- (f)SELECT employee_id, last_name, commission_pct FROM employees ORDER BY commission_pct ASC NULLS FIRST;
どうでしょうか? この問題で押さえるべきポイントは以下の2つです。
- NULL値を含む列をORDER BYで指定した際の出力結果
- NULLS FIRSTとNULLS LASTの動作について
NULL値は昇順ソートの場合、結果の末尾に表示されます。昇順ソートはORDER BY句で明示的にASCを指定する、あるいは省略した際のデフォルト動作だったことを思い出してください。次にNULLS FIRSTとNULLS LASTの動作は、以下のようになります。
NULLS FIRSTを指定 -> NULL値の表示順が結果の先頭に来る NULLS LASTを指定 -> NULL値の表示順が結果の末尾に来る
その名の通りの動作! という感じでしょうか。ここで問題をもう一度見てみましょう。(a)と(b)は昇順ソートになり、NULLが結果の末尾に表示されるので誤りです。また(d)のNULLS LASTも同様に、NULLは末尾表示となるので間違いですね。
悩むのは(f)だと思います。こちらの動作は、まず歩合が設定されている行で昇順ソート(ASC)し、その後NULLS FIRSTの動きとなるため、NULL値を含む行は先頭に出てきます。問題文の条件には一致しますね。したがって正解は(c)と(e)と(f)となります。
では今回の最後の問題です!
従業員表(EMPLOYEES)の全従業員から給与の高い6名の従業員の情報を出力するSQL文を選択してください。ただし、給与が同じ従業員がいた場合は6名を超えても表示する必要があります。
- (a)SELECT employee_id, last_name, salary FROM employees ORDER BY salary FETCH FIRST 6 PERCENT ROWS ONLY;
- (b)SELECT employee_id, last_name, salary FROM employees ORDER BY salary FETCH FIRST 6 PERCENT ROWS WITH TIES;
- (c)SELECT employee_id, last_name, salary FROM employees ORDER BY salary FETCH FIRST 6 ROWS ONLY;
- (d)SELECT employee_id, last_name, salary FROM employees ORDER BY salary FETCH FIRST 6 ROWS WITH TIES;
少し難しかったでしょうか? 各選択肢の内容はORDER BYで指定した列の最初の何行、あるいは何%の情報を取得するかというもので、Oracle 12cの行制限の新機能FETCH句を使用しています。それでは内容について説明していきましょう。
選択肢のSQL文の違いとして、まずはFETCH FIRST 6 ROWSと、FETCH FIRST 6 PERCENT ROWSという指定がありますね。ROWSは指定した行数を戻す動作となっており、PERCENT ROWSはSELECT文で選択された全行数に対して戻される行数の割合(%)を指定します。その他の構文の違いとしては、SQLの文末にあるONLYとWITH TIESがあります。この指定が問題文の最後の箇所になる、「給与が同じ従業員がいた場合は6名を超えても表示する必要があります」に関係してきそうですね。
ONLYとWITH TIESの動作は、以下にまとめてみました。
ONLY -> 厳密に指定した行数だけを結果として表示 WITH TIES -> 同じ値が複数存在している場合、指定した行数を超えて表示
うーん…… 文章と睨めっこだけしても何かイメージがわかない…… そんな時こそ実機検証です! 実行例として、問題文にある(c)と(d)の実行結果を以下に示します
いかがでしょう。実際に動かしてみるとONLYとWITH TIESの動作の違いについて理解が深まるのではないでしょうか。
以上の内容を踏まえ問題文を再確認すると、「6名の従業員」と指定があり、かつ給与が同じ従業員がいた場合は「6名を超えて表示が必要」とありますので、正解は(d)となります。
最後に、行制限を行うもう一つの機能であるOFFSET構文も覚えておきましょう。OFFSETは、指定した行数をスキップしてから何行読み込むか、という動作になります。
SELECT employee_id, salary FROM employees ORDER BY salary DESC OFFSET 5 ROWS FETCH NEXT 5 ROWS ONLY;
最初の5行を読み飛ばし(スキップ)(OFFSET 5 ROWS )
次の5行のみを表示(FETCH NEXT 5 ROWS ONLY)
いかがだったでしょうか? 選択肢が多数ある場合や複雑そうな問題文に見えたとしても、個々の構文をしっかりと学習し、注目すべきポイントを押さえることで正解の選択肢を導き出せることがお分かりいただけたと思います。必ず実機検証も行って知識を深めてください。新たな発見があったり、予期せぬエラーがあったり!? などなど、いろいろと体験できて楽しくなってくること間違いなしです(笑)。
それでは連載第2回目の内容は以上となります。第3回目も引き続きSQL文の学習となりますので、頑張っていきましょう!
* OracleとJavaは、Oracle Corporation 及びその子会社、関連会社の米国及びその他の国における登録商標です。文中の社名、商品名等は各社の商標または登録商標である場合があります。