0

私はphpサイトの簡単なフォーラムを書いています。各カテゴリの投稿数を計算しようとしています。これで、カテゴリは別のカテゴリに属する​​ことができ、ルートカテゴリはNULLのparent_category_idを持つものとして定義されます。このアーキテクチャでは、カテゴリに無制限の数のサブカテゴリを含めることができ、テーブル構造をかなり単純に保つことができます。

簡単にするために、カテゴリテーブルに3つのフィールドがあるとします:category_id、、。残りのデータベース構造は適切ではないと思うので、ここでは省略します。parent_category_idpost_count

もう1つのトリガーは、categoriesテーブルを呼び出して、このトリガーを実行することです。私が欲しいのは、投稿数を更新してから、各親カテゴリを再帰的に調べて、その投稿数を増やすことです。

DELIMITER $$

CREATE TRIGGER trg_update_category_category_post_count BEFORE UPDATE ON categories FOR EACH ROW
BEGIN
IF OLD.post_count != NEW.post_count THEN
  IF OLD.post_count < NEW.post_count THEN
    UPDATE categories SET post_count = post_count + 1 WHERE categories.category_id = NEW.parent_category_id;
  ELSEIF OLD.post_count > NEW.post_count THEN
    UPDATE categories SET post_count = post_count - 1 WHERE categories.category_id = NEW.parent_category_id;
  END IF;
END IF;
END $$

DELIMITER ;

私が得ているエラーは次のとおりです。

#1442 - Can't update table 'categories' in stored function/trigger because it is already used by statement which invoked this stored function/trigger. 

投稿の総数を計算するために各ページの読み込みでcount()を実行できると思いますが、大規模なフォーラムでは、ここで何度も説明されているように、処理が遅くなります(たとえば、phpで投稿をカウントするかデータベースに保存します)。したがって、将来の保証のために、投稿数をテーブルに保存しています。さらに一歩進むために、PHPではなくトリガーを使用してこれらのカウントを更新すると思いました。

MySQLには、更新されている同じテーブルでトリガーを実行するための制限があることを理解しています。これがこのエラーの原因です(つまり、無限ループを停止します)が、この場合、ループはNULLのparent_category_idを持つカテゴリに到達すると確実に停止します。 ?このトリガーを調整する場合でも、まったく異なるものにする場合でも、何らかの解決策が必要です。ありがとう。

編集私はこれが物事を行うための最良の方法ではないかもしれないことを感謝しますが、それは私が考えることができる最良のことです。親のカテゴリを別のカテゴリに変更すると、混乱する可能性がありますが、これは、すべてを再同期する別のトリガーによって修正できます。私はこの問題を解決する方法について他の提案を受け入れています。

4

3 に答える 3

2

本当に必要な場合を除いて、通常はトリガーを使用しないことをお勧めします。再帰的トリガーは、再現が非常に難しいバグを導入するための優れた方法であり、開発者は明らかに単純なアクションの副作用を理解する必要があります。「レコードをカテゴリテーブルに挿入するだけで、データベース全体がロックされました。上"。私はこれが何度か起こるのを見てきました-誰も悪いことや愚かなことをしませんでした、それはあなたが副作用で走るリスクです。

したがって、必要なことを証明できた場合にのみ、トリガーに頼ります。一般論に基づく見知らぬ人の意見に頼るのではなく、テスト環境を整え、数百万のテストレコードをドロップし、「ページの読み込み時に投稿を計算する」ソリューションを最適化して機能するようにします。

これに役立つ可能性のあるデータベース設計は、Joe Celkoの「入れ子集合」スキーマです。これは頭を悩ませるのに時間がかかりますが、クエリは非常に高速になる可能性があります。

投稿数を事前に計算する以外に本当に解決できない問題があることがわかって初めて、トリガーベースのアプローチを検討します。「投稿数」を別のテーブルに分けます。これにより、デザインが少しすっきりし、再帰的なトリガーの問題を回避できるはずです。

于 2012-07-04T09:38:38.437 に答える
1

最も簡単な解決策は、カテゴリごとにすべての投稿を取得し、その後、スクリプト/プログラミング言語を使用してそれらをリンクすることです。

たとえば、phpの場合:

<?php
// category: id, parent, name
// posts: id, title, message
$sql = "select *, count(posts.id) From category left join posts ON posts.cat = category.id Group by category.id";
$query = mysql_query($sql);
$result = array();
while($row = mysql_fetch_assoc($query)){
  $parent = $row['parent'] == null ? 0 : $row['parent'];
  $result[$parent][] = $row;
}
recur_count(0);
var_dump($result);
function recur_count($depth){
    global $result;

    var_dump($result[$depth],$depth); 
       foreach($result[$depth] as $id =>  $o){
          $count = $o['count'];
          if(isset($result[$o['id']])){
             $result[$depth][$id]['count']  += recur_count($o['id']);
           }   

       }   
        return $count;

}
于 2012-07-04T09:51:44.803 に答える
0

さて、これをどのように解決したのか疑問に思っている人のために、トリガーとPHPの両方を組み合わせて使用​​しました。

各カテゴリに親を更新させる代わりに、次の構造に任せました。投稿はスレッドを更新し、スレッドは投稿数でカテゴリを更新します。

次に、PHPを使用してデータベースからすべてのカテゴリを取得し、次のようなものを使用して各投稿数の値を合計するループを実行しました。

function recursiveCategoryCount($categories)
{
    $count = $categories['category']->post_count;

    if(!is_null($categories['children']))
        foreach($categories['children'] as $child)
            $count += recursiveCategoryCount($child);

    return $count;  
}

最悪の場合、PHPはページの読み込みごとにすべての投稿を合計するのではなく、カテゴリの投稿の合計のみを合計します(ツリー内のどのノードにいるかによって異なります)。カテゴリの数に応じて、計算の合計を1000から10または100に減らすため、これは非常に効率的です。また、phpBBのように、同期が外れた場合に備えて投稿数を再計算するために、毎週スクリプトを実行することをお勧めします。トリガーを使用して問題が発生した場合は、その機能をコードに移動します。みんなの提案をありがとう。

于 2012-07-05T13:55:38.850 に答える