【MySQL】UPDATE・DELETE・INSERT実行後に処理件数を取得する方法

UPDATE・DELETE・INSERT実行後に出力される``Query OK, 1 row affected``の件数を取得する方法です。
```sql
mysql> UPDATE animals SET name = 'cat' WHERE name = 'lion';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0
```

SQLとPHP、2種類の方法を説明します。

# SQLで取得する
``ROW_COUNT()``関数で取得できます。
必ずステートメント（UPDATE、DELETE、INSERTなどの実行単位）の完了直後に実行してください。

## ROW_COUNT()の使い方
``SELECT ROW_COUNT();``を実行します。

```sql
-- UPDATE
mysql> UPDATE animals SET name = 'dog' WHERE name = 'wolf';
Query OK, 7 rows affected (0.00 sec)
Rows matched: 7  Changed: 7  Warnings: 0

mysql> SELECT ROW_COUNT();
+-------------+
| ROW_COUNT() |
+-------------+
|           7 |
+-------------+
1 row in set (0.00 sec)

-- DELETE 
mysql> DELETE FROM animals WHERE id = 3;
Query OK, 1 row affected (0.00 sec)

mysql> SELECT ROW_COUNT();
+-------------+
| ROW_COUNT() |
+-------------+
|           1 |
+-------------+
1 row in set (0.00 sec)

-- INSERT
mysql> INSERT INTO animals (name) VALUES('dog'), ('cat'), ('rabbit');
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> SELECT ROW_COUNT();
+-------------+
| ROW_COUNT() |
+-------------+
|           3 |
+-------------+
1 row in set (0.00 sec)
```

## ROW_COUNT()とは
- MySQLの情報関数
- 直前のステートメントで影響を受けた行数を返す
- UPDATE・DELETE・INSERTの直後は変更、削除、または挿入された行数を返す
- ステートメントの実行直後に呼び出すことができる

以下、[MySQL公式マニュアル](https://dev.mysql.com/doc/refman/5.6/ja/)からの引用です。

[ROW_COUNT()](https://dev.mysql.com/doc/refman/5.6/ja/information-functions.html#function_row-count)
>ROW_COUNT() は、mysql_affected_rows() C API 関数から取得される値と同様で、ステートメントの実行後に mysql クライアントに表示される行数です。

[mysql_affected_rows()](https://dev.mysql.com/doc/refman/5.6/ja/mysql-affected-rows.html)
>mysql_affected_rows() は、mysql_query() または mysql_real_query() によるステートメントの実行直後に呼び出すことができます。それは、最後のステートメントが UPDATE、DELETE、または INSERT であった場合に、それによって変更、削除、または挿入された行数を返します。SELECT ステートメントの場合、mysql_affected_rows() は mysql_num_rows() のように動作します。

[events_statements_current テーブル](https://dev.mysql.com/doc/refman/5.6/ja/performance-schema-events-statements-current-table.html)
>ROWS_AFFECTED
ステートメントに影響を受けた行数。「影響を受けた」の意味については、セクション23.7.7.1「mysql_affected_rows()」を参照してください。

ROW_COUNT()はAPIの``mysql_affected_rows()``やステートメントイベントを格納するテーブル``events_statements_current``の``ROWS_AFFECTED``カラムの値と同じです。

# PHPで取得する
PDOクラスとmysqliクラスを利用します。
[PHP公式マニュアル: どのAPIを使うか](https://www.php.net/manual/ja/mysqlinfo.api.choosing.php)

## PDOクラス
PDOクラスの``exec()``は戻り値が処理件数です。
[PHP公式マニュアル: PDO::exec](https://www.php.net/manual/ja/pdo.exec.php)

```pdo_test1.php
$dbh = new PDO($dsn, $user, $password);
$count = $dbh->exec("INSERT INTO animals (name) VALUES('rabbit'), ('wolf')");

echo $count; # 2を返す
```

``rowCount()``でステートメントの実行直後に処理件数を取得することもできます。
[PHP公式マニュアル: PDOStatement::rowCount](https://www.php.net/manual/ja/pdostatement.rowcount.php)
```pdo_test2.php
$dbh = new PDO($dsn, $user, $password);
$del = $dbh->prepare("INSERT INTO animals (name) VALUES('rabbit'), ('wolf')");
$del->execute();
$count = $del->rowCount();

echo $count; # 2を返す
```

## mysqliクラス
mysqliクラスはステートメントの実行直後に``affected_rows``プロパティから処理件数を取得できます。

[PHP公式マニュアル: mysqli::$affected_rows](https://www.php.net/manual/ja/mysqli.affected-rows.php)
```mysqli_test.php
$mysqli = new mysqli($host, $user, $password, $database);
$result = $mysqli->query("INSERT INTO animals (name) VALUES('rabbit'), ('wolf')");
$count = $mysqli->affected_rows;

echo $count; # 2を返す
```

# 他のRDMSでは使えるのか
MySQL以外のRDMSにもROW_COUNT()に相当する機能は存在しますが、RDMS毎に仕様が異なるようです。
公式マニュアルで仕様を確認した後で利用しましょう。

[PHP公式マニュアル: PDOStatement::rowCount](https://www.php.net/manual/ja/pdostatement.rowcount.php)でも説明されています。
>関連する PDOStatement によって実行された直近の SQL ステートメントが SELECT 文の場合、いくつかのデータベースは文によって返された 行数を返すかも知れません。しかしながら、 この振る舞いは全てのデータベースで保証されていません。 さまざまな場所で使用するアプリケーションでは、 これに頼ってはいけません。

>このメソッドは SQLite ドライバの場合はあらゆる場合に "0" (ゼロ) を返します。 PostgreSQL ドライバの場合は ステートメント属性 PDO::ATTR_CURSOR の設定が PDO::CURSOR_SCROLL の場合にだけ "0" (ゼロ) を返します。

# まとめ
多くのORMはSQL実行メソッドの戻り値=処理件数なので、利用機会は少なさそう。

# 採用PR
弊社で一緒に働く仲間を募集しています。
全てのオタクを幸せにしたい方、是非ご覧ください！

https://hrmos.co/pages/vivion/jobs/0000191