SQLの高度な問合せ
- はじめに
- Windows Subsystem for LinuxでLinux環境を整えた場合
- SQLite3を用いたSQLの高度な問合せ
- 発展:
- リンク
- 戻る
はじめに_
本ページはSQLite3を用いて、SQLの使い方を演習形式で学ぶことを目的としている。
このため、端末(ターミナル)上でSQLite3を実行できるようにしておく必要がある。最低限、以下の内容まで終わらせておくこと。
また、以下の内容は理解していることとする。
Windows Subsystem for LinuxでLinux環境を整えた場合_
VcxSrv(XLaunch)の起動_
gnome-terminalを利用するので、まず、VcxSrv(XLaunch)を起動する。VcxSrvのインストールで設定したVcxSrvのアイコンをクリックし、VcxSrvを起動する。
gnome-terminalの起動_
gnome-terminalの利用で設定したgnome-terminalを起動する。
Ubuntuターミナルを起動し、ターミナル上で以下を起動する。
% gterm &
SQLite3を用いたSQLの高度な問合せ_
今回のチュートリアルで使うデータベース_
今日の作業ディレクトリの作成とデータベースをダウンロードする。
% cd % mkdir -p AppSQL % cd AppSQL % which wget /usr/bin/wget % wget http://www.aise.ics.saitama-u.ac.jp/~gotoh/Lectures/TUS_IP/download/edu.db % wget http://www.aise.ics.saitama-u.ac.jp/~gotoh/Lectures/TUS_IP/download/create_table.sql % ls create_table.sql edu.db
表:students(学生)
sid(学生ID) | sname(氏名) | highschool(出身高校) |
501 | Keitha Teague | Urawa High School |
601 | Marybeth Boros | Warabi High School |
602 | Shasta Pepper | Saitama Sakae High School |
701 | Peg Massingill | Shukutoku Yono High School |
702 | Eugenio Dengler | Johoku High School |
表:credits(修得単位)
sid(学生ID) | degree (学年) | unit(学年ごとの修得単位数) |
501 | 1 | 48 |
501 | 2 | 40 |
501 | 3 | 38 |
601 | 1 | 39 |
701 | 1 | 37 |
702 | 1 | 45 |
表:classes (履修)
sid(学生ID) | cname(科目名) | unit(単位数) |
501 | Graduation Research | 8 |
601 | Web Design | 2 |
601 | Image Processing | 4 |
601 | Computer Graphics | 2 |
603 | Database | 6 |
603 | Web Design | 2 |
701 | Information Retrieval | 2 |
701 | Programming Lang Java | 4 |
702 | Programming Lang Java | 4 |
702 | Web Design | 2 |
テーブルの名前と定義を確認する。
% sqlite3 edu.db sqlite> .tables classes credits students sqlite> .schema CREATE TABLE students( sid char(4), sname char(30), highschool char(30), PRIMARY KEY(sid) ); CREATE TABLE credits( sid char(4), degree int, unit int, PRIMARY KEY(sid,degree) ); CREATE TABLE classes( sid char(4), cname char(30), unit int, PRIMARY KEY(sid,cname) );
テーブル内の値を確認する。
sqlite> SELECT * FROM students; 501|Keitha Teague|Urawa High School 601|Marybeth Boros|Warabi High School 602|Shasta Pepper|Saitama Sakae High School 701|Peg Massingill|Shukutoku Yono High School 702|Eugenio Dengler|Johoku High School sqlite> SELECT * FROM credits; 501|1|48 501|2|40 501|3|38 601|1|39 701|1|37 702|1|45 sqlite> SELECT * FROM classes; 501|Graduation Research|8 601|Web Design|2 601|Image Processing|4 601|Computer Graphics|2 603|Database|6 603|Web Design|2 701|Information Retrieval|2 701|Programming Lang Java|4 702|Programming Lang Java|4 702|Web Design|2
distict句とorder by句_
SELECT DISTINCT column_list FROM table_list JOIN table ON join_condition WHERE row_filter ORDER BY column LIMIT count OFFSET offset GROUP BY column HAVING group_filter;
SELECT文にDISTINCT句をつけると問合せ結果から重複を取り除くことができる。以下の問合せを行ってみる。
sqlite> SELECT cname FROM classes; Graduation Research Computer Graphics Image Processing Web Design Database Web Design Information Retrieval Programming Lang Java Programming Lang Java Web Design sqlite> SELECT DISTINCT cname FROM classes; Graduation Research Computer Graphics Image Processing Web Design Database Information Retrieval Programming Lang Java
ORDER BY句を用いるとSELECTの問合せ結果を指定した列(フィールド)について辞書順にならべることができる。辞書順には昇順(ASC)と降順(DESC)が存在し、標準は昇順になっている。以下の問合せを行ってみる。
sqlite> SELECT cname FROM classes; Graduation Research Computer Graphics Image Processing Web Design Database Web Design Information Retrieval Programming Lang Java Programming Lang Java Web Design sqlite> SELECT cname FROM classes ORDER BY cname; Computer Graphics Database Graduation Research Image Processing Information Retrieval Programming Lang Java Programming Lang Java Web Design Web Design Web Design sqlite> SELECT cname FROM classes ORDER BY cname DESC; Web Design Web Design Web Design Programming Lang Java Programming Lang Java Information Retrieval Image Processing Graduation Research Database Computer Graphics
DISTINCT句とORDER BY句を同時につかうこともできる。
sqlite> SELECT DISTINCT cname FROM classes ORDER BY cname; Computer Graphics Database Graduation Research Image Processing Information Retrieval Programming Lang Java Web Design sqlite> SELECT DISTINCT cname FROM classes ORDER BY cname DESC; Web Design Programming Lang Java Information Retrieval Image Processing Graduation Research Database Computer Graphics
和集合演算(union)、共通集合演算(intersect)、差集合演算(except)、直積_
構文は以下のとおり。
query_1 UNION [ALL] query_2 UNION [ALL] query_3 ...;
UNIONを使うと問合せ(query)結果の和集合演算を行うことができる。
sqlite> SELECT cname FROM classes WHERE sid = '701'; Information Retrieval Programming Lang Java sqlite> SELECT cname FROM classes WHERE sid = '702'; Programming Lang Java Web Design sqlite> SELECT cname FROM classes WHERE sid = '701' ...> UNION ...> SELECT cname FROM classes WHERE sid = '702'; Information Retrieval Programming Lang Java Web Design
INTERSECT句を使うと問合せ(query)結果の共通集合演算を行うことができる。
sqlite> SELECT cname FROM classes WHERE sid = '701'; ~出力省略~ sqlite> SELECT cname FROM classes WHERE sid = '702'; ~出力省略~ sqlite> SELECT cname FROM classes WHERE sid = '701' ...> INTERSECT ...> SELECT cname FROM classes WHERE sid = '702'; Programming Lang Java
EXCEPT句を使うと問合せ(query)結果の差集合演算を行うことができる。
sqlite> SELECT cname FROM classes WHERE sid = '701'; ~出力省略~ sqlite> SELECT cname FROM classes WHERE sid = '702'; ~出力省略~ sqlite> SELECT cname FROM classes WHERE sid = '701' ...> EXCEPT ...> SELECT cname FROM classes WHERE sid = '702'; Information Retrieval sqlite> SELECT cname FROM classes WHERE sid = '702' ...> EXCEPT ...> SELECT cname FROM classes WHERE sid = '701'; Web Design
直積演算はSELECT文で実行できる。
sqlite> SELECT * FROM students; 501|Keitha Teague|Urawa High School 601|Marybeth Boros|Warabi High School 602|Shasta Pepper|Saitama Sakae High School 701|Peg Massingill|Shukutoku Yono High School 702|Eugenio Dengler|Johoku High School sqlite> SELECT * FROM classes; 501|Graduation Research|8 601|Web Design|2 601|Image Processing|4 601|Computer Graphics|2 603|Database|6 603|Web Design|2 701|Information Retrieval|2 701|Programming Lang Java|4 702|Programming Lang Java|4 702|Web Design|2 sqlite> SELECT * FROM students, classes; 501|Keitha Teague|Urawa High School|501|Graduation Research|8 501|Keitha Teague|Urawa High School|601|Web Design|2 501|Keitha Teague|Urawa High School|601|Image Processing|4 501|Keitha Teague|Urawa High School|601|Computer Graphics|2 501|Keitha Teague|Urawa High School|603|Database|6 ~省略~ 702|Eugenio Dengler|Johoku High School|701|Information Retrieval|2 702|Eugenio Dengler|Johoku High School|701|Programming Lang Java|4 702|Eugenio Dengler|Johoku High School|702|Programming Lang Java|4 702|Eugenio Dengler|Johoku High School|702|Web Design|2
CROSS JOIN句を用いても直積演算を実行できる。
以下の4つの文は同じ結果となる。
sqlite> SELECT * FROM students, classes; ~出力省略~ sqlite> SELECT * FROM students JOIN classes; ~出力省略~ sqlite> SELECT * FROM students INNER JOIN classes; ~出力省略~ sqlite> SELECT * FROM students CROSS JOIN classes; ~出力省略~
内部結合(JOIN / INNER JOIN)_
結合対象となるテーブルから結合条件を満たす行だけを取り出す演算を内部結合という。結合条件を満たさない行は欠落する。
SQL-89とSQL-92でそれぞれ規格化された構文があり、ほとんどのDBMSで双方に対応している。
sqlite> SELECT * FROM students AS S, credits AS C WHERE S.sid = C.sid; -- (1) 501|Keitha Teague|Urawa High School|501|1|48 501|Keitha Teague|Urawa High School|501|2|40 501|Keitha Teague|Urawa High School|501|3|38 601|Marybeth Boros|Warabi High School|601|1|39 701|Peg Massingill|Shukutoku Yono High School|701|1|37 702|Eugenio Dengler|Johoku High School|702|1|45 sqlite> SELECT * FROM students AS S JOIN credits AS C ON S.sid = C.sid; -- (2) 501|Keitha Teague|Urawa High School|501|1|48 501|Keitha Teague|Urawa High School|501|2|40 501|Keitha Teague|Urawa High School|501|3|38 601|Marybeth Boros|Warabi High School|601|1|39 701|Peg Massingill|Shukutoku Yono High School|701|1|37 702|Eugenio Dengler|Johoku High School|702|1|45 sqlite> SELECT * FROM students JOIN credits USING (sid); -- (3) 501|Keitha Teague|Urawa High School|1|48 501|Keitha Teague|Urawa High School|2|40 501|Keitha Teague|Urawa High School|3|38 601|Marybeth Boros|Warabi High School|1|39 701|Peg Massingill|Shukutoku Yono High School|1|37 702|Eugenio Dengler|Johoku High School|1|45
(1)はSQL-89で規格化された書き方。FROM句に結合対象のテーブルをすべて列挙し、WHERE句で結合条件を指定する。
(2)to(3)はSQL-92で規格化された書き方。「テーブル名 JOIN テーブル名 ON 結合条件」あるいは「テーブル名 JOIN テーブル名 USING (結合する属性)」という構文で2つのテーブルを結合する。
なお、文中の「テーブル名 AS 略記号」は結合条件を記載する際にテーブル名を略記するためのもの。
(1)と(2)はsidの列が複数個表示される。(3)はsidの列が一つだけ表示される。すなわち、(3)は自然結合演算の結果となっている。
3つ以上のテーブルも結合することができる。
sqlite> SELECT * FROM students AS S, credits AS C, classes AS R ...> WHERE S.sid = C.sid AND C.sid = R.sid; -- (4); 501|Keitha Teague|Urawa High School|501|1|48|501|Graduation Research|8 501|Keitha Teague|Urawa High School|501|2|40|501|Graduation Research|8 501|Keitha Teague|Urawa High School|501|3|38|501|Graduation Research|8 601|Marybeth Boros|Warabi High School|601|1|39|601|Computer Graphics|2 601|Marybeth Boros|Warabi High School|601|1|39|601|Image Processing|4 601|Marybeth Boros|Warabi High School|601|1|39|601|Web Design|2 701|Peg Massingill|Shukutoku Yono High School|701|1|37|701|Information Retrieval|2 701|Peg Massingill|Shukutoku Yono High School|701|1|37|701|Programming Lang Java|4 702|Eugenio Dengler|Johoku High School|702|1|45|702|Programming Lang Java|4 702|Eugenio Dengler|Johoku High School|702|1|45|702|Web Design|2 sqlite> SELECT * FROM students AS S JOIN credits AS C ON S.sid = C.sid ...> JOIN classes AS R ON C.sid = R.sid; --(5) 501|Keitha Teague|Urawa High School|501|1|48|501|Graduation Research|8 501|Keitha Teague|Urawa High School|501|2|40|501|Graduation Research|8 501|Keitha Teague|Urawa High School|501|3|38|501|Graduation Research|8 601|Marybeth Boros|Warabi High School|601|1|39|601|Computer Graphics|2 601|Marybeth Boros|Warabi High School|601|1|39|601|Image Processing|4 601|Marybeth Boros|Warabi High School|601|1|39|601|Web Design|2 701|Peg Massingill|Shukutoku Yono High School|701|1|37|701|Information Retrieval|2 701|Peg Massingill|Shukutoku Yono High School|701|1|37|701|Programming Lang Java|4 702|Eugenio Dengler|Johoku High School|702|1|45|702|Programming Lang Java|4 702|Eugenio Dengler|Johoku High School|702|1|45|702|Web Design|2 sqlite> SELECT * FROM students JOIN credits USING (sid) ...> JOIN classes USING (sid); -- (6) 501|Keitha Teague|Urawa High School|1|48|Graduation Research|8 501|Keitha Teague|Urawa High School|2|40|Graduation Research|8 501|Keitha Teague|Urawa High School|3|38|Graduation Research|8 601|Marybeth Boros|Warabi High School|1|39|Web Design|2 601|Marybeth Boros|Warabi High School|1|39|Image Processing|4 601|Marybeth Boros|Warabi High School|1|39|Computer Graphics|2 701|Peg Massingill|Shukutoku Yono High School|1|37|Information Retrieval|2 701|Peg Massingill|Shukutoku Yono High School|1|37|Programming Lang Java|4 702|Eugenio Dengler|Johoku High School|1|45|Programming Lang Java|4 702|Eugenio Dengler|Johoku High School|1|45|Web Design|2
なお、「JOIN」と記載している部分は「INNER JOIN」と記載してもよい。
sqlite> SELECT * FROM students AS S INNER JOIN credits AS C ON S.sid = C.sid; -- (7) ~出力省略~ sqlite> SELECT * FROM students INNER JOIN credits USING (sid); -- (8) ~出力省略~
外部結合(OUTER JOIN)_
内部結合により結合条件を満たす行が取り出されるが、外部結合では結合条件を満たさない行も取り出される。結合条件を満たしていない行については空値としてNULL値が挿入される。このため、外部結合を行う場合には該当する列にNOT NULL条件を付与してはいけない。
外部結合には左外部結合(LEFT OUTER JOIN)、右外部結合(RIGHT OUTER JOIN)、完全外部結合(FULL OUTER JOIN)の3種類がある。
内部結合に対応させる形で外部結合の働きを確かめてみる。
sqlite> SELECT * FROM students AS S INNER JOIN classes AS R ON S.sid = R.sid; 501|Keitha Teague|Urawa High School|501|Graduation Research|8 601|Marybeth Boros|Warabi High School|601|Web Design|2 601|Marybeth Boros|Warabi High School|601|Image Processing|4 601|Marybeth Boros|Warabi High School|601|Computer Graphics|2 701|Peg Massingill|Shukutoku Yono High School|701|Information Retrieval|2 701|Peg Massingill|Shukutoku Yono High School|701|Programming Lang Java|4 702|Eugenio Dengler|Johoku High School|702|Programming Lang Java|4 702|Eugenio Dengler|Johoku High School|702|Web Design|2 sqlite> SELECT * FROM students AS S LEFT OUTER JOIN classes AS R ON S.sid = R.sid; -- (9) 左外部結合。classesの行に空値が挿入される。 501|Keitha Teague|Urawa High School|501|Graduation Research|8 601|Marybeth Boros|Warabi High School|601|Computer Graphics|2 601|Marybeth Boros|Warabi High School|601|Image Processing|4 601|Marybeth Boros|Warabi High School|601|Web Design|2 602|Shasta Pepper|Saitama Sakae High School||| 701|Peg Massingill|Shukutoku Yono High School|701|Information Retrieval|2 701|Peg Massingill|Shukutoku Yono High School|701|Programming Lang Java|4 702|Eugenio Dengler|Johoku High School|702|Programming Lang Java|4 702|Eugenio Dengler|Johoku High School|702|Web Design|2 sqlite> SELECT * FROM students AS S RIGHT OUTER JOIN classes AS R ON S.sid = R.sid; -- (10) 右外部結合。studentsの行に空値が挿入される。 Error: RIGHT and FULL OUTER JOINs are not currently supported sqlite> SELECT * FROM students AS S FULL OUTER JOIN classes AS R ON S.sid = R.sid; -- (11) 完全外部結合。studentsの行に空値が挿入される。 Error: RIGHT and FULL OUTER JOINs are not currently supported
(10)と(11)のSQL文はSQLite3では実行できない。
重複したsidを取り除く場合はUSING句を用いる。
sqlite> SELECT * FROM students INNER JOIN classes USING (sid); -- (12) 501|Keitha Teague|Urawa High School|Graduation Research|8 601|Marybeth Boros|Warabi High School|Web Design|2 601|Marybeth Boros|Warabi High School|Image Processing|4 601|Marybeth Boros|Warabi High School|Computer Graphics|2 701|Peg Massingill|Shukutoku Yono High School|Information Retrieval|2 701|Peg Massingill|Shukutoku Yono High School|Programming Lang Java|4 702|Eugenio Dengler|Johoku High School|Programming Lang Java|4 702|Eugenio Dengler|Johoku High School|Web Design|2 sqlite> SELECT * FROM students LEFT OUTER JOIN classes USING (sid); -- (13) 左外部結合。classesの行に空値が挿入される。 501|Keitha Teague|Urawa High School|Graduation Research|8 601|Marybeth Boros|Warabi High School|Computer Graphics|2 601|Marybeth Boros|Warabi High School|Image Processing|4 601|Marybeth Boros|Warabi High School|Web Design|2 602|Shasta Pepper|Saitama Sakae High School|| 701|Peg Massingill|Shukutoku Yono High School|Information Retrieval|2 701|Peg Massingill|Shukutoku Yono High School|Programming Lang Java|4 702|Eugenio Dengler|Johoku High School|Programming Lang Java|4 702|Eugenio Dengler|Johoku High School|Web Design|2
クイズ:集合演算と結合演算(2021年11月4日の講義はここまで)_
表 studentsと表 classesを左外部結合する文(13)は以下のとおりである。
sqlite> SELECT * FROM students LEFT OUTER JOIN classes USING (sid); -- (13) 左外部結合。classesの行に空値が挿入される。
SQLite3は右外部結合が実装されていないが、右外部結合の結果および完全外部結合の結果をSQL文を用いて得ることができる。表 studentsと表 classesの右外部結合および完全外部結合と同等の結果をえるSQL文を書け。
ヒント
- SELECT文では問合せ結果の列(フィールド、属性)の順番を任意に指定することができる。
- 完全外部結合は左外部結合と右外部結合の和集合演算である。
- 解答例
2021年11月11日の講義の開始時作業_
先週の作業ディレクトリとデータベースファイルが存在するか確かめる。存在する場合は、それをそのまま使う。
% cd % ls AppSQL/ create_table.sql edu.db % cd AppSQL % sqlite3 edu.db
先週のディレクトリとデータベースファイルが存在しない場合は作業ディレクトリの作成とデータベースのダウンロードを行う。
% cd % mkdir -p AppSQL % cd AppSQL % which wget /usr/bin/wget % wget http://www.aise.ics.saitama-u.ac.jp/~gotoh/Lectures/TUS_IP/download/edu.db % wget http://www.aise.ics.saitama-u.ac.jp/~gotoh/Lectures/TUS_IP/download/create_table.sql % ls create_table.sql edu.db % sqlite3 edu.db
副問合せ(SELECT文の入れ子)_
SELECT文中にSELECT文を入れ子にすることができる。このとき、入れ子のSELECT文を副問合せ、外側のSELECT文を主問合せという。
副問合せを用いたSQL文では、まず、副問合せのSELECT文が実行され、その結果を用いて主問合せの文が実行される。主問合せに連携するための演算子として、比較演算子、INおよびNOT IN演算子、EXISTSおよびNOT EXISTS演算子、ANY(SOME)演算子、そして、ALL演算子がある。
比較演算子_
全学生の修得単位数の平均値よりも多く単位を修得している学生IDを副問合せを用いて取得する。以下のSQL文を実行する。
sqlite> SELECT sid, unit FROM credits; -- 学生IDと修得単位数を列挙する。 501|48 501|40 501|38 601|39 701|37 702|45 sqlite> SELECT avg(unit) FROM credits; -- avg()は平均値を求める集約関数。後ほど説明 41.1666666666667 sqlite> SELECT sid, unit FROM credits ...> WHERE unit >= (SELECT avg(unit) FROM credits); -- 全学生の修得単位数の平均値よりも多く単位を修得している学生ID 501|48 702|45
比較演算子は、=、<、>、>=、=<などがある。
INおよびNOT IN演算子_
副問合せの結果に検討対象の属性が含まれているかどうかを判定するためにIN演算子を用いる。含まれていないことを判定するためにはNOT IN演算子を用いる。
以下の例は、Web Designを履修している学生の学生ID(sid)と名前(sname)を表示するSQL文である。
sqlite> SELECT sid FROM classes WHERE cname = 'Web Design'; -- Web Designを履修している学生一覧 601 603 702 sqlite> SELECT sid, sname FROM students; -- 学生IDと名前の一覧 501|Keitha Teague 601|Marybeth Boros 602|Shasta Pepper 701|Peg Massingill 702|Eugenio Dengler sqlite> SELECT sid, sname FROM students ...> WHERE sid IN (SELECT sid FROM classes WHERE cname = 'Web Design'); 601|Marybeth Boros 702|Eugenio Dengler sqlite> SELECT sid, sname FROM students ...> WHERE sid NOT IN (SELECT sid FROM classes WHERE cname = 'Web Design'); 501|Keitha Teague 602|Shasta Pepper 701|Peg Massingill
INとNOT IN演算子は副問合せをつなぐためでなく、通常のSELECT文でも利用できる。
sqlite> SELECT * FROM students; 501|Keitha Teague|Urawa High School 601|Marybeth Boros|Warabi High School 602|Shasta Pepper|Saitama Sakae High School 701|Peg Massingill|Shukutoku Yono High School 702|Eugenio Dengler|Johoku High School sqlite> SELECT * FROM students WHERE highschool IN ('Urawa High School', 'Johoku High School'); 501|Keitha Teague|Urawa High School 702|Eugenio Dengler|Johoku High School
単位数が4、6、8の科目名を降順で列挙することもできる。
sqlite> SELECT cname, unit FROM classes; Graduation Research|8 Web Design|2 Image Processing|4 Computer Graphics|2 Database|6 Web Design|2 Information Retrieval|2 Programming Lang Java|4 Programming Lang Java|4 Web Design|2 sqlite> SELECT DISTINCT cname, unit FROM classes ...> WHERE unit IN (4, 6, 8) ORDER BY unit DESC; Graduation Research|8 Database|6 Image Processing|4 Programming Lang Java|4
EXISTSおよびNOT EXISTS演算子_
EXISTS演算子は副問合せの結果が空かどうかを判定する。空でない(1行以上ある)ならば真(true)、そうでなければ偽(false)となる。NOT EXISTSはEXISTSの反対の値をとる。
学生テーブル(students)と履修テーブル(classes)の双方に登場する学生の学生IDと名前を列挙する。
sqlite> SELECT * FROM students AS S, classes AS R WHERE S.sid = R.sid; -- 内部結合 501|Keitha Teague|Urawa High School|501|Graduation Research|8 601|Marybeth Boros|Warabi High School|601|Web Design|2 601|Marybeth Boros|Warabi High School|601|Image Processing|4 601|Marybeth Boros|Warabi High School|601|Computer Graphics|2 701|Peg Massingill|Shukutoku Yono High School|701|Information Retrieval|2 701|Peg Massingill|Shukutoku Yono High School|701|Programming Lang Java|4 702|Eugenio Dengler|Johoku High School|702|Programming Lang Java|4 702|Eugenio Dengler|Johoku High School|702|Web Design|2 sqlite> SELECT sid, sname FROM students AS S ...> WHERE EXISTS (SELECT * FROM classes AS R WHERE S.sid = R.sid); 501|Keitha Teague 601|Marybeth Boros 701|Peg Massingill 702|Eugenio Dengler sqlite> SELECT sid, sname FROM students AS S ...> WHERE NOT EXISTS (SELECT * FROM classes AS R WHERE S.sid = R.sid); 602|Shasta Pepper
ANY(SOME)演算子_
副問合せの結果を用いた条件式のうち、1つでも条件に当てはまるものがあれば真(true)を返す演算子、SOME演算子はANY演算子と同じ働きの演算子である。
以下の例は履修(classes)に存在する学生ID(sid)について学生IDと名前(sname)を列挙するSQL文である。
sqlite> SELECT sid, sname FROM students ...> WHERE sid = ANY (SELECT sid FROM classes); Error: near "SELECT": syntax error sqlite> SELECT sid, sname FROM students ...> WHERE sid = SOME (SELECT sid FROM classes); -- 上と同義 Error: near "SELECT": syntax error
SQLite3においてはANY演算子およびSOME演算子は使用できない。今回の例はIN演算子を用いることで実現できる。
sqlite> SELECT sid, sname FROM students ...> WHERE sid IN (SELECT sid FROM classes); 501|Keitha Teague 601|Marybeth Boros 701|Peg Massingill 702|Eugenio Dengler
ALL演算子_
ALL演算子はANY演算子とは異なり、副問合せの結果すべてが条件式と一致するときに真(true)となる。
以下の例は履修(classes)に含まれない学生ID(sid)について学生IDと名前(sname)を列挙するSQL文である。
sqlite> SELECT sid, sname FROM students ...> WHERE sid != ALL (SELECT sid FROM classes); Error: near "ALL": syntax error
SQLite3においてはALL演算子は使用できない。今回の例はNOT IN演算子を用いることで実現できる。
sqlite> SELECT sid, sname FROM students ...> WHERE sid NOT IN (SELECT sid FROM classes); 602|Shasta Pepper
FROM句での副問合せ_
FROM句では複数のテーブルを列挙することができる。このテーブル列挙の代わりに副問合せの結果を用いることもできる。
まず、修得単位数(creditsのunitの値)が44単位以上の行を列挙するSELECT文を考える。
sqlite> SELECT * FROM credits WHERE unit >= 44; 501|1|48 702|1|45
このSELECT文を副問合せとしてFROM句に埋め込む。これは修得単位数が44単位以上の学生IDと氏名を列挙するSELECT文である。
sqlite> SELECT DISTINCT S.sid, sname, degree, unit ...> FROM students AS S, ...> (SELECT sid, degree, unit FROM credits WHERE unit >= 44) AS VTB ...> WHERE S.sid = VTB.sid; 501|Keitha Teague|1|48 702|Eugenio Dengler|1|45
また、SELECT文の列の列挙部分(「SELECT 列の列挙 FROM テーブルの列挙」)に副問合せを用いることもできる。
先ほどの例と同様に修得単位数が44単位以上の学生とその名前を列挙する。
sqlite> SELECT sid, degree, unit, (SELECT sname FROM students AS S ...> WHERE S.sid = C.sid) FROM credits AS C WHERE unit >= 44; 501|1|48|Keitha Teague 702|1|45|Eugenio Dengler
副問合せの練習1_
学生ID(sid)が701と702の学生が両方とも履修している科目名(cname)を抽出するSELECT文をANY演算子を用いて作ると以下のようになる。
sqlite> SELECT DISTINCT cname FROM classes WHERE sid = ANY ('701', '702'); Error: no such function: ANY
これをIN演算子を用いて書き直せ。
副問合せの練習2_
学生ID(sid)が701の学生が履修しているが、702の学生は履修していない科目を抽出するSELECT文を考える。exceptを用いたSELECT文は以下のようになる。
sqlite> SELECT sid, cname FROM classes WHERE sid = '701'; 701|Information Retrieval 701|Programming Lang Java sqlite> SELECT sid, cname FROM classes WHERE sid = '702'; 702|Programming Lang Java 702|Web Design sqlite> SELECT cname FROM classes WHERE sid = '701' ...> EXCEPT ...> SELECT cname FROM classes WHERE sid = '702'; Information Retrieval
ALL演算子を用いると以下のようになる。
sqlite> SELECT cname FROM classes WHERE sid = '701' AND cname != ALL (SELECT cname FROM classes WHERE sid = '702'); Error: near "ALL": syntax error
IN演算子あるいはNOT IN演算子を用いて同等の働きをするSQL文を作れ。
集約関数_
DBMSの製品の多くは組み込み関数を提供している。SELECT文においてGROUP BY句やHAVING句と一緒に組み込み関数を用いる場合が多い。
COUNT関数_
列名を引数としてその列に含まれる行数をカウントする。一般に COUNT(*)はテーブルに含まれる行数をカウントするために使われる。
classesテーブルの行数をカウントする場合は以下のように入力する。
sqlite> SELECT * FROM classes; 501|Graduation Research|8 601|Web Design|2 601|Image Processing|4 601|Computer Graphics|2 603|Database|6 603|Web Design|2 701|Information Retrieval|2 701|Programming Lang Java|4 702|Programming Lang Java|4 702|Web Design|2 sqlite> SELECT COUNT(*) FROM classes; 10
「COUNT(*) AS 略称」という表記で名前をつけることができる。
sqlite> SELECT COUNT(*) AS num_of_reg FROM classes; 10
classesには科目ごとにどの学生が履修しているのかの情報が格納されている。一般に学生は複数の科目を履修する。このため、履修している学生のユニークな数を得たい場合はDISTINCT句を用いてカウントすることができる。
sqlite> SELECT COUNT(DISTINCT sid) AS num_of_reg_students FROM classes; 5
max, min関数_
列に含まれる最大値と最小値を得るための関数がMAX関数とMIN関数である。
creditsテーブルから修得単位数が最大の学生および最小の学生を列挙する。
sqlite> SELECT sid, MAX(unit) FROM credits; 501|48 sqlite> SELECT sid, MIN(unit) FROM credits; 701|37
AVG, STDDEV, VARIANCE関数_
統計処理を行う際に平均値、標準偏差、分散を用いることが多い。平均値はAVG関数で、標準偏差はSTDDEV関数で、分散はVARIANCE関数で得ることができる。
sqlite> SELECT AVG(unit) FROM credits; 41.1666666666667 sqlite> SELECT STDDEV(unit) FROM credits; Error: no such function: STDDEV sqlite> SELECT VARIANCE(unit) FROM credits; Error: no such function: VARIANCE
SQLite3ではSTDDEV関数、VARIANCE関数は提供されていない。
GROUP BY句とHAVING句_
GROUP BY句はテーブルの特定の列で行をグループ化し、個々のグループに対して集約関数を適用するときに使用する。以下の例は科目名ごとに履修学生の数を数えるSQL文である。
sqlite> SELECT * FROM classes; 501|Graduation Research|8 601|Web Design|2 601|Image Processing|4 601|Computer Graphics|2 603|Database|6 603|Web Design|2 701|Information Retrieval|2 701|Programming Lang Java|4 702|Programming Lang Java|4 702|Web Design|2 sqlite> SELECT cname,COUNT(*) FROM classes GROUP BY cname; Computer Graphics|1 Database|1 Graduation Research|1 Image Processing|1 Information Retrieval|1 Programming Lang Java|2 Web Design|3
HAVING句は集約された値に対して条件を付与するときに用いる。たとえば、履修登録者が2名以上の科目だけを列挙したい場合は以下のように入力する。
sqlite> SELECT cname,COUNT(*) FROM classes GROUP BY cname; Computer Graphics|1 Database|1 Graduation Research|1 Image Processing|1 Information Retrieval|1 Programming Lang Java|2 Web Design|3 sqlite> SELECT cname,COUNT(*) FROM classes ...> GROUP BY cname HAVING count(*) >= 2; Programming Lang Java|2 Web Design|3
sqlite> SELECT sid, sum(unit) FROM credits ...> GROUP BY sid HAVING sum(unit) >= 80; 501|126
creditsテーブルには学年ごとの総修得単位数が記載されているため、creditsテーブルに登録されている学生IDごとの行数をつかうことで現在の学年を得ることができる。
sqlite> SELECT sid, degree FROM credits; 501|1 501|2 501|3 601|1 701|1 702|1 sqlite> SELECT sid, count(sid)+1 AS current_degree FROM credits GROUP BY sid; 501|4 601|2 701|2 702|2
ビュー(再)_
ビューの使い方_
前回紹介したビューは今回紹介した複雑な問合せを繰り返し実行するときに非常に役立つ。
たとえば、年間の修得単位数が40単位以上の学生だけを列挙するビュー students_40を作成してみる。
sqlite> CREATE VIEW students_40 ...> AS SELECT S.sid, sname, highschool, degree, unit FROM students AS S, ...> credits AS C ...> WHERE S.sid = C.sid and unit >= 40; sqlite> .tables classes credits students students_40 sqlite> SELECT * FROM students_40; 501|Keitha Teague|Urawa High School|1|48 501|Keitha Teague|Urawa High School|2|40 702|Eugenio Dengler|Johoku High School|1|45
作成したビューを用いて結合演算を行うこともできる。年間の修得単位数が40以上の学生が履修している科目名を列挙する。
sqlite> SELECT DISTINCT cname FROM students_40 AS S, classes AS R ...> WHERE S.sid = R.sid; Graduation Research Programming Lang Java Web Design
ビューに対して集約関数を適用することもできる。students_40に含まれる学生と全学生の年間平均修得単位数の差を表示する。
sqlite> SELECT AVG(S.unit), AVG(C.unit), AVG(S.unit)-AVG(C.unit) ...> FROM students_40 AS S, credits AS C; 44.3333333333333|41.1666666666667|3.16666666666667
集約関数を含むビュー_
ビューの定義に集約関数を含めることもできる。以下の例は学生の学年と現時点での積算修得単位数を列挙するビューである。
sqlite> CREATE VIEW total_credits ...> AS SELECT sid, count(sid)+1 AS current_degree, sum(unit) AS total_unit ...> FROM credits GROUP BY sid; sqlite> .tables classes credits students students_40 total_credits sqlite> SELECT * FROM total_credits; 501|4|126 601|2|39 701|2|37 702|2|45
作成したビューを用いて現時点で80単位以上修得している学生を列挙する。
sqlite> SELECT * FROM total_credits WHERE total_unit >= 80; 501|4|126
更新可能ビュー_
ビューはテーブルと同じように使うことができるが、格納されている値の変更(INSERT, UPDATE, DELETE)については特定の条件を満たしたビューのみ実行できる。
その条件は以下のとおり
- ビューの列と実テーブルの列とが1対1対応になっている
- ビューを構成する実テーブルにおいて、実テーブルには存在するがビューに現れない列に対してNOT NULL制約がかけられていない、あるいはデフォルト値が定められている(ビュー経由の更新ではNULLが挿入されるため)
別の言い方をすると以下の条件のうちどれかを満たしているビューは更新可能ビューではない。
- 集約関数、GROUP BY句、HAVING句を使用している
- DISTINCT、UNIONなどの集合演算を使用している
- 派生列(たとえば COUNT(sid)+1など)を使用している
先の例で作ったstudents_40は更新可能ビューである。
sqlite> .schema students_40 CREATE VIEW students_40 AS SELECT S.sid, sname, highschool, degree, unit FROM students AS S, credits AS C WHERE S.sid = C.sid and unit >= 40 /* students_40(sid,sname,highschool,degree,unit) */; sqlite> SELECT * FROM students_40; 501|Keitha Teague|Urawa High School|1|48 501|Keitha Teague|Urawa High School|2|40 702|Eugenio Dengler|Johoku High School|1|45
更新してみる。students_40に登録されている学生の履修単位数を1増やす。
sqlite> UPDATE students_40 SET unit = unit + 1; Error: cannot modify students_40 because it is a view
ただし、SQLite3ではビューからの更新に対応していない。
ビューの練習1_
studentsテーブルから学生ID(sid)が7で始まる学生だけを抜き出したビュー students7を作成せよ。なお、ある列においてある文字列を含む行を抜き出す条件式は以下のように記述する。
- 特定の文字列から始まる場合: 「列名 like "文字列%"」
- 特定の文字列で終わる場合: 「列名 like "%文字列"」
- 特定の文字列が含まれる場合:「列名 like "%文字列%"」
- 解答例
ビューの練習2_
以下の条件を満たすビューを作成せよ
- ビュー名が current_students
- 列が学生ID (sid)、学生名 (sname)、現在の学年 (degree)、出身高校 (highschool)
ヒント
- 現在の学年の求め方は今日のページ内にある
- 解答例