クエリビルダ
データベースからレコードを取得する際、SQL文を組み立てて問い合わせをします。
この組み立てを簡単にできる機能のことをクエリビルダといいます。
LaravelやcakePHPなどのフレームワークが取り入れていますが、ここではLaravelでの使用方法に限定していこうと思います。
Laravelでのデータ操作は、「クエリビルダ」、「Eloquent」、「SQL」の3つの方法があります。
これらの実装方法にはどのような違いがあるのでしょうか。
クエリビルダとEloquentとSQLの違いについて
それぞれの違いについて、以下のデータを使用して確認してみましょう。
usersテーブル
| id | name | tel | birthday | address | |
|---|---|---|---|---|---|
| 1 | 田中 太郎 | taro@test.jp | 080-1234-5678 | 2000-01-01 | 沖縄 |
| 2 | 佐藤 二郎 | jiro@test.jp | 090-8765-4321 | 1969-05-07 | 東京 |
| 3 | 北島 三郎 | saburo@test.jp | 090-1111-0000 | 1936-10-04 | 東京 |
クエリビルダ
DBファサードを使用して、テーブルに対して直接データベース操作を行う実装方法です。
クエリビルダを利用する場合は、DBクラスのtable()メソッドを使用する必要があります。
メリット: 柔軟性が高いため、複雑なクエリを書くことができる。(複数のテーブル結合やサブクエリの作成などに有効的)
デメリット: メソッドを連鎖的(チェーン状)に書くため、SQLを直接書くよりは可読性があるが同じクエリを何度も書 く必要があるため冗長的になりやすい。
返却値: Illuminate\Support\Collectionオブジェクト
use Illuminate\Support\Facades\DB;
$users = DB::table('users')->get();
Eloquent
Modelディレクトリに作成されたモデルクラスを使用して、モデルクラスに対してデータベース操作を行う実装方法です。
※ORM(Object-Relational Mapping)の一種
メリット: クエリビルダの全機能を内包し、可読性と保守性の面で優れている。(リレーション・アクセサ・ミューテータ等の豊富な機能がある)
デメリット: シンプルなクエリを作成するのに適しているため、複雑なクエリの場合は不向き。
返却値: Illuminate\Database\Eloquent\Collectionオブジェクト
use App\Models\User;
$users = User::all();
SQL
DBファサードを使用して、直接SQL言語でデータベース操作を行う実装方法です。
最も直接的にデータベース操作を行う方法ですが、一つ一つ処理を記述する必要があります。
メリット: パフォーマンスが高い。※クエリビルダやEloquentではカバーできないような特殊ケース(MySQLなどのDB管理システム固有のメソッド使用等)の場合に使用するのが良い。
デメリット: 直接データベースに対してクエリを実行するため、再利用性がない。クエリが長くなりやすいため、可読性・保守性が低下する傾向にある。
返却値: stdClassオブジェクト
use Illuminate\Support\Facades\DB;
$users = DB::select('select * form users');
3つの実装方法について「パフォーマンス」・「可読性と保守性」・「柔軟性」の点で簡単にまとめると以下になります。
まず「パフォーマンス」ですが、この3つだと「SQL」が最も高い実装方法になります。
それは、直接データベースに対してクエリを実行するため、中間層を介さずにデータを取得でき、結果として処理速度が向上します。
「クエリビルダ」や「Eloquent」は、SQLを抽象化するための追加の処理が行われており、クエリ実行に少なからずオーバーヘッド(クエリ実行以外の余分な処理)が発生します。
次に「可読性と保守性」ですが、こちらは「Eloquent」が比較的高い実装方法になります。
クエリビルダの全機能を内包し、 リレーション、アクセサ、ミューテータなどの豊富な機能があるためシンプルなクエリ作成に適しています。
「クエリビルダ」や「SQL」は、直感的にコードを理解することが難しく、同じクエリを何度も書く場合があるため冗長的になりやすいです。
最後に「柔軟性」は、「クエリビルダ」が有効な実装方法になります。
クエリの一部を再利用したり、プログラムの状態に基づいてクエリを変更したりすることが容易にできます。
「SQL」は、柔軟性は高いですが可読性や保守性を低下させるので使用を避けた方が良いでしょう。
それぞれにメリット・デメリットがあるため、プロジェクトの要件に合った最適な実装方法を考えることが重要です。
それでは、実際にクエリビルダを使った基本的なデータ操作の実装方法をみていきましょう。
基本的なデータ操作
クエリビルダを活用すればほとんどのSQLは構築することができます。
以下の表は基本的なクエリビルダのメソッド一覧です。
| メソッド | 構文 | 返り値(型) | |
|---|---|---|---|
| get | get('フィールド名') | Collection | 全てのレコードが返る ※引数指定の場合、指定フィールドの値のみ返る |
| first | first() | Collection | レコード1件が返る ※データなしの場合、nullが返る |
| select | select('フィールド1', 'フィールド2', ...) | --- | 取得したいカラムを指定する ※遅延実行1 |
| pluck | pluck('フィールド名') | Collection | 指定カラムのコレクションが返る ※データなしの場合、空が返る |
| insert | insert(['フィールド1' => 値]) | boolean | 成功の結果が返る |
| update | update(['フィールド1' => 値]) | integer | 影響を与えたレコード数が返る ※データなしの場合、0が返る |
| delete | delete(['フィールド1' => 値]) | integer | 影響を与えたレコード数が返る ※データなしの場合、0が返る |
| upsert | upsert( [挿入または更新するレコードの配列], [一意のカラム名またはカラム名の配列], [更新するカラム名またはカラム名の配列] ) | integer | 影響を与えたレコード数が返る ※第1引数に第2引数のユニークキーがない場合は挿入処理になる |
| where | where('フィールド名', 値) or where('フィールド名', '演算記号', 値) | --- | 取得したいレコードを指定する ※遅延実行1 |
| orderBy | orderBy('フィールド名', asc または desc) | --- | レコードを指定カラムで並び替える ※遅延実行1 |
| groupBy | groupBy('フィールド名') | --- | 指定カラムでグルーピングする ※遅延実行1 |
リンク:https://github.com/laravel/framework/blob/10.x/src/Illuminate/Database/Query/Builder.php
データの取得、挿入、削除、並び替え、グルーピングなど使用頻度が高いクエリビルダをピックアップしてみました。
SQL文を見たことがある人には見慣れたメソッド名ばかりですね。
これらのメソッドを使用して、データベースの基本「CRUD操作」をクエリビルダで行うにはどのようにすればいいのでしょうか。
以下のコードで解説していこうと思います。
use Illuminate\Support\Facades\DB;
// Create: データの挿入
$user = DB::table('users')->insert([
'name' => '伊藤 四郎',
'email' => 'siro@test.jp',
'tel' => 090-2222-1111,
'birthday' => '1937-06-15',
'address' => '東京'
]);
// Read: データの取得
$users = DB::table('users')
->select('address', DB::raw('count(*) as total'))
->groupBy('address')
->orderBy('total', 'asc')
->get();
$user_id = 1;
// Update: データの更新
DB::table('users')->where('id', $user_id)->update([
'address' => '北海道',
]);
// Delete: データの削除
DB::table('users')->where('id', $user_id)->delete();
まず最初にクエリビルダを使用するのでDBファサードを定義しています。これがないとエラーになるのでクエリビルダを使用するときは必ず書くようにしてください。 DBファサード定義以降がクエリビルダでの「CRUD操作」実装になります。
では早速、「データの挿入」処理から解説していきます。
Create: データの挿入
insert()メソッドを使用して「データの挿入」を行っています。
usersテーブルに新しくレコードを追加しており、カラム名をキーにした連想配列の形でinsert()メソッドに渡しています。
連想配列のキーがデータベースのカラム名と一致しない場合はSQLエラーになるので、順番等正確に指定する必要があることを覚えておきましょう!
また、複数レコードを挿入したい場合もあると思います。その際は以下のような方法でデータを挿入することも可能です。
use Illuminate\Support\Facades\DB;
// 新規ユーザ
$users = [
[
'name' => '伊藤 四郎',
'email' => 'siro@test.jp',
'tel' => 090-2222-1111,
'birthday' => '1937-06-15',
'address' => '東京'
],
[
'name' => '小林 五郎',
'email' => 'goro@test.jp',
'tel' => 090-3333-4444,
'birthday' => '1999-12-31',
'address' => '北海道'
],
];
$user = DB::table('users')->insert($users);
挿入データの件数が多い場合は、データ配列を別で定義して変数を渡すとよいでしょう。
続いて、「データの取得」処理に移りたいと思います。
Read: データの取得
get()メソッドを使用して「データの取得」を行っています。
基本的に現場でDB::table('users')->get();を書くことはあまりありません。なので実践的な実装例を上げてみました。
行っている処理としては、usersテーブルのaddressごとにレコードをグループ化し、各レコード数をカウント、その結果を合計値の昇順で並び替えを行い取得しています。
クエリを実行した結果は以下になります。
dd($users);
/*
Collection {#418 ▼
#items: array:2 [▼
0 => {#414 ▼
+"address": "沖縄"
+"total": 1
}
1 => {#415 ▼
+"address": "東京"
+"total": 3
}
]
}
*/
addressカラムごとにデータが分かれていて、レコード数もカウントされていることが分かりますね。
取得したデータは確認できましたが、具体的にはどのような処理を行っているのでしょうか。
このクエリ実行にはいくつかのポイントがありますので、順に確認していこうと思います。