2010-04-21 127 views
0

我想獲得這樣做:需要幫助在php和mysql中的查詢嵌套循環?

<?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,它需要永遠,甚至沒有提到4萬用戶。

無論如何優化/改善此代碼?

編輯: 順便說一句,每個其餘20桌都有約20 - 40K記錄

EDIT2:

好吧,去掉 「錢花」 的想法。這是我的當前結構:

用戶表=>用戶是PK

logs_week_1表=>用戶是FK。

logs_week_2表=>用戶FK

logs_week_3表=>用戶FK

...將在未來更多的日誌表。

我想找到他們花在我的網站上的「平均時間」,這些時間存儲在每個日誌表中。

所以你們說,每週存儲日誌是一個壞主意?我應該合併成一張表?

+0

你爲什麼要對20個不同的表格進行查詢?你的問題似乎在你的數據庫設計中。發佈你的表結構的樣子。 – Galen 2010-04-21 05:33:25

+0

哦..那些20 +++表格是每週存儲的日誌表格 – mysqllearner 2010-04-21 05:38:00

+0

BIG RED ALARM BUTTON。數據庫設計錯誤。但並不罕見。搜索「SQL Pessimization」和「SQL Antipattern」。如果您需要超過一週的數據,則不需要每週的表格。如果數據太多,則需要OLAP數據庫。 – dkretz 2010-04-21 05:42:15

回答

2

聽起來像你有你的模型有問題。爲什麼你有20 data - 表,而不是一個與week列?

那麼你可以做一個

​​

甚至

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 

這肯定比您當前的解決方案更快。


而花在頁面上的時間應該存儲在一個數字字段中。

+0

。我怎麼做這筆錢?例如00:10:23,00:12:01 etc – mysqllearner 2010-04-21 05:39:40

+0

我認爲我們需要關於您的表結構和您的數據的信息來回答這個問題。 – 2010-04-21 05:41:59

+0

'money_spent'確實聽起來像某種數字列給我... – 2010-04-21 05:45:46

0

您應該將花在網站上的時間存儲爲數字(分鐘或秒),而不是時間。然後你可以計算出這個值的平均值和總和。並將您的日誌保存在一張表中。

1

彼得已經給了一個很好的答案,我將只發布查詢將如何看待與適當的設計(在一個表中的所有日誌數據)

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

您可以進一步適用,如果上述條件得到統計只有一段時間(一週,一個月等),或者你也可以按另一個級別進行分組。

您還可以以相同的查詢(以及標準差和other aggregate function)以有效的方式獲得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。

+0

@Unreason:什麼是不同的使用聯盟和INNER加入?目前我正在使用UNION,結果對我來說似乎有點奇怪。我正在嘗試使用INNER現在加入 – mysqllearner 2010-04-21 08:10:26

+1

@mysqllearner:如果您的內部聯接是1-1,則將您的表格彼此相鄰,則可以從單個行中的所有表中選擇所有列。如果使用連接,列數保持不變(必須相同),並且結果會一個接一個地追加(UNION和UNION ALL之間性能差異很大:UNION將返回唯一行,並且需要構建索引, UNION ALL可以返回重複的記錄,但我認爲你的日誌不會重疊)。 – Unreason 2010-04-21 08:41:26

+0

@mysqllearner:我發佈了UNION ALL解決方案,因爲它在概念上是等價的(用於選擇)來修復您的設計 - 將所有日誌保留在一個表中(但它仍然具有不同的性能)。 – Unreason 2010-04-21 08:43:42

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列,你有你的答案。