プロが教えるわが家の防犯対策術!

何か、ここの質問掲示板は自己レスが出来ないシステムらしいので(自己解決は放置推奨?)、同じような問題で躓く方のためにここに質問内容と解決方法を記述しておきます。

■環境
サーバ:Apache
言語 :Perl
DB :MySql

■質問内容
データベースから取得したデータを配列に格納することができません。
唯一、以下の方法で格納できたのですが、この場合データベースの項目の数が、あらかじめ分かっている場合にしか使用できません。googleで検索して出てくるサンプルは全てループ内でprintしているので参考になりませんでした。
やりたいことは単純で、データベースにクエリーを発行した結果の複数レコードをそのまま配列に格納することです。perlに詳しい方がおられましたらよろしくお願いします。

■解決方法
#以下の一行を
@result[$i] = ([$work[0],$work[1],$work[2],]);
#以下に変更
push @result, [@work];
※但し、実行速度などのパフォーマンスまでは実験していません。取得するレコード数が多くなった場合のパフォーマンスの低下など考慮が必要なことがあるかもしれませんし、もっと効率的なやり方があるかもしれません。もしより完成度の高い解決方法をご存知の方がおられましたら、後学のためにも回答をお願いいたします。

_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
■データベース内のデータ
id=1,subid=1,data1=aaa
id=1,subid=2,data1=bbb
id=1,subid=3,data1=ccc

■期待する結果(一部修正)
@result = (["1","1","aaa"],
["1","2","bbb"],
["1","3","ccc"],
)
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/

■実際のソース
**********************************************************
use DBI;
#//■DB接続
$db = DBI->connect("DBI:mysql:$DbName:$DbHost", $DbUser, $DbPass);
$sth = $db->prepare($sql);
$sth->execute;
$cnt = $sth->rows;

for($i=0; $i<$cnt; $i++){
@work = $sth->fetchrow_array;
#以下の一行を
#@result[$i] = ([$work[0],$work[1],$work[2],]);
#以下に変更
push @result, [@work];
}

#//■CLOSE
$sth->finish;
$db->disconnect;

for($i=0; $i<$cnt; $i++){
print "<p>".$result[$i][0]."/".$result[$i][1]."/".$result[$i][2]."</p>\n";
}
**********************************************************

■表示
1/1/aaa
1/2/bbb
1/3/ccc

A 回答 (3件)

こんにちは、


>*******************************************
>◆[2]リファレンスを代入した場合
>for($i=0; $i<$sth->rows; $i++){
>@work = $sth->fetchrow_array;
>$result[$i] = \@work;
>}
>※0.002~0.004秒(10件)0.4~0.5秒(50件)
>*******************************************
これの @result には期待した結果が得られなかったんじゃないですか?
@result の各要素に格納されたのは全部同じ @work へのリファレンスになる気がします
my @work = $sth->fetchrow_array;
でレキシカル宣言すれば、それぞれ違う @work へのリファレンスになり期待した結果になると思います。
(検証してませんので違ったらすみません。)


>もともと[3]のやり方は随所で「メモリーを大量に消費するので非推奨」
>*******************************************
>◆[3]一度に全部のデータを取得した場合
>$ref = $sth->fetchall_arrayref;
>@result = @$ref;
>※0.002~0.004秒(10件)0.5~0.6秒(50件)
>*******************************************
確かにこれはそうだと思い、(一応)とつけて書きました。
ですが、どの方法でも最終的に全てのデータを @result に格納するつもりなら、
結局メモリは使っちゃいますよね。
なら、
>@result = @$ref;
こういった形でコピーせずに、取得したリファレンスを直接使えば、
fetchall_arrayref でも良いと思いますけど。

データの数を制限するなら、
SQLを書くときにそうなるように書くか(その方法は知りません)

my @recs = ();
while ( my $rec_ref = $sth->fetchrow_arrayref ){
push @recs , $rec_ref;
last if scalar @recs > 100;
}

みたいにするかですかね


>配列に格納すること自体をあまりしないのかもしれないですね。
まあ、表示するだけなら、配列にバッファしたりしないで、
一つずつ処理した方が良いのかも知れませんが、
そうもいかない場合もあるでしょうね、
まあ、大きいデータだと分かっているなら、
不用意にコピーせずに、リファレンスをうまく使うのが良いかもですね。


まあ、私は一般人(謙遜じゃありません)なので、参考程度で。
    • good
    • 0
この回答へのお礼

また前後してしまいました。
[2]はご指摘の通りmyが抜けてますね。ソースにはコメントで記述があるのでこちらへ書き込むときに消えたのだと思います。

[3]は、@$refのコピーを生成しているからその分単純に時間が掛かっているのですね。プログラムの構造上、取得したデータ全体の配列、若しくはデータの配列への参照が必要だったのですが、$sth->fetchrow_arrayrefで取得した$refの参照をreturnしてあげた方がパフォーマンス的によさそうですね。実際はSQLを発行する際にソートと絞り込みは行うのでそんなに膨大なデータにはならないと思います。

大分助かりました、ありがとうございました。

お礼日時:2007/08/17 17:59

こんにちは、


>完成度の高い解決方法
かどうかは?かも知れませんが。

なにしろ TMTOWTDI(There's more than one way to do it.)
なのでいろいろですね、
速さについてはベンチマーク取ったりしないと分かりません。

ループで配列にフェッチなら、
my @recs = ();
while ( my @rec = $sth->fetchrow_array ){
push @recs , \@rec;
}

もしくは配列のリファレンス
my @recs = ();
while ( my $rec_ref = $sth->fetchrow_arrayref ){
push @recs , $rec_ref;
}


一応、一度に全部ってのもありますね。
my $array_ref = $sth->fetchall_arrayref;


で、最初のヤツですけど、
>@result[$i] = ([$work[0],$work[1],$work[2],]);
これは理解しにくいですね。
配列のスライスへ、無名配列へのリファレンスを一つリストとして代入ですかね?
分かっていてやっているならかまいませんけど。

@work をmyでレキシカル宣言しといて、
$result[$i] に @work へのリファレンスを代入すれば、
御希望の処理になったかも知れません。

for($i=0; $i<$cnt; $i++){
my @work = $sth->fetchrow_array;
$result[$i] = \@work;
}

で、push にして、$i が必要なくなったなら、
while ( my @work = $sth->fetchrow_array ){
 push @result, \@work;
}
の方が私は見やすいですね。
参考までに。

参考URL:http://search.cpan.org/~timb/DBI-1.58/DBI.pm

この回答への補足

回答ありがとうございます。

早速教えていただいたいくつかの方法で実験してみました。ネットワーク上のサーバでの実験なので、そのときの回線速度に影響されやすいですが、複数回数実験した平均値で計測してみました。(異常に高い数値は除外)取得データ10件の場合のページリクエストしてから表示されるまでの時間と50件の場合です。

*******************************************
◆[1]pushで配列に格納した場合
for($i=0; $i<$sth->rows; $i++){
@work = $sth->fetchrow_array;
push @result, [@work];
}
※0.002~0.004秒(10件)0.4~0.5秒(50件)
*******************************************

*******************************************
◆[2]リファレンスを代入した場合
for($i=0; $i<$sth->rows; $i++){
@work = $sth->fetchrow_array;
$result[$i] = \@work;
}
※0.002~0.004秒(10件)0.4~0.5秒(50件)
*******************************************

*******************************************
◆[3]一度に全部のデータを取得した場合
$ref = $sth->fetchall_arrayref;
@result = @$ref;
※0.002~0.004秒(10件)0.5~0.6秒(50件)
*******************************************

実験の結果、[1]と[2]は殆ど差が出ませんでしたので、好みの問題かもしれません。ただ共通して言えることは、データ件数は5倍程度なのに、掛かった時間が100~200倍となっているので、大規模データを配列で運用するのは限界があるのかなと思います。
次に[3]の結果ですが、10件程度だと[1][2]との差はありませんが、50件になると250倍~300倍となってしまい、もともと[3]のやり方は随所で「メモリーを大量に消費するので非推奨」との記事が散見されたので実験はしていませんでしたが、実行速度の面においても厳しいのかなと思いました。

ちなみにperlだと一般的にどのやり方が主流なのでしょうか?もしかすると配列に格納すること自体をあまりしないのかもしれないですね。

補足日時:2007/08/17 15:46
    • good
    • 0
この回答へのお礼

自分の記事を訂正できないようなのでこちらで失礼します。
実験で時間が掛かりすぎると思って調べたところブラウザの表示に時間を取られているようでした。再計測したら以下の通りでした。これだとどれを使っても許容範囲のような気もしますね。

___10件____50件
[1]0.002~3____0.003~4
[2]0.002~3____0.003~5
[3]0.002~4____0.004~7

お礼日時:2007/08/17 17:42

記述したところで質問でない物は削除されますので無意味かつネット資源の無駄遣いという事で有害ですらあります。



日記ならチラシの裏にでもどうぞ。

この回答への補足

****************************
※但し、実行速度などのパフォーマンスまでは実験していません。取得するレコード数が多くなった場合のパフォーマンスの低下など考慮が必要なことがあるかもしれませんし、もっと効率的なやり方があるかもしれません。もしより完成度の高い解決方法をご存知の方がおられましたら、後学のためにも回答をお願いいたします。
****************************
これが質問なのですが。読めませんか?
だから質問の緊急度もちゃんと変えてあるでしょう。

逆に伺いたいのですが、貴殿の書き込みによって誰か得をする方でもおられますか?ネット資源の無駄遣いと言うのなら同じような質問が大量に出回って放置されている方がよりそうなのだと私は思いますけどね。特に技術系の質問は質問内容と結果が結びついて初めてネット資源として有効なのだと思いますが。
少なくとも貴殿のこの書き込みを見て喜ぶのは貴殿自身だけで、全く他を益さないということだけは理解した方が今後の貴殿自身のためだと思います。
まぁ、貴殿のような人物が私の目の前で堂々と同じことが言えるとはとても思えませんが、顔が見えないということで好き勝手ができる。ネットの弊害ですね。

ストレス発散のための書き込みならチラシの裏にでもどうぞ。

補足日時:2007/08/16 13:37
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!