質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
PostgreSQL

PostgreSQLはオープンソースのオブジェクトリレーショナルデータベース管理システムです。 Oracle Databaseで使われるPL/SQLを参考に実装されたビルトイン言語で、Windows、 Mac、Linux、UNIX、MSなどいくつものプラットフォームに対応しています。

FuelPHP

FuelPHPは、軽量高速で開発が可能なPHPのWebアプリケーションフレームワークです。

Q&A

解決済

3回答

7260閲覧

[fuelPHP]アクションクエリの戻り値について

dthani

総合スコア131

PostgreSQL

PostgreSQLはオープンソースのオブジェクトリレーショナルデータベース管理システムです。 Oracle Databaseで使われるPL/SQLを参考に実装されたビルトイン言語で、Windows、 Mac、Linux、UNIX、MSなどいくつものプラットフォームに対応しています。

FuelPHP

FuelPHPは、軽量高速で開発が可能なPHPのWebアプリケーションフレームワークです。

0グッド

1クリップ

投稿2015/04/15 15:22

fuelPHPで、updateして0件だったら、insertというプログラムを作りたいと思い、
アクションクエリを実行した際の戻り値について調べていたところ、
下記サイトを見つけたのですが、まだわかりません。
http://qiita.com/bonon0/items/f5abeee65edf5ca37758

まず、
updateを実行したとき、int型と書いていますが、それはInsetを実行した戻り値と同様、配列型でその中身がint型なのでしょうか?配列型ではなく単純なint型なのでしょうか?

Insert,delete,updateが失敗したときは、どの形式(配列?なんらかの数値?)で何が戻ってくるのでしょうか?
エラーコードや、エラーメッセージが取得されるのでしょうか?

また、fuelPHPで、ストアドプロシージャ、ストアドファンクションを実行するサンプル(戻り値取得も)などありますでしょうか?

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

PHPのDB操作のお約束として、INSERT、UPDATE、DELETEの戻り値は大抵、直前の操作で影響が及ぼされた行数です。

fuelPHPのコードを読んでみると、まず、
fuelphp-1.7.2/fuel/core/classes/db.phpの中で、

// Query types const SELECT = 1; const INSERT = 2; const UPDATE = 3; const DELETE = 4;

と定義しています。

PDOの利用では
fuelphp-1.7.2/fuel/core/classes/database/pdo/connection.php

if ($type === \DB::SELECT) { 略 } elseif ($type === \DB::INSERT) { // Return a list of insert id and rows created return array( $this->_connection->lastInsertId(), $result->rowCount(), ); } else { // Return the number of rows affected return $result->errorCode() === '00000' ? $result->rowCount() : -1; }

となっていて、UPDATEの場合はelseになりますから
http://php.net/manual/ja/pdostatement.rowcount.php
PDOStatement::rowCount — 直近の SQL ステートメントによって作用した行数を返す
ので、intのみが返るのだと思います。

mysqliの利用では

if ($type === \DB::SELECT) { // Return an iterator of results return new \Database_MySQLi_Result($result, $sql, $as_object); } elseif ($type === \DB::INSERT) { // Return a list of insert id and rows created return array( $this->_connection->insert_id, $this->_connection->affected_rows, ); } else { // Return the number of rows affected return $this->_connection->affected_rows; }

となっていますので、
http://php.net/manual/ja/mysqli.affected-rows.php
mysqli::$affected_rows -- mysqli_affected_rows — 直前の MySQL の操作で変更された行の数を得る
やはり、intのみが返ると思います。

非推奨となったmysqlの利用でも

if ($type === \DB::SELECT) { // Return an iterator of results return new \Database_MySQL_Result($result, $sql, $as_object); } elseif ($type === \DB::INSERT) { // Return a list of insert id and rows created return array( mysql_insert_id($this->_connection), mysql_affected_rows($this->_connection), ); } else { // Return the number of rows affected return mysql_affected_rows($this->_connection); }

http://php.net/manual/ja/function.mysql-affected-rows.php
mysql_affected_rows — 一番最近の操作で変更された行の数を得る

一緒ですね。

これらは
fuelphp-1.7.2/fuel/core/classes/database/query.phpのexecute()内

$result = $db->query($this->_type, $sql, $this->_as_object);

で呼び出されていて、そのあと

return $result;

されるので、つまりUPDATE(とDELETE)の戻り値はintのみです。

INSERTの場合は上記のように

array(int insert_id, int affected_rows)となるようです。
insert_idは
http://php.net/manual/ja/mysqli.insert-id.php
このあたりを見ていただけると分かりますが、

直前のクエリで更新された AUTO_INCREMENT フィールドの値を返します。接続での直前のクエリがない場合や クエリが AUTO_INCREMENT の値を更新しなかった場合は ゼロを返します。

となっています。

Insert,delete,updateが失敗したときは、どの形式(配列?なんらかの数値?)で何が戻ってくるのでしょうか?
エラーコードや、エラーメッセージが取得されるのでしょうか?

mysqliを例にすると、
http://php.net/manual/ja/mysqli.affected-rows.php

正の整数が返された場合、それは変更された行数かあるいは取得された行数を 示します。ゼロが返された場合、それは UPDATE 文でレコードが更新されなかったか WHERE 条件に当てはまる行がなかった、またはクエリが実行されなかったことを 示します。-1 は、クエリがエラーを返したことを示します。


となります。PDOでもUPDATE、DELETEの場合、ステータスコードが正常でない場合はfuelphp側で-1を返すようになっていますね。

さらにコードを読んでみると分かりますが、SQL自体が実行できない場合にはDatabase_Exceptionがthrowされるようです。

ですので、これをcatchしてgetMessage()すれば、問題の内容が確認できるようですね。

投稿2015/04/15 23:46

編集2015/04/16 00:09
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

dthani

2015/04/17 14:18

ご回答ありがとうございます。 お手数ですが、下記の具体的な取得方法をお教え下さい。 さらにコードを読んでみると分かりますが、SQL自体が実行できない場合にはDatabase_Exceptionがthrowされるようです。 ですので、これをcatchしてgetMessage()すれば、問題の内容が確認できるようですね。
退会済みユーザー

退会済みユーザー

2015/04/17 14:20

try{}catch(){}ですね。 コードを書くので別スレッドにします。
guest

0

ベストアンサー

PHPとしては

try { $query = DB::select()->from('○○○'); $query->where('id', 1); $result = $query->excute(); } catch(Database_Exception $e){ //try{}内の処理でDatabase_Exceptionが投げられたらここにくる。PHPの処理は続行される。 echo $e->getMessage(); // ←エラー内容が表示される echo $e->getTraceAsString(); // ←スタックトレースが表示される } catch(ErrorException $e){ //ErrorExceptionが投げられたらこっち : } catch(Exception $e){ //Exceptionが投げられたらこっち }

こういうのがセオリーです。

Database_ExceptionはFuelExceptionを継承していて、FuelExceptionはPHP標準のExceptionを継承しています。

ただ、fuelPHPのフレームワーク自体でtry{}catch(){}しているようなので、エラーの内容を知りたいだけなら特に自前でtry{}catch(){}しなくてもエラーハンドリングすることは出来ますね。

ですからエラーがあった時にフレームワークの処理を停止せずに何か処理を続行したい時などに、自前でtry{}catch(){}を実行することになると思います。

投稿2015/04/17 14:39

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

dthani

2015/04/17 15:01

ご回答ありがとうございます。 すみませんが、そのような知識はどういう書籍やWebサイトで学んでいるのでしょうか? fuelPHPの電子本や、fuelPHPの公式サイトには載っておりません。 よければ教えてください。
退会済みユーザー

退会済みユーザー

2015/04/17 15:04

もう、コードを読んでます。慣れると読めるものですよ。
dthani

2015/04/17 15:10

そうなんですか。良いコーディング方法も学べそうですね。 ありがとうございます。
guest

0

行が無かったらinsert、行が有ったらupdate こういう処理を upsert と呼んだりします。
質問内容とは少しずれますが、lucker さんがFuelPHPの戻り値について詳しく書いているので私は簡単に upsert の事を書きましょう。

こういう処理はFuelPHPで結果を受け取らずに、SQL文だけで解決することができます。

まずSQL文です。

テーブル

lang

1CREATE TABLE sample( 2 id int NOT NULL, 3 message Text 4);

挿入+更新クエリ(upsertクエリ)

lang

1WITH updated AS (UPDATE sample SET message='updated!' WHERE id=1 RETURNING *) 2INSERT INTO sample(id, message) SELECT 1, 'inserted!' WHERE NOT EXISTS(SELECT * FROM updated);

この upsert クエリを実行することで、「updateして0件だったらinsert」が実現できます。
upsertクエリを実行すると、1度目はmessageが'inserted!'、idが1の行が挿入され、2度実行するとmessageが'updated!'に書き変わります。

UPDATE文で挿入されたデータをRETURNING句で受け取っています。RETURNING句を使うと更新された行数ではなく、更新された行を受け取ることができます。
WITH句はその結果に updated というエリアス名をつけています。
updatedを参照して、中身が空なら更新対象はなかった、あれば更新対象があったことがわかります。

INSERT文にはSELECT文の結果を挿入する機能があります。
NOT EXISTS句を使ってupdatedの結果が空ならINSERTが実行されるようにしています。

投稿2015/04/17 07:32

haru666

総合スコア1591

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

dthani

2015/04/17 14:04

ご回答ありがとうございます。 ただ、上記のupsertはselect文を実行してしまうので、データ件数が多い場合(数万件以上)は実行速度が遅くなりますよね。 よって、updateして処理件数が0件だったら、insertさせる方で考えています。それもその部分をストアドで実行すれば高速で処理できますので。
haru666

2015/04/17 14:19

いえ、データの件数による速度の劣化はupdateとinsertを別々に処理した場合とほとんど同じだと認識しています。 それどころかFuelPHPで結果を取得した場合と比較すればずっと早い可能性があります。 upsertクエリで行われているselect文は、あくまでupdateの結果(更新された1レコード、無ければ0レコード)を取得しているので、現在のテーブルの列数には影響を受けません。update文のWHERE句には適切なインデックスを張る必要がありますが、そこにかかるコストは通常update文を実行する場合と同じです。 FuelPHPでupdate対象が無ければinsertをする場合より早いというのは、上記のクエリは1度しかクエリを実行しないためです。 update時にクライアントで1度結果を受け取ってくるということは、insert時には合計で2度のクエリが実行されることになり、クライアントーサーバー間の通信が2度発生することになります。 このコストは単純に余剰なコストですから削除したい部分なのです。 upsertクエリのメリット、デメリットは以下のようなものです メリット:1度のクエリでinsertとupdateを同時に行える デメリット:文法が難解で可読性が低い これは計測しないと正しいことが分からないのであまり自信を持って言えないのですが、ストアドプロシージャにした場合と同程度のパフォーマンスになると思います。
haru666

2015/04/17 14:21

あ、だからといってストアドよりこちらを・・・とかそういう話ではないです。 ストアドは正しいアプローチだと思っています。
dthani

2015/04/17 14:41

ご回答ありがとうございます。 upsertって、そんなに実行速度が速いんですね。 わかりにくい書き方で実行速度が遅いと思っていたので、敬遠していました。 ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問