2013-02-21 81 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)");