# ch03 — SELECT 基本 / fetchAll / ループ

Source: https://php-school.pages.dev/topics/db/ch03-select-basic/
Site: AIと学ぶPHPの学校 (https://php-school.pages.dev)
Lesson: データベース連携
Level: L4

Reference (official): https://www.php.net/manual/ja/book.pdo.php

## 本文

# ch03 — SELECT 基本 / fetchAll / ループ

## 概要

接続できたら、まずやりたいのは **「テーブルからデータを取り出す」** こと。これを担う SQL が **`SELECT`** で、基本形は `SELECT * FROM users` のように「どの列を・どのテーブルから」を書くだけ。`*` は全列、`SELECT id, name FROM users` のように **列を絞る** 書き方も覚えておく (取りすぎない方が速い)。

PHP 側では `$pdo->query("SELECT ...")` で SQL を投げて結果セットを受け取る。行の取り出しは `foreach ($pdo->query(...) as $row)` で **1 行ずつ回す** か、`$stmt->fetchAll()` で **配列にまとめて受け取る** の 2 パターン。件数が少ないなら `fetchAll()`、何万件なら `foreach` でストリーミング。

取り出された `$row` は **連想配列** で、`$row['id']` / `$row['name']` のように列名でアクセスする。値は基本すべて文字列で来るので、数値計算したいときは `(int)$row['age']` のようにキャストする。「SQL を 1 つ書いて結果を foreach で回す」基本リズムを身体に通す。

## 学習目標

- `SELECT` で行を取り出すコードを書ける
- `foreach ($pdo->query(...) as $row)` で 1 行ずつ処理できる
- `fetchAll()` で全行を配列にまとめて取れる
- 行は連想配列 (`$row['name']`) としてアクセスできる

## 所要時間

スライド 5 分 + ドリル 3 問 = **20 分**

## ドリル

| no | 内容 |
|---|---|
| 01 | `users` を全件 SELECT して `id: name` 形式で出力 |
| 02 | `COUNT(*)` で件数を出力 |
| 03 | `name` 列だけ取り出し、改行区切りで出力 |

## スライド

---
marp: true
theme: dojo
paginate: true
size: 16:9
---

<!--
LLM_CONTEXT:
  Lesson 11 / Chapter 3
  目的: SELECT で行を取り出す / foreach と fetchAll の使い分け
  扱わない: WHERE (ch04) / プレースホルダ (ch04) / INSERT (ch05) / 取得モードの細かい指定 (ch07)
  読み上げ時間目安: 4 分半〜5 分
-->

# SELECT で行を取り出す

Lesson 11 / Chapter 3

---

## SELECT は「テーブルから行を取り出す」命令

```sql
SELECT id, name FROM users ORDER BY id;
```

- `SELECT 列名` で **取り出したい列** を指定
- `FROM テーブル名` で **どのテーブルから** か
- `ORDER BY 列名` で **並び順** を固定 (順序を保証したいときは必ず付ける)
- `*` を書くと全列 (`SELECT * FROM users`)

---

## PHP からは `$pdo->query()` で実行

```php
<?php
$pdo = new PDO('sqlite:' . getenv('DOJO_DB_PATH'));
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$stmt = $pdo->query('SELECT id, name FROM users ORDER BY id');
```

- `$pdo->query($sql)` に SQL 文字列を渡すと、結果が `PDOStatement` で返る
- `query()` は **値を埋め込まない固定 SQL 専用**。ユーザー入力を混ぜるときは `prepare` (ch04)

---

## `foreach` で 1 行ずつ取り出す

```php
foreach ($pdo->query('SELECT id, name FROM users ORDER BY id') as $row) {
    echo $row['id'] . ': ' . $row['name'] . "\n";
}
```

実行結果:

```
1: 太郎
2: 花子
3: 次郎
```

- 1 行が連想配列で渡ってくる: `$row['id']` / `$row['name']`
- 行数が多くても **メモリに全部載せずに済む** (1 行ずつ処理)

---

## `fetchAll()` で全行を 1 つの配列に

```php
$stmt = $pdo->query('SELECT id, name FROM users ORDER BY id');
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

// $rows は配列の配列
// [
//   ['id' => 1, 'name' => '太郎'],
//   ['id' => 2, 'name' => '花子'],
//   ['id' => 3, 'name' => '次郎'],
// ]

foreach ($rows as $row) {
    echo $row['name'] . "\n";
}
```

- 後から件数を数えたい (`count($rows)`) / 並べ替えたいときは `fetchAll` の方が楽
- ただし **全行メモリに載る** ので、大量データなら `foreach` で 1 行ずつの方が安全

---

## `COUNT(*)` で件数だけ取る

```php
$stmt = $pdo->query('SELECT COUNT(*) AS cnt FROM users');
$row = $stmt->fetch(PDO::FETCH_ASSOC);
echo $row['cnt'] . "\n";   // 3
```

- 1 行 1 列だけ返ってくるパターン
- `fetch()` は 1 行だけ取り出すメソッド
- `AS cnt` で列に名前を付けると `$row['cnt']` で取れて読みやすい

---

## このチャプターでできるようになること

✅ `SELECT 列 FROM テーブル ORDER BY 列` を書ける
✅ `$pdo->query()` で SELECT を実行できる
✅ `foreach` で 1 行ずつ処理できる
✅ `fetchAll(PDO::FETCH_ASSOC)` で全行を配列にできる
✅ 行は連想配列 (`$row['列名']`) でアクセスする

→ ドリルへ
