検索したデータの形成
データの結合
例えば、ユーザーのフルネームの一覧を取得したいとする。
フルネームは"family_name"と"first_name"から成るので、以下のようなSQLを書くことが出来る。
1 2 |
SELECT family_name, first_name
FROM users
|
すると実行結果は以下のように表示される。
名前の一覧は取得出来たが、取得したいのはフルネームなのでカラムが分かれているのは気になる。
そこで、"family_name"と"first_name"を併せて1つのカラムに表示させたい。
そんなときに使うのがCONCAT
関数。
CONCAT関数
CONCAT
関数は複数の文字列を連結させることが出来る関数。
1 |
CONCAT(文字列1, 文字列2, ……)
|
連結させる文字列の中にNULL
があった場合、結果は必ずNULL
となってしまう。
実際にSELECT
句の中で使って、"family_name"と"first_name"を連結させ、表示してみる。
1 2 |
SELECT CONCAT(family_name, first_name)
FROM users
|
以下のように結果が表示されていれば正常にSQL文が実行出来ている。
このようにいくつかのデータを用いて1つのデータを作りたいときはCONCAT
関数を使用すると、結果が扱いやすいものとなる。
検索結果のカラム名の変更
CONCAT
関数により文字列を連結したが、検索結果のカラム名がSQL文そのままとなっており、少し見にくいです。
そこで、検索結果のカラム名を変更する。
AS句
SELECT
句でデータを取得するとき、AS
句を併用すると、そのカラムに別名を付けることが出来る。
1 |
SELECT 取得するデータ AS 別名
|
このときの"別名"が検索結果ではカラム名として表示される。
実際にAS
句を使ってどのように表示が変わるかを見てみる。
1 2 |
SELECT CONCAT(family_name, first_name) AS "名前"
FROM users
|
以下のように結果が表示されていれば正常にSQL文が実行出来ている。
なお、AS
は省略することが出来るので、以下のように書くことも出来る。
1 2 |
SELECT CONCAT(family_name, first_name) "名前"
FROM users
|
実際に実行してみると、同じくカラム名が変更出来ていることが確認出来る。
重複する行の除外
shiftテーブルには「何日の、何時に、id何番のユーザーがシフトに入ったか」という情報が入っている。
またシフトは1コマ2時間制となっており、つまりレコードが1行存在することは、1コマ分シフトに入っている、ということとなる。
ここで、2015年7月1日にシフトに入った人の"user_id"を取得したいという場面を考えてみる。
以下のようなSQL文が書ける。
1 2 3 |
SELECT user_id
FROM shifts
WHERE date = "2015-07-01"
|
しかし、実行結果を見てみると、同じuser_idが複数回表示されてしまっているところがある。
これは1日のうち、2コマ分シフトに入っている(レコードが2行ある)人がいるからである。
そこで、user_idの重複をなくした検索結果を表示させてみる。
DISTINCTキーワード
DISTINCT
キーワードを使用すると、指定したカラムの値が重複する行を除外してデータを取得することが出来る。
1 |
SELECT DISTINCT カラム名
|
例えばSELECT DISTINCT user_id
と指定すると、以下のように"user_id"カラムが重複している行が除外される。
DISTINCT
キーワードを使用してSQL文を書きなおしてみる。
1 2 3 |
SELECT DISTINCT user_id
FROM shifts
WHERE date = "2015-07-01"
|
SELECT
句がuser_id
からDISTINCT user_id
となったことで、"user_id"が重複する行を除いたSQL文となっている。
以下のように結果が表示されていれば正常にSQL文が実行出来ている。
レコードのグループ化
DISTINCT
に少し似た、GROUP BY
句について。
GROUP BY句
GROUP BY
句を使用すると、指定したカラムが同じ値を持つデータを1つのグループとしてまとめることが出来る。
1 |
GROUP BY カラム名
|
例えばGROUP BY user_id
と指定すると、以下のように"user_id"カラムの値が同じレコードが、それぞれグループ化される。
上の例では、GROUP BY
句を使わない左側は5行のデータが表示されるが、GROUP BY
句を使用した右側では2行のデータのみが表示されるようになる。
このとき、表示されていないデータもきちんとそのグループに保持されている。
実際にSQL文を記述して、GROUP BY
句の挙動を確認してみる。
1 2 3 4 |
SELECT user_id
FROM shifts
WHERE date = "2015-07-01"
GROUP BY user_id
|
このSQL文では、まず"shifts"テーブルから"date"が"2015-07-01"のレコード取得している。
そして"user_id"が同じ値のレコードをグループ化し、そのグループ化された一覧の"user_id"のみを表示する、ということが行われている。
以下のように結果が表示されていれば正常にSQL文が実行出来ている。
実行結果を見てみると、先ほどDISTINCT
を使用したときと同じものになっていること。
この結果だけを見ると、DISTINCT
を使えば良いのではないか、と思われるかもしれないが、GROUP BY
を使うことには、そのグループ単位で集計した結果を取得することが出来る、という利点がある。
グループ化されたデータを用いて、データが何件あるか数えるということをやってみる。
レコードの数を数える
グループ化されたデータは、何件のレコードがグループ化されているかというデータを持っており、それを取得出来る関数がCOUNT
関数。
COUNT関数
COUNT
関数はグループ化されたデータに対して使用することが出来る集計関数の一つ。COUNT
関数はカラムを指定して使用することで、そのカラムの値がNULL
でないデータの行数を取得することが出来る。
1 |
SELECT COUNT(カラム名)
|
このとき、カラム名の指定でワイルドカードを使用し以下のように記述すると、行の値が全てNULL
であるレコードも含め、その行数を取得する。
1 |
SELECT COUNT(*)
|
そしてGROUP BY
句を併用している場合は、各グループが何行のレコードを持っているかというデータを取得することが出来る。
実際にSQL文を実行してCOUNT
関数の挙動を確認してみる。
先ほどは"2015-07-01"に誰がシフトに入ったか、という情報を取得することが出来た。
今度は"2015-07-01"に誰が、何コマずつシフトに入ったか、という情報を取得出来るようにしてみる。
1 2 3 4 |
SELECT user_id, COUNT(*)
FROM shifts
WHERE date = "2015-07-01"
GROUP BY user_id
|
上記のSQL文は、先ほどGROUP BY
句で実行したSQL文の、SELECT
句にCOUNT(*)
を追加したもの。
今回は特にNULL
の行を除きたいといったことはないため、COUNT
のカラム指定は*
となっている。
以下のように結果が表示されていれば正常にSQL文が実行出来ている。
"user_id"の隣の列に、1
や2
という数字が出てきた。
これがその"user_id"の人が何コマシフトに入ったかをカウントした結果である。
ですが、カウントしたものを表示している列の名前がCOUNT(*)
となってしまっている。
これではこの列の数字が何を表しているのかが分かりにくい。
そのため、このカラムに別名として"コマ数"という名前を付けてあげる。
1 2 3 4 5 6 |
SELECT
user_id,
COUNT(*) "コマ数"
FROM shifts
WHERE date = "2015-07-01"
GROUP BY user_id
|
ついでにSELECT
句を要素ごとに改行。
複数の要素を取得したい場合は、要素ごとに改行をしタブキーを押してインデントを付けてあげると、SQL文が可読性の高いものとなるので、ここも意識して書いてみると良い。
以下のように結果が表示されていれば正常にSQL文が実行出来ている。
これで実行結果が分かりやすくなった。
グループ化されたデータに使用できる集計関数は他にも、平均を求めるAVG
、最大値を求めるMAX
、最小値を求めるMIN
といったものが存在する。
複雑なデータの検索
テーブルの結合
COUNT
関数を使用してSQL文を書くことで、id何番のユーザーが、何コマシフトに入ったかという情報を得られるようになった。
ですが、idが何番のユーザー、と言われてもそれが誰かは分かりにくい。
そのため、検索結果にuser_idではなく、そのユーザーの名前を表示してあげるように変更する。
いまは"shifts"テーブルのデータを扱って検索をしているが、このテーブルにユーザーの名前の情報はない。
ではユーザーの名前の情報がどこにあるかというと、"users"テーブルなので、どうにかしてそこから情報を持ってくる必要がある、ということが分かる。
そこで、"shifts"テーブルから"users"テーブルの情報を取得するために、2つのテーブルを結合させるJOIN
を使う。
JOIN
JOIN
を使用すると、指定したそれぞれのテーブルの、カラムの値が一致するデータを結合することが出来る。
JOIN
句はFROM
句のあとに記述し、結合の対象となるテーブルを指定する。
そしてそれぞれのテーブルの結合するカラムをON
の後に=
(イコール)を用いて指定する。
1 2 |
すると、SQL文は以下のようになる。
1 2 |
FROM shifts
JOIN users ON shifts.user_id = users.id
|
このSQL文は、
「"shifts"テーブルに"users"テーブルを結合させる。
"shifts"テーブルの各行には、その行の"user_id"が"users"テーブルの"id"と一致する行を結合させる。」
という意味になる。
例えば"shifts"テーブルの"user_id"が1の行には、"users"テーブルの"id"が1の行を結合させる。
また、ON
の後にshifts
やusers
とテーブル名を再度書くのは冗長である。
ではどうするべきかというと、各テーブルに別名を付け、その名前で扱うのが良い。
そのため、上記のSQL文は以下のように書きなおすことが出来る。
1 2 |
FROM shifts s
JOIN users u ON s.user_id = u.id
|
このSQL文ではFROM
やJOIN
でテーブル名を指定する際、ついでにそのテーブルの別名も指定している。
このとき別名は、"shifts"テーブルならs
、"users"テーブルならu
のように、各テーブル名の頭文字を取ってあげるのが一般的。
実際にSQL文を実行し、テーブルが結合される様子を見てみる。
1 2 3 4 5 6 7 8 |
SELECT
user_id,
COUNT(*) "コマ数",
u.*
FROM shifts s
JOIN users u ON s.user_id = u.id
WHERE date = "2015-07-01"
GROUP BY user_id
|
先ほどのSQL文に、4行目のu.*
と、
6行目のJOIN users u ON s.user_id = u.id
が追加されている。
6行目のJOIN
句は上記のと同じ。
4行目のu.*
というのは、"users"テーブルの全てのカラムを取得する、という意味になる。
実行結果を見てみると、各行の右側にその行の"user_id"と同じ"id"のユーザーの情報が結合されていることが分かる。
これで"users"テーブルのデータを結合することが出来た。
あとは実行結果が"名前"と"コマ数"だけになるよう、SQL文を書き直す。
1 2 3 4 5 6 7 |
SELECT
CONCAT(family_name, first_name) "名前",
COUNT(*) "コマ数"
FROM shifts s
JOIN users u ON s.user_id = u.id
WHERE date = "2015-07-01"
GROUP BY user_id
|
先ほどからSELECT
句のみを変更した。
名前を表示させるために、この章の一番上で学習したCONCAT
関数を使用している。
以下のように結果が表示されていれば正常にSQL文が実行出来ている。
これで"2015-07-01"に、誰が、何コマシフトに入ったか、という情報を表示することが出来た。
今回使用したJOIN
はINNER JOIN
と呼ばれているもの。
そしてJOIN
にはLEFT JOIN
やRIGHT JOIN
など、結合法則の違った他の種類のJOIN
が存在する。
取得したいデータによってはそちらのJOIN
を使用したほうが適切という場合もある。
検索結果を用いた検索
今度は"2015-07-01"にシフトに入っていない人の一覧を取得したいとする。
シフトに入っていない人は、ユーザーの一覧から、シフトに入っている人を除くことで求められる。
これをSQL文で表現しようとすると、シフトに入っている人を検索し、その検索結果を用いてさらに検索をする必要がある。
そういったときにはサブクエリというものを使用する。
サブクエリ
サブクエリとは、ある検索結果を使用して別のSQL文を実行する仕組みのこと。
実際に"2015-07-01"にシフトに入っていない人を取得するSQL文を作成する工程を見ていく。
まずは"2015-07-01"にシフトに入っている人の一覧を取得。
ここはDISTINCT
キーワードを使って以下のようなSQL文が書ける。
1 2 3 |
SELECT DISTINCT user_id
FROM shifts
WHERE date = "2015-07-01"
|
このSQL文の実行結果は以下のようになる。
この赤枠の部分が"2015-07-01"にシフトに入っている人のリスト。
では、こういったリストがあるとして、
"users"テーブルから、idがリストに含まれない行のみを取得するSQL文を見てみる。
SQL文はWHERE
句で、NOT IN
を使用して以下のように記述することが出来る。
1 2 3 |
SELECT *
FROM users
WHERE id NOT IN (リスト)
|
あとはこのSQL文のリスト
という部分に、
先ほどの"2015-07-01"にシフトに入っている人のリストを取得するSQL文を埋め込むだけ。
1 2 3 4 5 6 7 |
SELECT *
FROM users
WHERE id NOT IN (
SELECT DISTINCT user_id
FROM shifts
WHERE date = "2015-07-01"
)
|
以下のように結果が表示されていれば正常にSQL文が実行出来ている。
実際に先ほどのリストと見比べてみても、きちんとリストにないidのユーザーのみを取得出来ていることが分かる。
サブクエリはWHERE
句で使用されることが多く、またイメージがし易いため、今回はWHERE
句で使用したが、
サブクエリはSELECT
句やFROM
句で使うことも出来る。