迭代数据库中的行并删除它们

时间:2009-08-24 09:52:29

标签: php sql concurrency

我想检查一下,这是迭代数据库中的行并在处理时删除它们的好方法吗?我第一次

  • 抓住所有
  • 处理所有
  • 全部删除

但是,如果在“全部处理”步骤运行的同时添加了新行(来自外部),可能会出现问题。

// the limited for loop is to prevent an infinite loop
// assume the limit to be much higher than the expected
// number of rows to process
for ($i = 0; $i < $limit; $i++) 
{

    // get next mail in queue
    $st = $db->prepare('
        SELECT id, to, subject, message, attachment
        FROM shop_mailqueue
        LIMIT 0,1
    ');
    $st->execute();
    $mail = $st->fetch(PDO::FETCH_ASSOC);

    // if no more mails in queue, stop
    if ($mail === false) {
        break;
    }

    // send queued mail
    $mailer = new PHPMailer();
    $mailer->AddAddress($mail['to']);
    $mailer->SetFrom('info@xxx.nl', 'xxx.nl');
    $mailer->Subject = $mail['subject'];
    $mailer->Body = $mail['message'];
    $mailer->AddAttachment($mail['attachment']);
    $mailer->Send();

    // remove mail from queue
    $st = $db->prepare('
        DELETE FROM shop_mailqueue WHERE id = :id
    ');
    $st->bindValue('id', $mail['id']);
    $st->execute();

}

4 个答案:

答案 0 :(得分:2)

我认为你抓住所有,处理全部,删除所有方法都可以通过一个简单的改变:

  • 不要做一个简单的delete from table,而是做一个 delete from table where key in (<all keys retrieved>)

您可以轻松构建&lt;检索到的所有密钥&gt;因为你先抓住一切。

这比执行现在要做的许多查询要好(很多)。

答案 1 :(得分:1)

首先,任何时候你说“迭代数据库中的行”,你最好谈论每一行发生的一些非常复杂的操作。否则,你可以而且应该一起思考!

看起来你在发送所有电子邮件后将所有内容都放在邮件队列中并转储表。现在,我应该注意到你的limit 0,1有点令人不安,因为你没有order by。没有数据库以可靠的顺序存储东西(并且有充分的理由 - 它必须不断地移动以优化它们)。所以,更好的代码是:

// get next mail in queue
$st = $db->prepare('
    SELECT id, to, subject, message, attachment
    FROM shop_mailqueue
    ORDER BY id ASC
    LIMIT 0, ' . $limit);
        $st->execute();

$lastid = 0;

while ($mail = $st->fetch(PDO::FETCH_ASSOC))
{    
    // send queued mail
    $mailer = new PHPMailer();
    $mailer->AddAddress($mail['to']);
    $mailer->SetFrom('info@xxx.nl', 'xxx.nl');
    $mailer->Subject = $mail['subject'];
    $mailer->Body = $mail['message'];
    $mailer->AddAttachment($mail['attachment']);
    $mailer->Send();

    $lastid = $mail['id'];
}


// remove mail from queue
$st = $db->prepare('DELETE FROM shop_mailqueue WHERE id <= ' . $lastid);
$st->execute();

这样,你只需要对数据库进行两次(计数'em!)查询,这比每次拉回一行 更优化。

现在,如果整个$limit交易是不必要的,并且您真的想要撤回队列中的所有行,然后只是将队列转储出来,请将delete语句更改为:< / p>

TRUNCATE TABLE shop_mailqueue

请参阅,truncate不受事务逻辑的约束,只需擦除表格,没有问题。因此,它非常快(无论表大小如何,以毫秒为单位)。 delete只是没有那种速度。请记住truncate是一个核选项 - 一旦你truncate,它就消失了。

答案 2 :(得分:0)

您可以在处理过程中锁定表格

http://dev.mysql.com/doc/refman/5.0/en/lock-tables.html

答案 3 :(得分:0)

我认为你做得很好。您获取所有内容,处理它们并仅删除您处理的内容。我也不会在你的过程中锁定表格,因为你会阻止那些创建记录的人。

如果您遇到的唯一问题是处理在发送现有电子邮件时可能已创建的电子邮件,那么您可以在所有程序之上创建一个while循环以便始终运行当有新记录时。

此外,为什么要获得LIMIT 0,1$limit)次,如果您只需修改查询以在一个查询LIMIT 0,$limit中获取最多$ limit项。