2013-03-25 26 views
0

我需要每天運行這個腳本(cron)來更新1個月的反饋總量(至少這是我現在設計的)。這是我的代碼。有人對我應該如何解決這個有更好的想法嗎?也許會改變我對此的方式或優化我的updateMonthlyFeedback.php腳本?MySQL PHP查詢優化 - 反饋

updateMonthlyFeedback.php

session_start(); 
include("db.php"); 

$sql="SELECT MAX(uid) as maxUID FROM users"; 
$result = mysql_query($sql) or die(mysql_error()); 
$row = mysql_fetch_array($result); 
$maxUID = $row['maxUID']; 

for($i=0;$i<$maxUID;$i++){ 
    $sql="SELECT COUNT(*) as negativeCount FROM feedbacks WHERE date_created >= (CURDATE() - INTERVAL 30 DAY) AND type = -1 AND uid = '$i'"; 
    $result = mysql_query($sql) or die(mysql_error()); 
    $row = mysql_fetch_array($result); 
    $negativeCount = $row['negativeCount']; 
    $sql="SELECT COUNT(*) as neutralCount FROM feedbacks WHERE date_created >= (CURDATE() - INTERVAL 30 DAY) AND type = 0 AND uid = '$i'"; 
    $result = mysql_query($sql) or die(mysql_error()); 
    $row = mysql_fetch_array($result); 
    $neutralCount = $row['neutralCount']; 
    $sql="SELECT COUNT(*) as positiveCount FROM feedbacks WHERE date_created >= (CURDATE() - INTERVAL 30 DAY) AND type = 1 AND uid = '$i'"; 
    $result = mysql_query($sql) or die(mysql_error()); 
    $row = mysql_fetch_array($result); 
    $positiveCount = $row['positiveCount']; 
    $sql = "UPDATE feedback_totals SET negativeCount = '$negativeCount', neutralCount = '$neutralCount', positiveCount = '$positiveCount' WHERE uid = '$i'"; 
    $result=mysql_query($sql) or die(mysql_error()); 
} 

MySQL表

CREATE TABLE feedback_totals (
    uid      VARCHAR(40), 
    negativeCount   int, 
    neutralCount   int,     
    positiveCount   int, 
    halfStarCount   int, 
    oneStarCount   int, 
    oneHalfStarCount  int, 
    twoStarCount   int, 
    twoHalfStarCount  int, 
    threeStarCount   int, 
    threeHalfStarCount  int, 
    fourStarCount   int, 
    fourHalfStarCount  int, 
    fiveStarCount   int, 
    PRIMARY KEY    (uid) 
    #FOREIGN KEY   (uid) REFERENCES users(uid) ON DELETE CASCADE 
); 

CREATE TABLE feedback_last_month (
    uid      VARCHAR(40), 
    negativeCount   int, 
    neutralCount   int,     
    positiveCount   int, 
    halfStarCount   int, 
    oneStarCount   int, 
    oneHalfStarCount  int, 
    twoStarCount   int, 
    twoHalfStarCount  int, 
    threeStarCount   int, 
    threeHalfStarCount  int, 
    fourStarCount   int, 
    fourHalfStarCount  int, 
    fiveStarCount   int, 
    PRIMARY KEY    (uid) 
    #FOREIGN KEY   (uid) REFERENCES users(uid) ON DELETE CASCADE 
); 

CREATE TABLE feedback (
    feedback_id    INT NOT NULL AUTO_INCREMENT, 
    uid      VARCHAR(40),INDEX (uid), 
    sender_id    VARCHAR(40), 
    type     int,    #-1 = neg, 0 = neutral, 1 = positive 
    starCount    VARCHAR(40), 
    description    VARCHAR(80), 
    date_created   timestamp DEFAULT CURRENT_TIMESTAMP, 
    fromType    VARCHAR(40), # buyer or seller 
    fromUsername   VARCHAR(40), 
    PRIMARY KEY    (feedback_id) 
    #FOREIGN KEY   (uid) REFERENCES users(uid) ON DELETE CASCADE 
); 
+1

那麼顯而易見的第一個答案將是刪除'for'循環,而不是通過uid來限制查詢,'group on' uid。 – 2013-03-25 22:07:37

+0

您確定要從*用戶*中選擇COUNT()而不是計算* feedback *行的數量嗎? – Hazzit 2013-03-25 22:44:34

+0

是的,我更新了這個。剛剛寫了一個快速原型,並沒有測試。只是想擁有一些東西而不是解釋我的問題。 – Toosick 2013-03-25 23:07:22

回答

1

好吧,正如其他人所說,使用PDO/MYSQLI。但是,使用你已有的代碼,這裏有兩種方法可能工作並且性能更好。

首先是使用相關的子查詢來獲得負/正/中性值。這很好,因爲它很短,但是它並不理想。您仍然對數據庫執行大量查詢(每個uid +最初的更新爲3)。然而,你只是從php發送一個查詢到服務器,讓數據庫完成剩下的工作。對於少數用戶來說,這可能會表現良好,但過了一段時間後,就會出現性能問題。這一個查詢將更新feedback_totals中的所有行。但是,如果feedback_totals中沒有行,則不會爲任何新的uid插入新行。

//one query, this is it. updates it all. 
$sql = "UPDATE `feedback_totals` 
     SET 
      `negativeCount`=(SELECT COUNT(*) FROM `users` WHERE `uid`=`feedback_totals`.`uid` AND `date_created` >= (CURDATE() - INTERVAL 30 DAY) AND `type`=-1), 
      `positiveCount`=(SELECT COUNT(*) FROM `users` WHERE `uid`=`feedback_totals`.`uid` AND `date_created` >= (CURDATE() - INTERVAL 30 DAY) AND `type`=1), 
      `neutralCount`=(SELECT COUNT(*) FROM `users` WHERE `uid`=`feedback_totals`.`uid` AND `date_created` >= (CURDATE() - INTERVAL 30 DAY) AND `type`=0)"; 
$result=mysql_query($sql) or die(mysql_error()); 

其次可能是從長遠來看更好。查詢一個查詢中所需的所有數據。循環通過該結果在PHP中格式化,循環和做更新。這個可能會執行得更好,因爲你運行的查詢數量要少得多(對於每個uid,獲取數據爲1)。

//query for all the data 
$sql="SELECT 
      `uid`, 
      `type`, 
      COUNT(*) AS cnt 
     FROM `users` 
     WHERE `date_created` >= (CURDATE() - INTERVAL 30 DAY) 
     GROUP BY `uid`,`type`"; 
$result=mysql_query($sql) or die(mysql_error()); 

$data = array(); 
//loop through the result 
while($row=mysql_fetch_assoc($result)){ 
    //if the uid is not in $data 
    if(!isset($data[$row['uid']])){ 
     //add it with a blank array 
     $data[$row['uid']] = array('negativeCount'=>0,'neutralCount'=>0,'positiveCount'=>0); 
    } 
    //add to the data for this uid depending on type 
    if($row['type']==-1){ 
     $data[$row['uid']]['negativeCount']=$row['cnt']; 
    } elseif($row['type']==1){ 
     $data[$row['uid']]['positiveCount']=$row['cnt']; 
    } else { 
     $data[$row['uid']]['neutralCount']=$row['cnt']; 
    } 
} 

//now loop through the data and update the table 
foreach($data as $uid=>$cnt){ 
    $sql = "UPDATE `feedback_totals` 
      SET 
       `negativeCount`={$cnt['negativeCount']}, 
       `positiveCount`={$cnt['positiveCount']}, 
       `neutralCount`={$cnt['neutralCount']} 
      WHERE `uid`=$uid"; 
    $result=mysql_query($sql) or die(mysql_error()); 
} 
+0

好吧,我明白了。但是從我已經擁有的代碼之外。會不會有更好的方法來解決這個問題?僅供參考當用戶創建時,反饋總數將被初始化。 – Toosick 2013-03-25 22:46:23

+0

我給出的代碼只是對代碼的重新配置。它應該作爲一個直接替代品。這兩個*應該*工作(我沒有你的數據庫來測試),仍然比你的代碼執行更好。 – 2013-03-25 22:56:52

+0

像我應該每天更新這些總數還是有更好的方法? – Toosick 2013-03-25 22:59:25

-1

首先嚐試改變你的MySQL的mysqli

只是改變你的db.php中爲:

$mysqli = new mysqli("localhost", "XXX", "XXX", "XXX"); 

if ($mysqli->connect_errno) { 
    printf("Connect failed: %s\n", $mysqli->connect_error); 
    exit(); 
} 

,改變你的代碼:

session_start(); 
include("db.php"); 
$sql="SELECT MAX(uid) as maxUID FROM users; "; 

for($i=0;$i<$maxUID;$i++){ 
    $sql.="SELECT COUNT(*) as negativeCount FROM users WHERE date_created >= (CURDATE() - INTERVAL 30 DAY) AND type = -1 AND uid = '$i'; "; 
    $sql.="SELECT COUNT(*) as neutralCount FROM users WHERE date_created >= (CURDATE() - INTERVAL 30 DAY) AND type = 0 AND uid = '$i'; "; 
    $sql.="SELECT COUNT(*) as positiveCount FROM users WHERE date_created >= (CURDATE() - INTERVAL 30 DAY) AND type = 1 AND uid = '$i'; "; 
    $sql.="UPDATE feedback_totals SET negativeCount = '$negativeCount', neutralCount = '$neutralCount', positiveCount = '$positiveCount' WHERE uid = '$i'; "; 
} 

if ($mysqli->multi_query($sql)) { 
    do { 
     $rows=array(); 
     if ($result = $mysqli->store_result()) { 
      while($rows[] = mysqli_fetch_assoc($result)); 
      array_pop($rows); 
      $result->free(); 
     } 
     $data[]=$rows; 
    } while ($mysqli->next_result()); 
    print_r($data); 
} else 
    echo "Error with SQL"; 

這將onlye使一批連接到數據庫,並且將打印在陣列中的所有數據。

+0

如果你打算改變DB API,你也可以使用PDO;它比mysqli更好。 – Spudley 2013-03-25 22:29:24

1

這裏顯然有相當多的重複。它大部分都可以通過重構代碼來移除,但是作爲一個起點,即使在當前進程中,也可以通過使用更好的數據庫API來使其性能更好。

因此,我建議的第一件事是停止使用mysql_xxx()函數,並切換到使用PDO庫。舊的mysql函數無論如何都不推薦使用,所以不建議儘可能使用它們,但在這種情況下,有一個使用PDO的具體原因,因爲它比舊函數具有顯着的性能優勢。

PDO允許您使用稱爲Prepared Queries的功能,如果您要重複調用類似查詢,則允許數據庫更高效地緩存查詢。

其次,查詢本身。是的,這些可以絕對簡化。循環中的三個查詢可以使用GROUP BY組合成一個查詢。該查詢將如下所示:

SELECT COUNT(*) FROM users 
WHERE date_created >= (CURDATE() - INTERVAL 30 DAY) 
AND uid = :uid 
AND type = -1 OR type = 1 OR type = 1 
GROUP BY type 

您應該從該查詢中獲得三個值作爲三個要提取的記錄。

你可以做的事情還有很多,但這是一個好的開始。我相信你會得到其他答案來幫助你進一步。

希望有所幫助。

0

我會運行三個查詢來獲得您需要的信息:

SELECT uid, COUNT(*) as negativeCount FROM users 
WHERE date_created >= (CURDATE() - INTERVAL 30 DAY) AND type = -1 
GROUP BY uid ORDER BY uid ASC"; 

SELECT uid, COUNT(*) as neutralCount FROM users 
WHERE date_created >= (CURDATE() - INTERVAL 30 DAY) AND type = 0 
GROUP BY uid ORDER BY uid ASC"; 

SELECT uid, COUNT(*) as positiveCount FROM users 
WHERE date_created >= (CURDATE() - INTERVAL 30 DAY) AND type = 1 
GROUP BY uid ORDER BY uid ASC"; 

然後他們排着隊與當前UID我會走的結果,增加了行。返回結果的一個棘手的問題是缺少一個uid表示計數爲0.但是它們是有序的,所以你應該知道增加返回結果(通過你使用的fetch_row)或者每個索引的結果。

這將是這個樣子:

// Have to load up the first result 
$p_row = mysql_fetch_array($positive_result); 
$neu_row = mysql_fetch_array($neutral_result); 
$neg_row = mysql_fetch_array($negative_result); 

for($i = 0; $i < $maxUID; $i++){ 
    $positive = $neutral = $negative = 0; 
    if($p_row[0] == $i){ 
    $positive = $p_row[1]; 
    $p_row = mysql_fetch_array($positive_result); 
    } 
    if($neu_row[0] == $i){ 
    $neutral = $neu_row[1]; 
    $neu_row = mysql_fetch_array($neutral_result); 
    } 
    if($neg_row[0] == $i){ 
    $negative = $neg_row[1]; 
    $neg_row = mysql_fetch_array($negative_result); 
    } 
    $sql = "UPDATE feedback_totals SET negativeCount = '$negative', neutralCount = '$neutral', positiveCount = '$positive' WHERE uid = '$i'"; 
    mysql_query($sql) or die(mysql_error()); 
} 
0

所有這一切都可以用一個SQL查詢來完成:

INSERT INTO `feedback_totals` (uid,negativeCount,positiveCount,neutralcount) 
SELECT users.uid, 
    COUNT(neg.feedback_id) as negativeCount, 
    COUNT(pos.feedback_id) as positiveCount, 
    COUNT(neut.feedback_id) AS neutralCount 
FROM users 
LEFT JOIN feedback neg ON neg.uid =users.uid AND neg.type=-1 AND neg.date_created >=(CURDATE() - INTERVAL 30 DAY) 
LEFT JOIN feedback pos ON pos.uid =users.uid AND pos.type=1 AND pos.date_created >=(CURDATE() - INTERVAL 30 DAY) 
LEFT JOIN feedback neut ON neut.uid=users.uid AND neut.type=0 AND neut.date_created>=(CURDATE() - INTERVAL 30 DAY) 
GROUP BY uid 
ON DUPLICATE KEY UPDATE 
    negativeCount=VALUES(negativeCount), 
    positiveCount=VALUES(positiveCount), 
    neutralCount=VALUES(neutralCount); 

ON DUPLICATE KEY UPDATE將允許查詢添加新行以及更新現有的。你可以玩SQL Fiddle