[Codeigniter3]データの件数を取得するために「num_rows()」は使っていけない

[Codeigniter3]データの件数を取得するために「num_rows()」は使っていけない
2021年08月18日2023年10月10日

PHPのフレームワークのCodeigniterでツールを開発して思ったことです。

負荷テストをするために、大量にデータをInsertして動作確認をしていたところ、SQLに怪しいところがありました。

「num_rows()」を使うとメモリ不足になる

データベースからデータを取得するためにnum_rows()を使用してページング機能の実装していたのですが、num_rows()を呼び出すことでSQLで「select count(*) from table_name;」をしてくれていると思っていました。

しかし、そうではないらしく実際は下記のsqlが発行されています。

select * from table_name;

 つまりcountの件数を取得しているのではなくデータをいったん取得してPHP内で件数だけ返しているようです。

そして、当たり前ですが下記のエラーが発生します。

Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 4096 bytes) in C:\xampp\htdocs\project\system\database\drivers\sqlite3\sqlite3_result.php on line 142
Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 4096 bytes) in C:\xampp\htdocs\project\system\core\Common.php on line 163

 「num_rows()」はあくまでクエリで返された行数を返す役割であって、SQL発行時に「select count(*) from table_name;」をしているわけではないということです。

Codeigniter3のDB関連の記事を読んでると、勘違いしている人多い気がします。

データ量が少なければさほど影響はないかもしれませんが、10万単位でデータを操作するとかなり重たいし、メモリ不足エラーも発生します。

解決方法

データベースから件数を取得したい場合は、下記のように対処することができます。

// 件数を取得
$result = $this->db->select('count(*) as count')->get("table_name")->row();

//件数
var_dump($result->count);

SELECT部分でAS句を使ってカラム名を指定しとかないと、後でデータ取得ができません。

デバッグのSQLログを確認しよう

Codeigniter3にはデバッグモードにするとSQLログを画面上に表示してくれるのでそこで確認しましょう。

download

最後に

公式サイトにもちゃんと書かれていますね。

num_rows()

クエリで返された行数を取得します。Note: 下記の例では $query は、クエリの結果オブジェクトを代入した変数です:

公式サイトを見ずにメソッド名だけで判断するのは危険て言うことを今回学習しました。

コメント

ID3
匿名
問題の捉え方が本質とズレています。
『「num_rows()」を使うとメモリ不足になる』ではなく『巨大なテーブルに対して「select * from table_name」を実行するようなCodeIgniterの使い方をしたのでメモリ不足になった』という話です。

num_rows()はクエリの結果オブジェクトで定義されているメソッドであり、データベースクラスに定義されているメソッドではありませんよね。
例えばnum_rowsが「$this->db->num_rows(table_name)」のような定義であったなら「CodeIgniter内でselect count(*) from table_name;をしてくれていると思っていた」は正しいと思いますが、実際はそのような定義ではありません。

恐らく「$this->db->get(table_name)->num_rows()」のような使い方をされてメモリエラーが出たのではないでしょうか。この場合は、
①『$this->db->get(table_name)』部分で「select * from table_name」を実行しクエリ結果オブジェクトを生成
 ※selectメソッドでカラム指定しない場合はデフォルトで*が設定されているため
②『->num_rows()』部分で①の行数を取得
の2ステップの処理が走っています。
num_rows()の仕様や名前が問題ではなく、「select * from table_name」クエリを実行するような使い方をしていたのが問題だった(=①の部分が問題だった)ことが分かります。

フレームワークのAPIと、フレームワークを使わずに同じ処理を行う場合とで対応関係を整理しておくと、誤解なく理解できると思いますよ。

コメントを残す

お名前(任意)
コメント:新規