• ベストアンサー

Doctrine(ORM)での複数行Insert

PHP/MySQL/Doctrineで開発しております。 配列フィールドを持つオブジェクトを永続化する際、パフォーマンスのため、INSERTの際に複数行をまとめてInsertしたいのですが、Doctrine_Recordのsaveメソッドを使うと、配列の各要素に対して1つのSQLが発行されてしまいます。 DQL等を用いた複数行Insertは可能でしょうか? また、不可能であれば、PHPでそういったことが可能であるORMは存在しますでしょうか? 宜しくお願い致します。

質問者が選んだベストアンサー

  • ベストアンサー
回答No.4

>御説明が足りずに失礼しました。てっきり文脈から読み取って頂けるものとばかり思っておりました。 いえ、こちらのほうこそ申し訳ないです。データベース周りのことについて、言葉をあまりに知らなさすぎて、回答者が質問者に余計な手間をかけさせてしまっているようでは話になりません。 とりあえず、色々あさっていたところ、 http://www.doctrine-project.org/blog/doctrine2-batch-processing こちらのエントリの一番したの「UPDATE」の箇所に、サポートしていない旨とその理由が掲載されていました。 Yahoo翻訳で、そこそこ読める翻訳が出来たのでそのまま転記します。 ------------------------------------- まず第一に、この構文は、mysqlとより新しいpostgresql版で支えられるだけです。第2に、AUTO_INCREMENTまたはSERIALとORMを使うことが識別子を物のアイデンティティ管理のために必要とするとき、そのようなマルチ挿入物ですべての発生する識別子をつかむ簡単な方法がありません。最後に、挿入パフォーマンスは、めったにORMのボトルネックでありません。大部分の状況のために十分に速いより、通常の挿入物は多くです、そして、あなたが本当に速い大きさ挿入物をしたいならば、マルチ挿入物はいずれにしろ最高の方法でありません、すなわち、Postgres COPYまたはMysql LOAD DATA INFILEは数桁より速いです。 -------------------------------------- 結局ORMは複数のデータベースで実装されているSQL文を抽象化していることを考えると文自体のサポートの幅が少なければ、対応しないような方向のようですね。 Oracleでもマルチテーブルインサートとかあるようですし、SQL Server2008でもマルチインサートの構文がかけるようではありますが。 また、せっかくなので、CakePHPが独自で実装しているORMの、saveAllメソッドも試してみましたが、ログを見ると、複数のInsert文が流れているようでした。 やはり、SQL文を直接叩いて対応するのが近道なのではないかと思います。 MySQL/Postgres/SQL Server2008の間であれば、記法も同じようですし。 詳細な説明を求めておいて、ろくな回答できなくて、本当にすみません。

i_110
質問者

お礼

とんでもありません。丁寧な御回答、本当に感謝しています。 出来ないということが明確にわかっただけで収穫です。ありがとうございました。同時に自分の事前調査不足にも幾分反省しています。 このマニュアルではボトルネックではないと自信をもって断言していますが、1000件程度の同時Insertですらパフォーマンスに大きな差が出ます。また、LOAD DATA INFILE等ではあまりに柔軟性に欠けるので、話になりませんね。この自信はどこから来るのか… とはいえ、マルチインサートに対応していないDBMSが存在することを考えると、確かに仕方ないのかもしれませんね。現行ORMの限界といったところでしょうか。とりあえずhogehoge78様のおかげでネイティブSQLが叩けることもわかったので、Doctrineのコードを読んで、saveメソッドをフックできるように改変してみたいと思います。 本当にありがとうございました。

その他の回答 (3)

回答No.3

となると、複数行Insertとはいったいどういうものをさしているのでしょう。 >配列フィールドを持つオブジェクトを永続化する際 コレについても良くわからないのですが、Array型のフィールドを有するテーブルに対して、Insertを行いたいということでしょうか。 もしそうであれば、MySQLはそもそもArray型をサポートしておりません。(一応Doctrineも見てみましたがArray型は正しくサポートしていないようです) それとも、「複数行をまとめてInsertしたい」の複数行というのは、何か全然違う解釈のものでしたか?

i_110
質問者

補足

御説明が足りずに失礼しました。てっきり文脈から読み取って頂けるものとばかり思っておりました。 複数行Insertとは以下のようなものです。 http://dev.mysql.com/doc/refman/5.1/ja/insert.htmlより ============================== VALUES 構文を利用する INSERT ステートメントは複数行を挿入する事ができます。これをする為には、それぞれが括弧で囲まれカンマで区切られている、カラム値の複数リストを含んでください。例: INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(4,5,6),(7,8,9); ============================== これにより、(1,2,3)と(4,5,6)と(7,8,9)の3行を挿入するのに必要なSQL発行回数が1回で済みます。SQL発行回数の減少はディスクへのランダムアクセス回数の減少に繋がりますので、結果として全体のパフォーマンスが向上します。 >配列フィールドを持つオブジェクトを永続化する際 クラスAの配列をフィールドとして持つクラスBのsave()メソッドを叩いた時、ということです。 この際、大概のORMでは(もしくは適切な設定によって)BのインスタンスのフィールドであるAの配列の永続化も自動的に行われますが、Doctrineの場合ですと要素ごとに1件のSQLが発行されてしまいます。 上記の例で言えば INSERT INTO tbl_name (a,b,c) VALUES(1,2,3); INSERT INTO tbl_name (a,b,c) VALUES(4,5,6); INSERT INTO tbl_name (a,b,c) VALUES(7,8,9); ということですね。 ※「配列の永続化」とは、Array型?を用いた永続化ではなく、外部キーによって結合されているテーブルへの永続化を指しています。 この複数件のSQLを、先程の複数行同時InsertSQLに自動的にパックするような機能や設定方法はないか?もしくはクラスBの特定のフィールドだけ永続化処理をハックできるような方法はないか?というのが本質門の主旨でした。 宜しくお願い致します。

回答No.2

となると、ある程度コストがかかってしまいますが、 <?php //一回目 $insert = new Hoge(); $insert->name = 'aaa'; $insert->date = time(); $insert->save(); //二回目 $insert = new Hoge(); //ここでインスタンス作り直し $insert->name = 'bbb'; $insert->date = time(); $insert->save(); ?> と、一行インサートを行うたびにインスタンスを作り直すという作業は必要かもしれませんね。 実際マニュアルでも2回インサート処理をしているコードのサンプルがありましたが、そのようにしていたようです。

i_110
質問者

補足

御回答ありがとうございます。 ですがそれですと2件のSQLが発行されてしまい、全く意味がありません。

回答No.1

Doctrine使ったことないですが、 多少複雑なことを行うのであれば、他のORマッパーでもそうですが、直接SQLを叩いたほうがよさそうですが、いかがでしょうか。 Doctrine_Managerのconnectionメソッドが返すDoctrine_Connectionクラスには、prepareメソッドがありますので、 $stmt = $conn->prepare("INSERT INTO hoge (a, b, c) VALUES (?, ?, ?)"); $stmt->execute(array(1, 2, 3)); といった感じで。

i_110
質問者

お礼

御回答ありがとうございます。 やはりネイティブSQLを叩けるインターフェースがあったんですね。調査不足でした、すみません。 しかしDoctorine_Record::saveメソッドで安直に永続化できないとなると、ORMを使う意味がどんどん薄れていきますね…コレクション周りを工夫すれば割と簡単に実現できそうなものですが。。(自分で実装する気は起きない) 複数行Insertを実現できるPHPのORMをもし御存知でしたら、御教示下さい。 宜しくお願い致します。

関連するQ&A