3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

生のSQLを書いたり書かなかったり

Last updated at Posted at 2025-12-08

サーバーサイドの開発に関わる業務でよくPHPとMySQLを使っていて,その中でクエリビルダというものを触ったりしています.PHPはサーバーサイドで時々使われるプログラム言語で,MySQLはデータベース管理システムです.ここでのクエリビルダというのは,PHPの書き方でMySQLを操作することが可能な機能です.今回はLaravelというフレームワークのクエリビルダを例とします.

基本的な説明

MySQLのようなデータベース管理システムは,CRUDと呼ばれる基本操作を備えています.CRUDとはデータの作成,読み取り,更新,削除のことで,MySQLだとinsert文,select文,update文,delete文が対応しています.

insertする2つのやり方

たとえば,以下のようなbooksテーブルと行があるとします.サンプルデータなので,テーブル名や列名,行の値に深い意味はないと思います.また,book_idはauto_incrementで値を指定しなくても自動補完される仕様とします.

book_id name price activated start_time end_time
1 あたまがさえるほん 750 yes 2025-12-01 00:00:00 2038-01-01 00:00:00
2 わんぱくじてん 600 yes 2025-12-02 00:00:00 2025-12-31 23:59:59
3 かなしいものがたり 370 no 2025-12-03 00:00:00 2038-01-01 00:00:00

ここに「ユーモアのほん」という性格に影響しそうな行を追加します.

MySQLのクエリをそのまま書く形式,いわゆる生のSQLに近いものを書くと以下のようになります.

DB::insert('insert into books (name, price, activated, start_time, end_time) values (?, ?, ?, ?, ?)', ['ユーモアのほん', 900, 'yes', '2025-12-08 00:00:00', '2038-01-01 00:00:00']);

概ね同じ内容をクエリビルダで書くと以下のようになります.

// insertする値
$params = [
  'name' => 'ユーモアのほん',
  'price' => 900,
  'activated' => 'yes',
  'start_time' => Carbon::parse('2025-12-08 00:00:00'),
  'end_time' => Carbon::parse('2038-01-01 00:00:00'),
]
// 実際にinsertする部分
$new_row = DB::table('books')->insert($params);

実行結果こうなります.

book_id name price activated start_time end_time
1 あたまがさえるほん 750 yes 2025-12-01 00:00:00 2038-01-01 00:00:00
2 わんぱくじてん 600 yes 2025-12-02 00:00:00 2038-01-01 00:00:00
3 かなしいものがたり 370 yes 2025-12-03 00:00:00 2038-01-01 00:00:00
4 ユーモアのほん 900 yes 2025-12-08 00:00:00 2038-01-01 00:00:00

どちらが良いのか

弊社のサーバーサイド開発に携わっている技術者のほとんどは,PHPもMySQLも習得しています.なので,素直にクエリビルダを書く人もいれば,ほぼ生のSQLを書く人もいます.

先に結論

基本的にはクエリビルダの方が良いです.ただし,集計のクエリなど一部のケースは生のSQLの方が良い場合もあります.

理由1.SQLインジェクションの対策

先ほどinsertの例を紹介しましたが,ほぼ生のSQLの書き方を変数をそのまま入れるようにするとSQLインジェクションできてしまいます.SQLインジェクションとは,SQLのパラメータにSQL文を仕込むなどして,不正な操作を行う攻撃のことです.

$name = 'のろいの本';
$price = 900;
$activated = 'yes';
$start_time = '2025-11-08 00:00:00';
// 不正なSQL文が流れる(全行UPDATEされる)
$end_time = "2038-01-01 00:00:00); UPDATE books set name='おきのどくですがbookはきえてしまいました' where 1=1;"
DB::insert("insert into books (name, price, activated, start_time, end_time) values ($name, $price, $activated, $start_time, $end_time)");

クエリビルダはその点気にする必要はないので,上記のようなセキュリティの問題を抱えずに済むことになります.

理由2.分岐や使いまわしなど設計が良い

たとえば,状況によってselect文の条件を変更したい場合,生のSQLよりもクエリビルダの方が書きやすい場面があります.以下のような処理にさらに条件を増やそうとしたとき,生のSQLだと文字列の連結でいちいちスペースとかANDとか気にしなければいけない点を,クエリビルダは解決してくれます.

生のSQL

$sql = "SELECT * FROM comics WHERE activated = 1";
$bindings = [];

// 文字列のスペースやANDの有無を気にする必要がある
if ($request->has('book_id')) {
    $sql .= " AND book_id = ?";
    $bindings[] = $request->book_id;
}

クエリビルダ

$query = DB::table('books')->where('activated', 'yes');

if ($request->has('book_id')) {
    $query->where('book_id', $request->book_id);
}

理由3.DB移植しやすい

…という話をよく耳にするものの,筆者も勉強途中なので具体例はなかなか出てこないのですが,たとえばMySQLからPostgresqlに移行することになった場合,MySQLのSQL文にバッククォートを付けていると全て外す手間が発生します.

生のSQLが良いケースも紹介

実用的な例を挙げると,MySQLのDATE_FORMATを使うときは部分的に生のSQLを書きます.

DB::raw("DATE_FORMAT(start_time, '%Y-%m-%d %H:%i:%s') AS start_time"),

日付型をselect文でMySQLから取得すると,PHPの場合はよくDateTimeとかのオブジェクトとして変数に格納されます.しかし,取得する行数が多くなるとオブジェクトを格納するのにメモリを大量に消費して,時にはAllowed memory size of~のようなメモリ不足のエラーに遭遇することがあります.これを回避するために,MySQLから文字列として日付を取得して,必要なタイミングでのみDateTimeのようなオブジェクトに変換して処理を行う,ような手続きを実装することがあります.つまり,重そうな処理をMySQLにやらせた方が良いかPHPにやらせた方が良いか,都度判断して使い分けするのが良さそうです.

さいごに

サーバーサイドの業務経験はまだ浅いのですが,これまでの業務で得た知見を整理する機会として記述してみました.習いたての頃は生のSQLをよく書いていたのですが,最近はほとんどクエリビルダで済ませるようになりました.色々なやり方を知っていて,それぞれの良さを理解していると自分のためにもなって,アウトプットすることで誰かのためにもなるのかなぁと思いました.

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?