简体   繁体   English

如何改进和加速使用嵌套循环的PHP脚本?

[英]How to Improve and speed up this PHP script that uses a nested loop?

I wrote a PHP script that joins two tables to display information in this format: 我写了一个PHP脚本,它连接两个表来显示这种格式的信息:

Jane Doe
Phone: 082 980 9514
Home Loan Applications (Active) - 17/07/2013
Credit Report (Free Report) (Unsubscribed) 12/06/2013

You'll notice that the first and lastname is in the first line of the output, followed by the phone number and then the mailing lists they've been subscribed. 你会注意到第一个和最后一个名字在输出的第一行,然后是电话号码,然后是他们订阅的邮件列表。 Next to the list is the status. 列表旁边是状态。 Active means that the user has not unsubscribed and is still active while the unsubscribed date is displayed if the user already opted out of the mailing list. “活动”表示如果用户已选择退出邮件列表,则在显示未订阅日期时,用户尚未取消订阅并仍处于活动状态。 Then it is followed by the registration date of the subscriber to a certain mailing list. 然后是订户到某个邮件列表的注册日期。

Here's the link to the sample tables: Tables 以下是示例表的链接: 表格

It works as it should but it takes forever to complete since there are about 76,000 records in 1 table and about 100,000 in the other. 它可以正常工作,但需要永远完成,因为1个表中有大约76,000个记录,而另一个表中大约有100,000个记录。 I would like to ask suggestions on how to optimize the code to speed up the script. 我想问一下如何优化代码以加快脚本的建议。

Here's the current code that I've written: 这是我写的当前代码:

$resultarray = array();

$rs4 = mysqli_query($con1,"SELECT interspire_customfield.subscriberid, interspire_customfield.fname, interspire_customfield.lname, interspire_customfield.phone, emailaddress, subscribedate, unsubscribed, interspire_customfield.listid, listname FROM `interspire_subscriber` INNER JOIN `interspire_customfield` ON interspire_subscriber.subscriberid = interspire_customfield.subscriberid GROUP BY emailaddress");

while($row4 = mysqli_fetch_array($rs4)) {
        $resultarray[] = $row4['subscriberid'].",".$row4['fname'].",".$row4['lname'].",".$row4['phone'].",".$row4['emailaddress'];
}

foreach ($resultarray as $arrlist) {

    $arr = explode(',', $arrlist);
    $sid = $arr[0];
    $frstname = $arr[1];
    $lstname = $arr[2];
    $pnum = $arr[3];
    $emailadd = $arr[4];

    echo $frstname." ".$lstname."<br />";
    echo "Phone: ".$pnum."<br />";

    $rs5 = mysqli_query($con1,"SELECT interspire_customfield.subscriberid, subscribedate, unsubscribed, interspire_customfield.listid, listname FROM interspire_subscriber INNER JOIN interspire_customfield ON interspire_subscriber.subscriberid = interspire_customfield.subscriberid WHERE interspire_subscriber.emailaddress = '$emailadd' GROUP BY interspire_subscriber.listid");

    if (!$rs5) {
    printf("Error: %s\n", mysqli_error($con));
    exit();
}

    while($row5 = mysqli_fetch_array($rs5)) {
        $listname = $row5['listname'];
        $subdate = $row5['subscribedate'];
        $unsub = $row5['unsubscribed'];

        if($unsub == "0"){
            $stat = "Active";
        }else{
            $stat = date('d/m/Y', $unsub);
        }

        $subdt = date('d/m/Y', $subdate);

        echo "* $listname ($stat) - $subdt <br />";
    }
        echo "<br />";
}

If there are indexes (if not, create it on subscriberid in both fields) in both tables try to use query without JOIN: 如果在两个表中都有索引(如果没有,在两个字段中的subscriberid上创建它),请尝试使用不带JOIN的查询:

SELECT 
ic.subscriberid, 
ic.fname, 
ic.lname, 
ic.phone, 
ic.listid, 
emailaddress, 
subscribedate, 
unsubscribed, 
listname 
FROM `interspire_subscriber` isub, `interspire_customfield` ic
WHERE isub.subscriberid = ic.subscriberid
GROUP BY emailaddress

And use buffer to store strings before printing. 并在打印前使用缓冲区存储字符串。 You can use string value or array with join() method or just ob_start. 您可以将字符串值或数组与join()方法或ob_start一起使用。

Your intended output necessitates the determination of exactly one set of name and phone number per email address: yet because interspire_subscriber associates multiple subscriberid values with a single emailaddress , the join with interspire_customfield (which contains the name and phone number data) is not 1:1; 你想要的输出就必须恰好一组每个电子邮件地址,姓名和电话号码的决心:只因interspire_subscriber多个关联subscriberid值与单一emailaddress ,在与加盟interspire_customfield (其中包含姓名和电话号码数据)不是1:1 ; thus it is possible that an email address may be associated with multiple different names and phone numbers. 因此,电子邮件地址可能与多个不同的姓名和电话号码相关联。

Either you must group your output by subscriberid (which presumably will not satisfy your desired result of collating each user's list subscriptions), or you must change the schema: any such change must necessarily involve a one-time determination of which values from interspire_customfield should be associated with each emailaddress . 您必须按subscriberid对输出进行分组(可能不会满足您对每个用户列表订阅进行整理所需的结果),或者您必须更改架构:任何此类更改必须包含一次性确定来自interspire_customfield值应该是与每个emailaddress相关联。 I would then suggest keying interspire_customfield on emailaddress (and dropping listid from that table and subscriberid from one of the tables, depending on whether it should be associated with a subscriber or a subscription ). 然后,我建议在emailaddress上键入interspire_customfield (并从该表中删除listid ,从其中一个表中删除subscriberid ,具体取决于它是否应与订阅者订阅相关联)。

With those changes made, you could do: 通过这些更改,您可以:

$dbh = new PDO("mysql:dbname=$dbname;charset=utf8", $username, $password);
$qry = $dbh->query('
  SELECT   emailaddress,
           c.fname, c.lname, c.phone,
           s.subscribedate, s.unsubscribed, s.listname
  FROM     interspire_subscriber  AS s
      JOIN interspire_customfield AS c USING (emailaddress)
  ORDER BY emailaddress, s.listid
');

if ($qry) {
  echo '<ol>';

  $row = $qry->fetch();
  while ($row) {
    $current_email = $row['emailaddress'];
    echo '<li>',
           htmlentities($row['fname']),' ',htmlentities($row['lname']),'<br/>',
           'Phone: ',htmlentities($row['phone']),
           '<ul>';
    do {
      $unsub = $row['unsubscribed'];
      echo '<li>',htmlentities($row['listname']),
           ' (',$unsub ? 'Active' : date('d/m/Y', $unsub),')',
           ' - ',date('d/m/Y', $row['subscribedate']),
           '</li>';
    } while ($row = $qry->fetch() and $row['emailaddress'] == $current_email);
    echo   '</ul>',
         '</li>';
  }

  echo '</ol>';
}

As for speed, you should ensure you have defined a composite index on the interspire_subscriber table over (emailaddress, listid) ; 至于速度,你应该确保你在interspire_subscriber表上定义了一个复合索引(emailaddress, listid) ; and a UNIQUE index on the interspire_customfield table over (emailaddress) . interspire_customfield表上的UNIQUE索引(emailaddress)

Whilst we're at it, you may wish to consider whether interspire_subscriber.listname is required (it appears to be denormalised, as it ought to be associated with listid in some other table)? 虽然我们正在使用它,但您可能希望考虑是否需要interspire_subscriber.listname (它似乎是非规范化的,因为它应该与其他表中的listid相关联)? You may also wish to consider storing temporal values using MySQL's native temporal data types instead of integers. 您可能还希望考虑使用MySQL的本机时态数据类型而不是整数来存储时态值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM