PHP ve MySQL sorguları iç içe döngü yardıma mı ihtiyacınız var?

4 Cevap php

Bunu almak için çalışıyorum:

<?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;
      }
    }
  }

Bu sadece bir örnektir. Ben localhost üzerinde test ediyorum, tek bir kullanıcı için, çok hızlı döner. Ben 1000 deneyin, hatta 40k kullanıcıları söz değil, sonsuza kadar sürer.

Neyse bu kod geliştirmek / optimize etmek için?

EDIT: By the way, each of the others 20 tables has ~20 - 40k records

EDIT2:

Okay, "para harcama" fikrini bırakın. Bu benim şimdiki yapıları:

kullanıcı tablo => kullanıcı PK olduğunu

logs_week_1 tablo => kullanıcı FK olduğunu.

logs_week_2 tablo => kullanıcı FK olduğunu

logs_week_3 tablo => kullanıcı FK olduğunu

... Gelecekte daha günlükleri tablo var olacaktır.

Ben zaman günlükleri tabloları her saklanan benim sitede harcamak "ortalama zaman" bulmak istiyorum.

Yani siz günlükleri saklamak, diyordun haftalık kötü bir fikir mi? Ben bir tablo birleştirmek gerekir?

4 Cevap

Eğer modeli ile ilgili bir sveyaun var gibi geliyveya. Neden 20 data-tabloları yerine bir week-sütunu ile bir tane var mı?

Sonra bir yapabilirdi

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

ya da

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

Mevcut yapısı ile sadece böyle bir şey yapabilirsiniz:

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 )
...

veya

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

Bu kesinlikle geçerli çözüm daha hızlı olacaktır.


And the time spent on a page should be stveyaed in a numeric field.

Peter zaten iyi bir cevap verdi ben sadece sorgu (bir tablodaki tüm günlük veriler) uygun tasarımı ile nasıl görüneceğini yayınlayacağız

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

Yukarıdaki koşullar sadece belirli bir süre (hafta, ay, vb) istatistikleri elde etmek ya da başka bir düzeyine göre de grup olabilir nerede daha uygulayabilirsiniz.

Ayrıca MAX almak ve etkin bir şekilde aynı sorguda (yanı sıra, standart sapma ve other aggregate function) içinde sayabiliriz.

Tabii ki, daha büyük veri setleri ile en iyi performans için indeksler ilgileneceğim.

EDIT:

Peter +1 veriyordu gibi ben o UNION ALL seçeneği bahsetmiyorlar fark

Yani, (bu optimal değildir ve başkaları tarafından verilen tasarım sorunu uyarıları çelişmez) olabilir

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

Ve aynı zamanda bu birliğin bir görünüm oluşturabilirsiniz.

Sen değil zaman olarak, (dakika veya saniye) numarası olarak sitenizde harcanan zamanı saklamak gerekir. Sonra bu değer ortalamaları ve toplamları hesaplayabilirsiniz. Ve bir tabloda günlüklerini tutmak.

40k kullanıcılar için 1 + 20 * 40k sorgular yaratıyor. Bu, her durumda yavaş olacaktır. 20 tablolarda günlükleri tutarak durdurun. Siz başka bir şekilde veritabanı tasarımı olmalıdır. Uygun şekilde tasarlanmış bir veritabanında bu hepsi 1 sorgu ile gerçekleştirilebilir olmalıdır

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.

Kötü senaryoya göre de bütün her tablo için 1 sorgu ile yapmalıdır.

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

Daha sonra bu 20 money_spent sütunları toplamı ve size cevabım var.