MySQL PHP查询优化 - 反馈

时间:2013-03-25 22:04:20

标签: php mysql performance apache optimization

我需要每天运行这个脚本(cron)来更新1个月的反馈总量(至少这是我现在设计的)。这是我的代码。有没有人对我应该如何解决这个问题有更好的了解?也许改变我的方式或优化我的updateMonthlyFeedback.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
);

5 个答案:

答案 0 :(得分: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

您应该从此查询中获取相同的三个值作为要获取的三个记录。

你可以做更多的事情,但这是一个好的开始。我相信你会得到其他答案来帮助你。

希望有所帮助。

答案 1 :(得分:1)

好的,正如其他人所说,使用PDO / MYSQLI。但是,使用您已有的代码,这里有两种方法可能会有效并且表现更好。

首先使用相关子查询来获得负/正/中性值。这很好,因为它很短,但它绝不是理想的。您仍在对数据库执行大量查询(每个uid为3次,初始更新为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中格式化它,循环并执行更新。这个可能会表现得更好,因为您运行的查询数量要少得多(1个用于获取数据+ 1个用于每个uid)。

//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());
}

答案 2 :(得分: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());
}

答案 3 :(得分: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

上玩这个

答案 4 :(得分:-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";

只能与DB进行一次批量连接,并将打印出数组中的所有数据。