Androidアプリで、SQLを書くけど、上手な書き方ってあるの?
Webアプリケーション系の技術者であれば、SQLは必須の技術ですが、組込み系の技術者の多くは、おそらく今まではSQLを書く、という事はあまり無かったのではないでしょうか?
私は今でこそWeb系なので、SQLを頻繁に書いたり治したりしますが、携帯電話の組込みをしていたときは、全然書いた事ありませんでした。
基本情報技術者試験やソフト開発技術者試験で、ちょろっと基本的な内容を勉強した程度です。
という事で、見やすいSQLの書き方、というのを紹介したいと思います。
Androidアプリで複数のテーブルを使うという大々的なデータベースを使う、という事はあまりないかもしれませんが、複数のテーブルを元にデータを取得する、というときの上手な書き方を紹介したいと思います。
もちろん、私の主観ですので、必ずしもそうではないかもしれません。
私ごときが偉そうに、、と自分で思うのですが・・・ご容赦を。(苦笑)
SQLの基本的用法
Androidアプリで使うSQLは、以下が主なパターンだと思います。
テーブルの作成、削除
create tableや、delete tableの事です。
レコードの追加、更新、削除
insert、delete、update文の事です。
レコードの検索
select文の事です。
スポンサーリンク
Androidアプリに関わらず、Webアプリでも、アプリケーション側から実行するSQLというのは大体上記のパターンです。
テーブルの追加や削除は、アプリケーションから行う事もできますが、大体は環境構築時にテーブル作成を行う事が多いです。
テーブルの追加、削除、レコードの追加、更新、削除というのは、誰が書いても同じようなSQLになると思います。
ですので、今回は上記には触れません。
肝心なのは、データ検索時のselect文です。
SQLのselect文の書き方のコツ
select文の基本形は以下のような形です。
select 列名 from テーブル名 where 条件 order by 並び順
この基本形で事足りるのであれば、難しくはありません。
今回は、複数のテーブルを使う場合のSQLの書き方のコツを紹介したいと思います。
以下にような二つの表があるとします。
社員マスタ(社員番号、部署番号、社員名)
部署マスタ(部署番号、部署名)
これで、部署名と社員名の一覧を出力したい場合は、以下のようになります。
select 部署マスタ.部署名,社員マスタ.社員名
from 社員マスタ,部署マスタ
where 社員マスタ.部署番号 = 部署マスタ.部署番号
私は、上記のように、from句にテーブルを複数書く、という書き方が大嫌いです。
上記のように2つだけならいいのですが、10個ぐらいのテーブルをくっつけたときに、何のテーブルのどのカラムと、何のテーブルのどのカラムが結び付いているのか?がさっぱりわからなくなります。
あとでSQLを読む人はもちろん、書いた本人ですら、わけのわからないまま結合していくハメになります。
という事で私は、JOIN句を使います。
以下のようになります。
select 部署マスタ.部署名,社員マスタ.社員名
from 社員マスタ
inner join 部署マスタ on 社員マスタ.部署番号 = 部署マスタ.部署番号
一般的に、from句に複数テーブルを記入すると可読性が落ちると言われています。
その可読性を高めてくれるのが、joinです。
私は、めちゃくちゃ長いSQLを分析した事があります。
そのとき、ほとんどはこのJOINを使って書かれているので、多少時間を掛ければ分析できるのですが、from句でガンガン複数のテーブルを書かれているSQLは、分析不可能に近いです・・・。
あまりfrom句でガンガン結んでるSQLはあまりないのですが、人によってはそういう人も・・・。
JOINを使うのは、もう一つ理由があります。
例えば、今回のケースでは異常系になるかもしれませんが、
部署マスタにない部署番号が登録されている社員がいたとします。
この場合、最初のSQLでは部署マスタに登録されている社員のレコードは取得できません。
2つめのSQLでもそうなのですが、以下のように書き換えるだけでそういうレコードが取得できます。
select 部署マスタ.部署名,社員マスタ.社員名
from 社員マスタ
left outer join 部署マスタ on 社員マスタ.部署番号 = 部署マスタ.部署番号
inner join から、left outer joinに書き換えると、部署マスタにない部署番号と人は、部署名がNULLとしてレコードが取得できるわけです。
逆に、そういう人のレコードを取得したくない場合は、inner joinを使えばいいのです。
こういう調整も簡単にできるので、joinをしっかり使いこなせれば、どんな複数のテーブルを結合させなければならないSQLでも書けてしまいます。
まだ理由があります。
今度は、部署番号が1の人たちだけを取得したいとします。
一つ目のSQLだと以下のようになります。
select 部署マスタ.部署名,社員マスタ.社員名
from 社員マスタ,部署マスタ
where 社員マスタ.部署番号 = 部署マスタ.部署番号
and 社員マスタ.部署番号 = 1
join句を使ったSQLだと以下のようになります。
select 部署マスタ.部署名,社員マスタ.社員名
from 社員マスタ
inner join 部署マスタ on 社員マスタ.部署番号 = 部署マスタ.部署番号
where 社員マスタ.部署番号 = 1
where句は、取得するレコードの条件だけを指定すべきだと思うのですが、複数テーブルをfrom句に書いた場合は、where句には必然的に、結合条件も書かなければなりません。
もはやこうなってくると、where句のこの条件文は何を意味している?という事がわかりにくくなります。
一方、join句を使っているSQLの場合は、明確です。
「inner join テーブル名 on」の後の条件が、部署マスタを結合する条件で、where句は、取得した結果を絞り込む条件です。
なんとわかりやすい!と思いませんか?
今回言いたかったことは、JOIN句を理解して使いこなそう!という事です。
SQLiteDatabaseクラスのrawQuery()メソッドを使って、生のSQLを使うときには、SQLの可読性が大事になると思います。
SQLiteDatabaseクラスには、query()というメソッドもありますが、複雑なSQLを使うときは、rawQuery()の方が使いやすいでしょう。
簡単なSQLでも、SQLが理解できれば、rawQuery()メソッドの方が使いやすいのではないかと思います。
なお、AndroidのSQLでは、「inner join」、「left outer join」は使用できますが、「right outer join」、「full outer join」は未サポートらしいです。
でも、個人的には、サポートされてても、これらを使う事はないです。
普段の仕事で、right outer join、full outer joinなんて使ったこと無いですね。
inner join、left outer joinが使えれば、どんな条件のselect文でも書けます。
この知識は、Androidに関わらず、DBを使用する全てのシステムのSQLにおいていえることです。
joinをサポートしているDBであれば、join句を使用する事をおススメします。
現在のDBは、大体サポートされてるはずです。