PHPとMySQLを使い、過去15日間のデータを表示できるようにしたいと思っています。


登録値がなかったとしても必ず過去15日間を表示する為に、forを使い、以下のようにしています。

for($i=0;$i<15;$i++){
$acc_day = date("Y-m-d",strtotime("-$i day"));
$sql = "SELECT date,count(acc) AS cnt FROM access WHERE date='$acc_day' GROUP BY acc";
$res = mysql_query($sql,$conn);
$acc = mysql_fetch_array($res);

echo $i.":";
echo number_format($acc[cnt])."<br>";
}

しかし、これだとforの度にクエリを発行しているので、そこが気になっています。

もっと簡略化した、データベースに負担のかからない書き方というのがあれば、教えて下さい。
PHPは5.1.20、MySQLは4.1.22を使用しています。

回答の条件
  • 1人2回まで
  • 登録:2008/03/06 15:01:49
  • 終了:2008/03/08 00:03:16

回答(3件)

id:b-wind No.1

b-wind回答回数3344ベストアンサー獲得回数4402008/03/06 16:33:08

ポイント40pt
$day_map = array();
for($i=0;$i<15;$i++){
  $acc_day = date("Y-m-d",strtotime("-$i day"));
  $day_map[$acc_day] = 0;
}

$start_day = $day_map[0];
$end_day   = $day_map[-1];
$sql = "SELECT DATE_FORMAT(date,'%Y-%m-%d') AS date, count(acc) AS cnt FROM access WHERE date BETWEEN '$start_day' AND '$end_day' GROUP BY acc";
$res = mysql_query($sql,$conn);
while ( $row = mysql_fetch_array($res) ) {
  $day   = $row['day'];
  $count = $row['cnt'];
  $day_map[$day] += $count;
}

foreach ( $day_map as $date => $count ) [
  echo $date.":";
  echo number_format($count)."<br>";
}
}

試してないけど。

id:kt26

うーん、、、当初の希望である「簡略化した」という希望に沿わない気がします。。


とりあえず試して参考にします。ありがとうございました。

2008/03/06 17:30:54
id:pahoo No.2

pahoo回答回数5960ベストアンサー獲得回数6332008/03/06 18:31:19

ポイント15pt

dateカラムは文字列型のようなので、下記のようにすればforループは不要になると思います。

$acc_day = date("Y-m-d",strtotime("-14 day"));
$sql = "SELECT date,count(acc) AS cnt FROM access WHERE strcmp(date,'$acc_day')>=0 GROUP BY acc";

実際には、strcmp関数の代わりに

date>='$acc_day'

で処理できるのですが、これが正規のやり方かどうかは分かりません。


参考サイト

id:kt26

dateなんで文字列ではなく日付型ですよ。


また、上記の方法で15日間のデータが出せるのでしょうか?試してみたのですが、何も出力されませんでした。

2008/03/06 22:38:52
id:y-kawaz No.3

y-kawaz回答回数1420ベストアンサー獲得回数2252008/03/06 18:41:50

ポイント25pt

以下のようなSQLを投げればデータの無い日付はcntがゼロとして一発で取得できますが、求めているのはこういうことで合ってるでしょうか?

SELECT days.date, count(acc) AS cnt
FROM (  SELECT '2008-03-02' AS date
  UNION SELECT '2008-03-03'
  UNION SELECT '2008-03-04'
  UNION SELECT '2008-03-05'
  UNION SELECT '2008-03-06'
  UNION SELECT '2008-03-07'
  ) days
LEFT OUTER JOIN access ON days.date=access.date
GROUP BY access.acc
ORDER BY days.date, access.acc;
id:kt26

確かに、この方法だとデータの内日付でも表示できるのですが、

やはりUNIONしている箇所はforで取得するしかないですよね?

もしくは

$day01 = date("Y-m-d");

$day02 = date("Y-m-d",strtotime("-1 day"));

と言う風に15個の変数とSQL文が必要になると思うのです。

そうすると、処理が遅くなると思います。


私もLEFT JOINのようにNULLでも表示される方法を考えたのですが、なかなか難しそうですね。素直にPHPだけで完結したらいいのかな・・。

2008/03/06 22:50:54
  • id:b-wind
    >簡略化した
    とは何に対するものですか?コード?
    あと、
    >データベースに負担のかからない
    とどっちが優先?
  • id:kt26
    簡略化→コード
    優先→データベスに負担のかからない

    です。

    私が想像するのは、SQLの1文だけで、テーブル上に値が保存されていなくても、開始期間~終了期間まで表示する方法があるのではないか?と思っています。(自分では書き方が分かりませんが。。)

    b-windさんの方法は、PHPを使い配列に分けて処理する方法で、これはこれで理にかなっていると思うのですが、SQLだけで出来ればそちらの方が良いと持っています。
  • id:b-wind
    >SQLの1文だけで、テーブル上に値が保存されていなくても
    不可能じゃないと思うけど、見難いうえに性能も悪くなるよ?
  • id:kt26
    アクセス解析のようなものを作ろうと思ったので、PHPの配列を使う方法だと、柔軟性が無くなってしまうと思い、SQLだけで出来ないかな?と思って質問した次第です。

    性能が悪くなるなら使えませんよね・・。
  • id:b-wind
    > PHPの配列を使う方法だと、柔軟性が無くなってしまうと思い
    むしろアクセス解析なら SQL だけで何とかしようとするほうが柔軟性が無くなると思うが。

    データの格納部分(DB)とデータの加工( PHP 等での表示 )は役割分担させたほうがよいかと。
  • id:pahoo
    #2で回答した者です。
    >dateなんで文字列ではなく日付型ですよ
    とコメントをいただきましたが、PHPスクリプトの2行目
    $acc_day = date("Y-m-d",strtotime("-$i day"));
    で$acc_dayに格納されるのは文字列型です。
    もしMySQL側のdataカラムが日付型なのだとすると、おそらくMySQLでキャスト(型変換)が行われていると考えられます。そのため、#2で回答したstrcmp関数や不等号演算子が適用できなかったものと思われます。
  • id:kt26
    b-windさん

    >>データの格納部分(DB)とデータの加工( PHP 等での表示 )は役割分担させたほうがよいかと。

    そうですよね。あまりSQLだけにこだわっても処理が遅くなっては意味無いですからね。b-windさんの方法を参考にさせていただきます。

    pahooさん

    >$acc_dayに格納されるのは文字列型です。

    それはそうですよね。変数に入れているのですから。
    「dateカラムが文字型のようなので~」とあったので、
    日付型と返信させていただきました。

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

絞り込み :
はてなココの「ともだち」を表示します。
回答リクエストを送信したユーザーはいません