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

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

ただいまの
回答率

90.60%

  • PHP

    19854questions

    PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

  • MySQL

    5696questions

    MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

  • Symfony

    125questions

    Symfony はPHPで記述されたWebアプリケーションフレームワークです。よく利用するコーディングをテンプレーティングするなど、Webアプリケーション開発の効率化を目的として設計されています。

DBから取得したデータをCSVに出力するときにループを使用しない方法はありますか?

受付中

回答 6

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 913

red13

score 71

あるシステムの改修をしています。
そのシステム内で検索結果をCSVに出力する機能があり、
以下の「取得したデータをループしてCSVのデータに設定する」ような処理が実装されています。

*** アクションクラス ***
// SQL作る?
$sql = Doctrine_Query::create()->from('test t');
$sql->addWhere('t.id = ?', $id);
// 他条件追加したり

// SQL発行して変数設定
$this->csvData = $sql->getQuery()->execute();
// 設定した変数はテンプレートphpで使用する
*** テンプレートphp ***
<?php foreach($csvData as $dataObj): ?>
  <?php echo $dataObj->getId() ?>
  // ここでさらにループしたり
<?php endforeach; ?>
*** SQLの結果の格納オブジェクト? ***
class Product extends sfDoctrineRecord {
    // メインテーブル?
    public function setTableDefinition() {
        $this->setTableName('product');
        $this->hasColumn('name', 'string', 255, array(
             'type' => 'string',
             'length' => '255',
             ));
        // 他複数のhasColumn
    }

    // 結合テーブルの設定?
    public function setMany() {
        $this->hasMany('hoge2', array(
            'local' => 'mst_id',
            'foreign' => 'id'
            ));
        // 他複数のhasMany
    }
}


このCSV出力がとても遅く、改修する必要が出てきました。
調査したところ、DBからのデータ取得は問題なく、②のループ処理で極端に遅いことがわかりました。
3重ループほどしているのが原因だと思います。
そのためループせずに検索結果をCSVに出力するよう考えていますが、方法が中々見つかりません。
ループせずにCSVを出力する方法はありますでしょうか?

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • miyahan

    2017/09/22 21:50

    チューニング・高速化というシビアな質問であれば、もう少し現状の処理方法(コード)が判らないと答えづらいです

    キャンセル

  • red13

    2017/09/22 23:21

    とりあえず「ループ使わずにCSV出力できるか?」ぐらいしか考えていなかったので、一旦はこのままにしています。

    キャンセル

回答 6

+2

三重ループになっているということは、データどうしの相関関係を見る、あるいは集計を行うようなコードになっている可能性があります。

そういった処理は、SQLでうまく組んでDBサーバの側に処理させる、という選択肢もあります。

ただ、それ以上のことはループの具体的な中身がわからないとなんとも言えません。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/09/22 22:18

    確かフレームワークはsymfonyを使用し、そのsymfonyの機能でSQLを作成、発行しています。
    SQLを組み替えて多重ループを避けたいのですが、そのSQLが他機能でも使用しており、
    SQL修正をするとどこまで影響が出るのか分かりません。
    そのため影響の少ないと思われるCSVの出力部分を修正しようと考えました。

    キャンセル

+1

DBのアクセスがむごくない限り、LOOP処理が3重だろうとよほど間抜けな作り方をしていない限り極端に処理が遅くなるとは考えにくいです。差支えない範囲で遅いという現行のコードを現象を再現できるものを載せては?
こうしたら良いのでは?って案がいくつも出てくるかも?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/09/23 08:53

    覚えている範囲でコードを再現してみました。
    アクションクラスでデータを取得し、テンプレートで出力値を設定しています。
    そのテンプレートをcsvとしてダウンロードさせているような感じです。

    キャンセル

+1

このCSV出力がとても遅く、改修する必要が出てきました。
調査したところ、DBからのデータ取得は問題なく、②のループ処理で極端に遅いことがわかりました。
3重ループほどしているのが原因だと思います。

DB データの取得に問題がないのであれば、問題箇所はループ処理の中身であり、中身がわからなければ、手の出しようがありません。。。

ループ毎に全走査して合致箇所だけ抜き出す。みたいな処理しているんじゃないですかね?
そのあたりを整理すれば、早くなると思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/09/23 10:18

    <?php echo $obj->getId() ?>
    と取得しているだけだと思うんですが、ループ時の中の調査もしないとダメですね。
    4クラスほど継承してるクラスがあったりで、実際に処理が実行されるクラスが見つからないのですが、
    そこはやるしかありませんね。

    キャンセル

+1

1000万回の文字連結を行っても高々数秒しかかかりません。ループを万単位で行うことはプログラミングの世界ではよくあることですし、それが数分かかるのであればループそのものではなくループ内で行っている処理に問題があると想像します。

まずは推測ではなく事実に基づき問題箇所を計測しましょう。最も簡単なのはベンチマークです。print('ポイントA:' . microtime(true)); みたいな単純なコードでもよいので、随所でタイムスタンプを計測・記録し、どこで時間がかかっているか特定しましょう。

ただ今回はちりも積もれば状態の問題なので、より詳細なプロファイリングが必要になってくると思います。余力があればお試し下さい:

またDBMSへの問い合わせが不適切ではないかも確認が必要です。

DBMSで実行したクエリのログ(general log)を取って、同じようなクエリが大量に実行されてないか調査してください。なお general log はその性質上非常に巨大になるため、解析が終わったら記録を止めるのを忘れないで下さい。また、slow log で log_queries_not_using_indexes オプションを使ってインデックスを使っていないクエリがないかも併せて調査してください。


十分なコードが提示されていないため想像でしかありませんが、考えられる原因を挙げておきます:

 実はDBMSへの大量アクセスしている

(ORマッパーを使っている場合) 結合先テーブルのカラムを読み込むときに遅延ロードが起きて、1レコード読むたびにDBへ再問い合わせしてしまっている。

→ 予めJOINして読み込むようオプションを設定する。

※個人的に一番臭いと思っています。

 実は出力に時間がかかっている

既に php://output を試しているとのことなので、この可能性は薄いでしょう。

 ループ内で別の処理・副次問い合わせを繰り返している

  • ループ外に追い出せるものは追い出す
  • 入力に対して出力が常に一定な関数(DBを検索して商品区分コードから区分名を返すとか)ならば関数キャッシュを使う
  • 必要な情報は最初のDB問い合わせ時に JOIN 等を使って一度に取り出す。

まあこれもあるならとっくに気づいているはずですから望み薄ですね。

結局のところエンジニアは超能力者ではないので真実はわかりません。:-)


以下余談:

そもそもあなたの知識ドメインでは解決できなかった問題を質問しているわけですから、あなたが考えた仮定(ループを減らせば高速化出来る!)のみ書いてもおそらく解決には結びつきません。仮に満足いく回答が得られてもそれは極大値に過ぎず、最良の方法を見つけるのは十分な "情報" が必要です。NDA等で難しい部分もあると思いますが、回答者が協力するわけですから質問者も協力してほしいところです。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/09/23 13:51

    質問内容がざっくりとしてるのは自分でもよくわかっていないためです。
    開発環境の事情により、ローカルでの開発環境を構築することができません。
    また、確認時にはテスト環境で確認していますがphpやサーバーの設定を容易に変更することができません。
    テーブルの結合はhasManyで結合していると思われますが、
    もしかしたらループ時に1件ずつid等の値でDBに探しに行っている可能性があります。
    そうなるとSQLの組み立てをしている処理を大幅に修正する必要があるかもしれません。
    よくあるDBから取得したのを配列に格納し、ループで設定する処理しか見たことがないので、
    1件ごとにDBアクセスするという考えはありませんでした。

    キャンセル

0

ループしない方法ではないですが、こんな感じでどうでしょう?

$csv=[
  ["a","b","c"],
  ["x","y","z"],
  [1,2,3],
  [10,200,3000],
  ];
foreach ($csv as $row)
{
    fputcsv(fopen('php://output', 'w'), $row);
}
$str = ob_get_clean();
print $str;

$csvはfetchAll的な処理でDBから取り出して下さい

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/09/22 22:23

    「php://output」は試しましたが、ほとんど変わりませんでした。
    3重ループになっているので、処理を一部削除して1重ループにしてみました。
    約100件で10秒ほどなのが、3秒ほどになりました。
    データ数は最大で数万件あるので、1重ループで300秒ほど、
    そのためループしない処理を試そうと思いました。

    キャンセル

  • 2017/09/22 22:36

    環境にもよりますが、mysqlでoutfileを使うという考え方もあります

    SELECT * FROM テーブル
    INTO OUTFILE '/tmp/hoge.csv'
    FIELDS TERMINATED BY ','
    OPTIONALLY ENCLOSED BY '"'

    その後、hoge.csvを公開ディレクトリに移動して
    出力してやるとかですかね・・・

    キャンセル

  • 2017/09/22 23:19

    なるほど。
    調べたらDBサーバー内のcsvファイルに出力するようですね。
    phpの実行とDBサーバーは別なので、出力後に取得する処理が必要かもしれませんね。
    それかexecやsystemを使用して
    mysql コマンド~ csv.sql > hoge.csv
    みたいにすれば良いかもしれません。

    キャンセル

0

SymfonyでSQLを生成している旨の記載がありまずが、スイッチの初期化をしていないために
全てのループでSQL生成を行っているということはないですか?

そもそもオブジェクトでのループで遅いのであれば、
オブジェクトから出力用のSQL生成だけ拾って、
後は自力で生成すればいいと思います。

Orlofskyさんの言われる通り、オブジェクトの作りに問題があるようにしか思えません。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/09/23 10:10

    全てのループでSQL生成をしているとは思えませんが、そこまでは調査できていません。
    SQLを格納するオブジェクトを追記しました。
    その格納するオブジェクトが問題なのかまでも調査できていません。

    キャンセル

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

  • ただいまの回答率 90.60%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • PHP

    19854questions

    PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

  • MySQL

    5696questions

    MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

  • Symfony

    125questions

    Symfony はPHPで記述されたWebアプリケーションフレームワークです。よく利用するコーディングをテンプレーティングするなど、Webアプリケーション開発の効率化を目的として設計されています。