SQLクエリーの基本

query()

実行するSQLをクエリー(Query) といい、PDOオブジェクトのquery() メソッドを実行します。

$pdo->query(SQL);
  • $pdo は PDOオブジェクト

PDO Statementオブジェクト

query() を実行すると PDO Statementオブジェクトが取得できます。

$stmt = $pdo->query(SQL);

SQLクエリーの実行

ファイル構成

basic/
├── env.php
├── DB.php
└── query_test.php

SQLクエリー実行

usersテーブルから10件選択するSQLクエリーを実行します。

query_test.php
<?php
require_once 'env.php';
require_once 'DB.php';

$db = new DB();

$sql = "SELECT * FROM users LIMIT 10;";
$stmt = $db->pdo->query($sql);
?>

オブジェクト確認

PDO StatementオブジェクトのqueryStringを確認します。

query_test.php
...

<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>SQL Query</title>
</head>

<body>
    <h3>SQL</h3>
    <p>
        <?= $stmt->queryString ?>
    </p>
</body>

</html>

ブラウザにSQLが表示されるか確認します。

結果

SQLエラーの場合

SQL構文やプログラムエラーが発生すると、エラーメッセージ表示されます。

ターミナル
Fatal error: Uncaught PDOException: SQLSTATE[42S02]: Base table or view not found: ...

フェッチ

フェッチとは

データベースからデータ取得する処理をフェッチ(Fetch) といいます。PDOのフェッチ処理はメソッドがいくつかあり、PDO Statementから PHPで扱える配列やオブジェクトに変換します。

fetch()

fetch() はSQL結果を1件取得します。

PDOStatementオブジェクト->fetch();

fetchAll()

fetchAll() はSQL結果をすべて取得します。

PDOStatementオブジェクト->fetchAll();

フェッチモード

フェッチモードはデータをフェッチしたときの配列の構造を指定します。

モード データ形式
PDO::FETCH_BOTH カラム名と連番の配列
PDO::FETCH_ASSOC カラム名の配列
PDO::FETCH_COLUMN 1カラム指定時に、連番の配列
PDO::FETCH_KEY_PAIR 2カラム指定時にキーと値の連想配列
PDO::FETCH_OBJ カラム名をプロパティとしたオブジェクト
PDO::FETCH_ASSOC

カラムをキーにした配列 PDO::FETCH_ASSOC がよく利用されます。

$stmt->fetch(PDO::FETCH_ASSOC);

$stmt->fetchAll(PDO::FETCH_ASSOC);

ユーザデータ取得

fetchAll() で一括取得

fetchAll() で、「users」テーブルデータを多次元連想配列として取得します。

require_once 'env.php';
require_once 'DB.php';

$sql = 'SELECT * FROM users LIMIT 10;';
$stmt = $pdo->query($sql);

// データフェッチ
$users = $pdo->fetchAll(PDO::FETCH_ASSOC);

ユーザデータ表示

多次元連想配列を、HTMLで繰り返し表示します。

    <h3>users</h3>
    <ul>
        <?php foreach ($users as $key => $user) : ?>
            <li><?= $user['id']  ?></li>
            <li><?= $user['account_name']  ?></li>
            <li><?= $user['name']  ?></li>
            <li><?= $user['email']  ?></li>
            <li><?= $user['password']  ?></li>
        <?php endforeach ?>
    </ul>
結果

fetch() の繰り返し

fetchAll() はデータが大量になると負荷がかかるため、whilefetch() を1件ずつ繰り返して処理することもできます。

// データフェッチ
// $users = $stmt->fetchAll(PDO::FETCH_ASSOC);

// while で fetch() 繰り返し
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
    $users[] = $row;
}

プリペアドステートメント

プリペアドステートメントとは

Prepared Statement(プリペアドステートメント) はSQL文を実行する前に準備のことで、SQL文にパラメータを安全にバインドすることができ、SQLインジェクション攻撃を防ぐことができます。

主な役割

SQL文の準備

prepare() は、SQL文をデータベースに送信し、実行する前に準備します。この準備された文は、PDOStatementオブジェクトとして返されます。

パラメータのプレースホルダ

SQL文内の値の代わりにプレースホルダを使用できます。プレースホルダは名前付きプレースホルダ :? の特殊記号で表現し、SQL文とデータを分離できます。

プレースホルダ形式
SELECT * FROM users WHERE name = :name

パフォーマンス向上

prepare() で準備されたSQLはデータベースでキャッシュされます。同じSQL文を複数回実行するときに、異なるパラメータで複数回実行することでパフォーマンス向上が期待できます。

実行メソッド

prepare()

PDOでプリペアドステートメントを利用するには prepare() メソッドを利用します。

$stmt = $pdo->prepare("SELECT * FROM users WHERE name = :name");

execute()

execute() で準備されたSQLを実行します。

// 通常のSQLを実行する場合
$stmt->execute();
// データ代入してSQLを実行する場合
$stmt->execute($data);

以下は、users.id で検索します。

$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id");

// 実行
$stmt->execute();

データバインド

プリペアドステートメントでプレースホルダー記述したSQLは、データバインドが必要です。PDOのデータバインドメソッドには、参照渡しの bindParam() と 値渡しの bindValue() があります。

bindParam()

bindParam() は、パラメータを参照渡しでバインドします。バインドした変数の値を後で変更すると、その変更がクエリの実行時に反映されます。

$stmt = $pdo->prepare("SELECT * FROM users WHERE name = :name");

$name = 'Alice';  // 変数の値を Alice
$stmt->bindParam(':name', $name);
$name = 'Bob'; // 変数の値を Alice から Bobに変更
$stmt->execute(); // 'John' が使用される

bindValue()

bindValue() は、パラメータを値渡しでバインドします。バインドした値は実行時に固定され、後で値を変更してもクエリには影響しません。

$stmt = $pdo->prepare("SELECT * FROM users WHERE name = :name");

$name = 'Alice';  // 変数の値を Alice
$stmt->bindValue(':name', $name);
$name = 'Bob'; // 変数の値を変更しても影響なし
$stmt->execute(); // 'Alice' が使用される

ユーザ検索

ユーザ検索処理

クエリパラメータ(GETパラメータ)「user_id」がリクエストされたときに、ユーザ検索します。

sql_test.php
if (isset($_GET['user_id']) && $_GET['user_id'] > 0) {
    $sql = "SELECT * FROM users WHERE id = :id;";
    $stmt = $db->pdo->prepare($sql);
    // user_id バインド
    $stmt->bindValue(":id", $_GET['user_id']);
    $stmt->execute();
    // データフェッチ
    $select_user = $stmt->fetch(PDO::FETCH_ASSOC);
}

execute() に直接データをバインドすることもできます。

sql_test.php
if (isset($_GET['user_id']) && $_GET['user_id'] > 0) {
    $sql = "SELECT * FROM users WHERE id = :id;";
    $stmt = $db->pdo->prepare($sql);
    $data['id'] = $_GET['user_id'];
    // execute() で直接バインド
    $stmt->execute($data);
    // データフェッチ
    $select_user = $stmt->fetch(PDO::FETCH_ASSOC);
}

HTML表示

ユーザ一覧修正

ユーザ一覧表示で「user_id」クエリパラメータ付きの a リンクを追加します。

sql_test.php
    <h3>users</h3>
    <ul>
        <?php foreach ($users as $key => $user) : ?>
            <dt><?= $user['id'] ?></dt>
            <!-- 「user_id」クエリパラメータ付きのリンク -->
            <li><a href="?user_id=<?= $user['id'] ?>"><?= $user['account_name'] ?></a></li>
            <li><?= $user['name'] ?></li>
            <li><?= $user['email'] ?></li>
            <li><?= $user['password'] ?></li>
        <?php endforeach ?>
    </ul>

選択ユーザ表示

選択ユーザが存在した場合、HTML表示します。

sql_test.php
    <p>
        <?= $sql2 ?>
    </p>
    <?php if (isset($select_user) && $select_user["id"] > 0) : ?>
        <ul>
            <dt><?= $select_user['id']  ?></dt>
            <li><?= $select_user['account_name']  ?></li>
            <li><?= $select_user['name']  ?></li>
            <li><?= $select_user['email']  ?></li>
            <li><?= $select_user['password']  ?></li>
        </ul>
    <?php endif ?>

一覧表のユーザリンクで、ユーザデータが選択されるか確認します。

結果

PHP + MySQL Webサーバプログラミング