2

データベースから30日より古いデータを削除するcronスクリプト(MySQLデータベース用のPHP PDS)に取り組んでいます。テーブルとステートメントにカスケードされた結合と削除を試みましたが、うまくいきませんでした。そのため、ここでより簡単なアプローチを使用して機能させましたが、これが最もプロセッサに効果的な方法ではないことを知っています。

これらのテーブルの一部(特にリンクテーブル)には数十万の行があることに注意してください。そのため、ここにあるものよりも少し賢くなりたかったのです。

テーブルの構造は簡単で、タイムスタンプと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]");
    }
}

これを1つのステートメントにまとめる方法はありますか?どうぞよろしくお願いいたします。

編集:これが私が最終的に得たものです。

/* 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']));
    }
}   
4

5 に答える 5

5

次のような 1 つのクエリで削除できるはずです。

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。そうであれば、内部結合を使用できます。

于 2013-02-21T16:33:21.590 に答える
1

tablekey_id がインデックス化されている場合、これは数千行であっても問題ありません。データベース スキーマをシンプルに保ちたい場合は、4 つのクエリを保持します。削除はそれほど CPU を集中的に使用しません。本当に1つのステートメントが必要な場合は、物事を複雑にし、外部キー制約を使用してカスケード削除を使用する必要があります:

CASCADE: 親テーブルから行を削除または更新し、子テーブル内の一致する行を自動的に削除または更新します。ON DELETE CASCADE と ON UPDATE CASCADE の両方がサポートされています。2 つのテーブル間で、親テーブルまたは子テーブルの同じ列に作用する複数の ON UPDATE CASCADE 句を定義しないでください。

ソース: http://dev.mysql.com/doc/refman/5.5/en/innodb-foreign-key-constraints.html

そして、これはあなたのデータベースがinnoDBであることを認めています

于 2013-02-21T16:25:37.497 に答える
1

最初に、現在行っているように ID のリストを取得し、次に次のように 4 つのクエリを作成します。

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

これは、個別の削除を行うよりもはるかに効率的であることが証明されるはずです。クエリの量が 1+4N ではなく 5 に減少します。

于 2013-02-21T16:28:33.820 に答える
0

私の知る限り、PDOは1つのステートメントしか受け入れません...

最初の(またはメインの)テーブルから削除する際のトリガーを見ることができます...

于 2013-02-21T16:24:09.587 に答える
-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)");
于 2013-02-21T16:51:30.173 に答える