PHPとMySQLでアクセスカウンター(ログ履歴)のようなものを作っています。


例えば6月15日にアクセスがあればテーブルに追加してcount(フィールド名)を1とし、dateに2007-06-15を入れて保存。更新されればcountに+1され2として更新されます。次の日にアクセスがあれば、新規にテーブルに追加される、と言ったイメージです。

現在は以下のようにしています。(要約しています)
$today = date("Y-n-d"); //今日の日付を取得
$sql = "select date from counter where date='$today'";
$res = mysql_query($conn,$sql);

if(mysql_num_rows($res)=="0"){ // 登録されているか調べる
// テーブルに新規登録するコード
}else{
// テーブルを更新するコード
}

しかし、上記では効率が悪いと思い、出来るだけ簡略化したソースで新規か更新か判別出来ればと思っています。新規登録・更新を繰り返し行うと負荷もかかるそうですし。

文面だけではわかりづらいかもしれませんが、どのようなソースの書き方が良いと思われるでしょうか?

具体的なソースコードは結構ですので、構造・考え方を教えていただければと思います。

回答の条件
  • 1人2回まで
  • 登録:2007/06/15 22:26:23
  • 終了:2007/06/16 19:33:26

回答(6件)

id:t_shiono No.1

t_shiono回答回数256ベストアンサー獲得回数222007/06/15 22:40:45

ポイント25pt

テーブルを2つに分けるのはどうですか?

「全てのアクセスの履歴を格納するテーブルA」と「日付ごとのアクセス数のテーブルB」を用意しておき、日付が変わった時点で、Aを集計し、その結果を元にBを更新する。

文面からは読み取れなかったのですが、行いたいことが、リアルタイムにデータを表示するよりは、後から統計的に使いたいというように思われたので。

テーブルAのサイズは大きくなりますが、不要であればBにデータを追加した時点で過去のものを消してしまってもよいと思います。


あとは、用途次第でしょうか。

id:kt26

回答ありがとうございます。私もそれは考えていて、質問には書いていませんが、cronで処理して日付が変わったら、履歴を格納するテーブルに集計するようにしています。

ただ、もっと効率化出来ないものか?sqlの書き方で何とか出来ないものか?っと思って質問しました。

これが1コンテンツだけならいいですが、アクセス集計、ポイント履歴、ランキング履歴…っと増えていくと、倍々でテーブル数も増えていきますし‥。

2007/06/15 22:44:44
id:t_shiono No.2

t_shiono回答回数256ベストアンサー獲得回数222007/06/15 22:59:32

ポイント25pt

では、空データを常に用意しておくのはどうですか?

毎日23:59:59にでも、アクセス数0で翌日の分のデータを作成しておくと、アクセス時には、必ずデータは存在するので、updateを行うだけでよいです。

データの確認とデータの更新をアクセス時に行う以上、提示しているものよりは効率化は難しいので、データの確認を不要にする必要があると思います。

その方法としては、

常にデータを用意しておく(今回の答え)

または、

確認を不要にする(先ほどの答え)

となってしまうのかと。

いかがでしょうか?

id:kt26

なるほど。その考えはありませんでした。先に用意しておけばupdateですみますからね。

いろんな角度から回答ありがとうございます。参考にします。

2007/06/16 18:33:03
id:mass3 No.3

mass3回答回数118ベストアンサー獲得回数152007/06/15 23:05:22

ポイント15pt

掲載してあるプログラムだと、N件のアクセスがあったとき、2N回のSQLの実行がされます。内訳は以下のとおりです。

select文の実行 N回

insert文の実行 1回

update文の実行 N-1回

データベースの初心者が書いたコードはこんな風になっていることが多いです。

select文をやめてしまって、いきなりupdate文を実行、そしてupdateの影響を受けた行数が0行だったら、insert文を実行するプログラムに変更したとき、同じくN件のアクセスがあったときSQLの実行回数はN+1回ですみます。

update文の実行 N回

insert文の実行 1回

id:kt26

なるほど。先に存在するかどうか調べるのではなく、”存在していない場合”の処理(登録)にするという事ですね。

こちらも参考にさせていただきます。

2007/06/16 18:35:23
id:chuken_kenkou No.4

chuken_kenkou回答回数722ベストアンサー獲得回数542007/06/15 23:05:43

ポイント30pt

sqlの書き方で何とか出来ないものか?っと思って

表の設計等でなく、SQLの書き方に絞ると。。。

1.selectでの存在有無のチェックをやめる

(1)updateの操作行数を利用

update counter
 set count=count+1
 where date=curdate()

updateで影響を受けた行数を得る方法があると思うので、その行数が0なら、insertする。

(2)MySQL 4.1サポートのinsert文での「ON DUPLICATE KEY UPDATE」を使う。

 キー(今回の場合は、日付)重複の有無で、重複なしの場合は通常のinsert、重複の場合は「ON DUPLICATE KEY UPDATE」で指定された列の更新を行なってくれる。

MySQL AB :: MySQL 4.1 リファレンスマニュアル :: 6.4.3 INSERT 構文

id:kt26

(2)の方、初耳でしたので、凄く勉強になりました。早速実験してみます。ありがとうございました。


※後で気づいたらMySQL4.1系じゃないと駄目なんですね。。私の利用しているサーバは4.0.x系だったので、他の方法を考えます。

2007/06/16 19:28:52
id:b-wind No.5

b-wind回答回数3344ベストアンサー獲得回数4402007/06/15 23:31:55

ポイント15pt

その方法だと、トランザクションを使ってもテーブルロックでもかけない限り同時アクセス時に取りこぼしが発生しますね。


一般にアクセスログ(アクセス解析)用のデータは蓄積はアクセスごとなので頻度は高いですが、参照(統計データ)は確認したい時に見るだけなので使用頻度は低いです。


このため、1アクセスごとに1レコード(既存レコードの更新ではなく全て新規)記録していく形を推奨します。

その方がリファラやアクセス元など残せる情報も多いですし。


アクセス記録は、

INSERT INTO table ( date ) VALUES ( CURRENT_DATE );

集計処理は

SELECT count(*), date FROM table GROUP BY date ORDER by date;

とでもすればよいと思います。

id:kt26

ありがとうございます。参考にさせていただきます。

2007/06/16 19:31:22
id:mlkc81 No.6

mlkc81回答回数27ベストアンサー獲得回数22007/06/16 02:29:30

ポイント15pt

予めcount 0でレコードを入れておくというのはいかがでしょうか?

cronを利用できるなら、1日ごとに次の日の分や、1週間ごと、月ごとなりで。

そうすれば、必ず既にレコードは存在しているので、更新処理だけで済み、毎回条件判定を行う手間は省けるかと思います。

データを取り出す際のSQLで、count 0のレコードを弾くようにすれば当日以降のレコードを省くことも出来ます。

過去の日付でcount 0のレコードが残ってしまうのがデメリットですが、定期的にcount 0のレコードを削除するようにすれば、それも解決するかと思います。

思いつきそうなアイディアではありますが、参考になれば幸いです。

id:kt26

この方法も良いと思います。参考にさせていただきます。ありがとうございました。

2007/06/16 19:32:17

コメントはまだありません

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

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

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

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