0

私はこれをやろうとしています:

<?php
  $good_customer = 0;
  $q = mysql_query("SELECT user FROM users WHERE activated = '1'"); // this gives me about 40k users

  while($r = mysql_fetch_assoc($q)){
    $money_spent = 0;

    $user = $r['user'];
    // Do queries on another 20 tables
    for($i = 1; $i<=20 ; $i++){
      $tbl_name = 'data' . $i;

      $q2 = mysql_query("SELECT money_spent FROM $tbl_name WHERE user = '{$user}'");
      while($r2 = mysql_fetch_assoc($q2)){
        $money_spend += $r2['money_spent'];
      }

      if($money_spend > 1000000){
        $good_customer += 1;
      }
    }
  }

これはほんの一例です。シングルユーザーのローカルホストでテストしていますが、非常に高速に戻ります。しかし、私が 1000 を試してみると、40,000 人のユーザーについても言及されていなくても、永遠にかかります。

とにかく、このコードを最適化/改善するには?

編集:ちなみに、他の20のテーブルにはそれぞれ〜20〜40kのレコードがあります

EDIT2:

さて、「お金を使う」という考えを捨ててください。これは私の現在の構造です:

ユーザーテーブル => ユーザーは PK

logs_week_1 テーブル => ユーザーは FK です。

logs_week_2 テーブル => ユーザーは外部キー

logs_week_3 テーブル => ユーザーは外部キー

... 将来的にはより多くのログ テーブルが作成される予定です。

各ログテーブルに保存されている、サイトでの「平均時間」を見つけたいです。

つまり、ログを毎週保存するのは悪い考えだとおっしゃいましたか? 1 つのテーブルにマージする必要がありますか?

4

4 に答える 4

2

モデルに問題があるようです。data-columnを持つ 1 つではなく、20 の -tables があるのはなぜweekですか?

次に、次のことができます

Select user, Sum( money_spent ) As total_money_spent
From data
Group By user

あるいは

Select Count(*) As good_customer_count
From data
Group By user
Having Sum( money_spent ) > 1000000

現在の構造では、次のようなことしかできません。

Select u.user, d1.money_spent + d2.money_spent + ...
From users u
Join data1 d1 On ( d1.user = u.user )
Join data2 d2 On ( d2.user = u.user )
...

また

Select Count(*) As good_customer_count
From
  ( Select d1.money_spent + d2.money_spent + ... As total_money_spent
    From data1 d1
    Join data1 d1 On ( d1.user = u.user )
    Join data2 d2 On ( d2.user = u.user )
    ...
  )
Where total_money_spent > 1000000

これは確かに現在のソリューションよりも高速です。


また、ページに費やされた時間は、数値フィールドに格納する必要があります。

于 2010-04-21T05:31:15.583 に答える
1

ピーターはすでに良い答えを出していたので、適切な設計でクエリがどのように見えるかのみを投稿します(すべてのログデータは1つのテーブルにあります)

SELECT user, AVG(TIMEDIFF(start_time, end_time)) AS average_time
FROM logs
GROUP BY user

上記に条件をさらに適用して、特定の期間 (週、月など) のみの統計を取得するか、別のレベルでグループ化することもできます。

MAX と COUNT を同じクエリで (標準偏差やその他の集計関数と同様に) 効果的な方法で取得することもできます。

もちろん、より大きなデータ セットで最高のパフォーマンスを得るには、インデックスに注意してください。

編集:

私がピーターに+1を与えていたのと同じように、彼がUNION ALLオプションについて言及していないことに気づきました

したがって、可能です(これは最適ではなく、他の人から与えられた設計上の問題の警告と矛盾しません)

SELECT user, AVG(TIMEDIFF(start_time, end_time)) AS average_time
FROM (
    SELECT * FROM log_week_1
    UNION ALL
    SELECT * FROM log_week_2
    UNION ALL
    SELECT * FROM log_week_3
    ...
) U
GROUP BY user

また、このユニオンの VIEW を作成することもできます。

于 2010-04-21T06:17:57.390 に答える
0

40k ユーザーの場合、1 + 20*40k クエリを作成しています。これはいずれにしても遅くなります。ログを 20 個のテーブルに保持するのをやめます。データベースを別の方法で設計する必要があります。適切に設計されたデータベースでは、これはすべて 1 つのクエリで完了する必要があります。

SELECT count(user) as good_customers FROM users JOIN $tbl_name ON users.user = {$tbl_name}.user ON WHERE users.activated = '1' HAVING SUM(money_spent) > 100000.

最悪のシナリオでは、テーブルごとに 1 つのクエリでこれをすべて行う必要があります。

SELECT user, SUM(money_spent) as money_spent FROM users JOIN $tbl_name ON users.user = {$tbl_name}.user ON WHERE users.activated = '1'.

次に、これらの 20 個の money_spent 列を合計すると、答えが得られます。

于 2012-12-11T07:01:05.657 に答える
0

サイトで費やした時間は、時間ではなく数値 (分または秒) として保存する必要があります。次に、この値の平均と合計を計算できます。ログを 1 つのテーブルにまとめます。

于 2010-04-21T05:56:37.283 に答える