2013-02-21 62 views
2

我正在从数据库中删除超过30天的数据的cron脚本(在PHP PDS中为MySQL数据库)。我试图加入和删除级联我的表和声明,但他们没有解决,所以我在这里采取了一个更简单的方法来让它工作,但我知道这不是最有效的处理器方法。跨多个表的MySQL删除

请注意,这些表中的一些(特别是链接表)有成千上万的行,所以这就是为什么我想比我在这里更聪明一些。

表结构很简单,有一个表的关键表,有一个时间戳和一个ID。在所有表格中重复该id为tablekey_id

我目前的cron作业如下。

/* Cron script to delete old data from the database. */ 
if (@include dirname(dirname(dirname(__FILE__))) . '/dbconn/website_connections.php') { 
    $conn = $connectionFromOtherFile; 
    $keyTable = 'table_key'; 
    $linksTable = 'links'; 
    $domainsTable = 'link_domains'; 
    $terms = 'searched_domains'; 

    $query = $conn->query("SELECT `id` FROM `$keyTable` WHERE `timestamp` < (NOW() - INTERVAL 30 DAY)"); 
    $query->setFetchMode(PDO::FETCH_ASSOC); 

    while($row = $query->fetch()) { 
     $conn->exec("DELETE FROM `$linksTable` WHERE `tablekey_id` = $row[id]"); 
     $conn->exec("DELETE FROM `$domainsTable` WHERE `tablekey_id` = $row[id]"); 
     $conn->exec("DELETE FROM `$terms` WHERE `tablekey_id` = $row[id]"); 
     $conn->exec("DELETE FROM `$keyTable` WHERE `id` = $row[id]"); 
    } 
} 

有没有什么办法可以把它变成一个声明?请谢谢你的帮助。

编辑:继承人我结束了。

/* Cron script to delete old data from the database. */ 
if (@include dirname(dirname(dirname(__FILE__))) . '/dbconn/website_connections.php') { 
    $conn = $connectionFromOtherFile; 
    $keyTable = 'table_key'; 
    $linksTable = 'links'; 
    $domainsTable = 'link_domains'; 
    $terms = 'searched_domains'; 
     $delete = $conn->prepare("DELETE tablekey, linktable, domaintable, searched 
          FROM `$keyTable` AS tablekey 
          LEFT OUTER JOIN `$linksTable` AS linktable on tablekey.id = linktable.tablekey_id 
          LEFT OUTER JOIN `$domainsTable` AS domaintable on tablekey.id = domaintable.tablekey_id 
          LEFT OUTER JOIN `$terms` AS searched on tablekey.id = searched.tablekey_id 
          WHERE tablekey.id = :tablekey_id"); 

    $query = $conn->query("SELECT `id` FROM `$keyTable` WHERE `timestamp` < (NOW() - INTERVAL 30 DAY)"); 
    $query->setFetchMode(PDO::FETCH_ASSOC); 

    while($row = $query->fetch()) { 
     $delete->execute(array('tablekey_id' => $row['id'])); 
    } 
} 
+0

你有考虑外键和级联删除吗? – 2013-02-21 16:22:42

+0

为什么使用PDO构建查询字符串?它支持预处理语句,它在循环中具有性能优势,并且不易受SQL注入的影响 – GordonM 2013-02-21 16:22:57

+0

可能重复[Mysql - 使用一个查询从多个表中删除](http://stackoverflow.com/questions/4839905/mysql-delete -from-multiple-tables-with-one-query) – 2013-02-21 16:23:09

回答

5

代替你应该能够删除一个这样的查询:

DELETE kt, lt, dt, t 
FROM `$keyTable` AS kt 
LEFT OUTER JOIN `$linksTable` AS lt on kt.id = lt.tablekey_id 
LEFT OUTER JOIN `$domainsTable` AS dt on kt.id = dt.tablekey_id 
LEFT OUTER JOIN `$terms` AS t on kt.id = t.tablekey_id 
WHERE kt.id = $row[id] 

请注意,我用外连接在这里,因为我是不当然,如果keyTable以外的表格可以保证与tablekey_id有关的记录。如果他们是,你可以使用内部连接。

+0

当记录不能保证存在于其他表中并且在查询过程中出现“与MySQL服务器失去连接”错误时,我的确有这种情况。 – user109764 2014-11-19 17:18:38

+0

@ user109764可能有许多事情会导致这种情况。也许你的索引不好,你的查询时间太长,你的客户端超时。也许你的数据库服务器有问题。谁知道。它可能与这种LEFT JOIN查询本身无关。您可能需要查看MySQL日志以开始调查问题。 – 2014-11-19 22:12:32

0

据我知道PDO只接受一个说法......

你可以看一个触发从第一删除(或者你是主)表...

+0

FWIW,PDO默认接受多个语句。我希望它没有 - 它为SQL注入创造了更多的风险! – 2013-02-21 16:53:02

1

如果tablekey_id被索引,即使有成千上万行,这也应该没问题。如果你想保持你的数据库模式简单,我会保留4个查询,删除不是CPU密集型。 如果你真的想要一个说法,你将不得不使事情变得复杂,并使用级联删除与外键约束:

CASCADE:删除或更新从父表中的行,并自动删除或更新>匹配子表中的行。支持ON DELETE CASCADE和ON UPDATE CASCADE。在两个表之间,不要定义几个ON UPDATE CASCADE子句,这些子句在父表或子表中的同一列上起作用。

来源:http://dev.mysql.com/doc/refman/5.5/en/innodb-foreign-key-constraints.html

,这是承认你的数据库是InnoDB的

1

首先要检索的ID列表像你现在这样,那么你要建造4个查询这样的:

DELETE FROM 'table' where 'id' IN ('id1', 'id2', 'id4') 

这应该证明是一个更有效,然后做单独的删除。这将查询的量降低到5 1+ 4N

+0

其实这就是我所做的 - 当我不想触发! - 结合准备好的陈述。 – Brian 2013-02-21 16:29:53

-1

有时我会用这个来解决我的问题。首先将ID放入字符串中。

$ids = array(); 
while($row = $query->fetch()) { 
    $ids[] = $row[id]; 
} 
$ids_str = implode(',', $ids); 

$conn->exec("DELETE FROM `$linksTable` WHERE `tablekey_id` in ($ids_str) "); 
$conn->exec("DELETE FROM `$domainsTable` WHERE `tablekey_id` in ($ids_str)"); 
$conn->exec("DELETE FROM `$terms` WHERE `tablekey_id` in ($ids_str)"); 
$conn->exec("DELETE FROM `$keyTable` WHERE `id` in ($ids_str)");