0

これはMySQLとPHP用です

次の列を含むテーブルがあります。

navigation_id (unsigned int primary key)
navigation_category (unsigned int)
navigation_path (varchar (256))
navigation_is_active (bool)
navigation_store_id (unsigned int index)

データは次のように入力されます。

1, 32, "4/32/", 1, 32
2, 33, "4/32/33/", 1, 32
3, 34, "4/32/33/34/", 1, 32
4, 35, "4/32/33/35/", 1, 32
5, 36, "4/32/33/36/", 1, 32
6, 37, "4/37/", 1, 32
... another group that is under the "4/37" node
... and so on

したがって、これはツリーのような構造を表します。私の目標は、ストア ID が 32 でカテゴリ ID が 33 の場合に返される SQL クエリを作成することです。

まず、カテゴリ 33 の親である要素のグループ (この場合は 4 と 32)

次に、カテゴリ 33 の子である要素のグループ (この場合は 34、35、および 36)

次に、カテゴリ 4 (この場合は 37) の下の残りの「ルート」カテゴリ。

したがって、次のクエリは正しい結果を返します。

SELECT * FROM navigation 
WHERE navigation_store_id = 32 
AND (navigation_category IN (4, 32) 
    OR navigation_path LIKE "4/32/33/%/" 
    OR (navigation_path LIKE "4/%/" 
        AND navigation_category <> 32))

私の問題は、カテゴリの「グループ」を上記の順序で並べたいことです (最初に 33 の親、33 番目の子、最後にルート ノードの親)。したがって、最初の条件を満たす場合は最初に並べ、2 番目の条件を満たす場合は 2 番目に並べ、3 番目 (および 4 番目) の条件を満たす場合は最後に並べます。

このサイトでカテゴリ構造がどのように機能するかの例を見ることができます:

www.eanacortes.net

かなり遅いことに気付くかもしれません。私がこれを行っている現在の方法では、magento の元のカテゴリ テーブルを使用し、3 つの特に遅いクエリを実行しています。次に、結果を PHP でまとめます。この新しいテーブルを使用して、magento に関する別の問題を解決していますが、同時にパフォーマンスも改善したいと考えています。これを達成する最善の方法は、3 つのクエリをすべてまとめて、結果を適切に並べ替えることで PHP の動作を軽減することです。

ありがとう

編集

よし、今はうまくいっている。4秒から500MSに短縮。今すごいスピード:)

これがCollecitonクラスの私のコードです:

    function addCategoryFilter($cat)
    {
        $path = $cat->getPath();
        $select = $this->getSelect();
        $id = $cat->getId();
        $root = Mage::app()->getStore()->getRootCategoryId();
        $commaPath = implode(", ", explode("/", $path));

        $where = new Zend_Db_Expr(
            "(navigation_category IN ({$commaPath}) 
                    OR navigation_parent = {$id}
                    OR (navigation_parent = {$root}
                    AND navigation_category <> {$cat->getId()}))");

        $order = new Zend_Db_Expr("
                CASE
                    WHEN navigation_category IN ({$commaPath})  THEN 1
                    WHEN navigation_parent = {$id} THEN 2
                    ELSE 3
                END, LENGTH(navigation_path), navigation_name");

        $select->where($where)->order($order);
        return $this;
    }

次に、Category ブロックにある次のコードでそれを使用します。

        // get our data
        $navigation = Mage::getModel("navigation/navigation")->getCollection();
        $navigation->
            addStoreFilter(Mage::app()->getStore()->getId())->
            addCategoryFilter($currentCat);

        // put it in an array
        $node = &$tree;
        $navArray = array();
        foreach ($navigation as $cat)
        {
            $navArray[] = $cat;
        }
        $navCount = count($navArray);

        $i = 0;

        // skip passed the root category
        for (; $i < $navCount; $i++)
        {
            if ($navArray[$i]->getNavigationCategory() == $root)
            {
                $i++;
                break;
            }
        }

        // add the parents of the current category
        for (; $i < $navCount; $i++)
        {
            $cat = $navArray[$i];

            $node[] = array("cat" => $cat, "children" => array(), 
                "selected" => ($cat->getNavigationCategory() == $currentCat->getId()));
            $node = &$node[0]["children"];

            if ($cat->getNavigationCategory() == $currentCat->getId())
            {
                $i++;
                break;
            }
        }

        // add the children of the current category
        for (; $i < $navCount; $i++)
        {
            $cat = $navArray[$i];
            $path = explode("/", $cat->getNavigationPath());
            if ($path[count($path) - 3] != $currentCat->getId())
            {
                break;
            }

            $node[] = array("cat" => $cat, "children" => array(), 
                "selected" => ($cat->getNavigationCategory() == $currentCat->getId()));
        }

        // add the children of the root category
        for (; $i < $navCount; $i++)
        {
            $cat = $navArray[$i];
            $tree[] = array("cat" => $cat, "children" => array(), 
                "selected" => ($cat->getNavigationCategory() == $currentCat->getId()));
        }

        return $tree;

2 つの回答を受け入れることができれば、最初と最後の回答を受け入れることができます。回答を「興味深い/役立つ」として受け入れることができれば、2 番目の回答を受け入れます。:)

4

3 に答える 3

1

CASE式はトリックを行う必要があります。

SELECT * FROM navigation 
    WHERE navigation_store_id = 32 
        AND (navigation_category IN (4, 32) 
            OR navigation_path LIKE "4/32/33/%/" 
            OR (navigation_path LIKE "4/%/" 
            AND navigation_category <> 32))
    ORDER BY
        CASE
            WHEN navigation_category IN (4, 32) THEN 1
            WHEN navigation_path LIKE "4/32/33/%/" THEN 2
            ELSE 3
        END, navigation_path
于 2009-01-24T03:55:49.593 に答える
1

「重み」のような追加の派生列を試してください。

(未テスト)

(IF(criteriaA,1,0)) + (IF(criteriaB,1,0)) ... AS weight
....
ORDER BY weight 

各基準により、ソートの「重み」が増加します。次のように、IF をネストし、グループに特定の整数を指定して並べ替えることで、重みを明確に設定することもできます。

IF(criteriaA,0, IF(criteriaB,1, IF ... )) AS weight
于 2009-01-24T00:30:07.183 に答える
1

MySQL にはUNIONクエリを結合するための SQL キーワードがありますか? あなたの3つのクエリは主に重複しない基準を持っているので、それらを本質的に別々のクエリのままにしておくのが最善だと思いますが、UNIONor UNION ALL. これにより、2 つの DB ラウンドトリップが節約され、MySQL のクエリ プランナーが各行セットを見つけるための最良の方法を「確認」しやすくなります。

ちなみに、ルートから先端までのパスを格納することでツリーを表す戦略は簡単に実行できますが、フォームの WHERE 句を使用する必要がある場合は常に非効率的ですnavigation_path like '%XYZ'。私が見たすべての DB で、LIKE条件は a で始まる必要がありますその列でインデックスを使用できるようにするための非ワイルドカード。(例のコード スニペットでは、ルート カテゴリが 4 であることをまだ知らなかった場合、そのような句が必要になります (ちなみに、別の以前のクエリからどうやってそれを知ったのですか?))

カテゴリはどのくらいの頻度で変更されますか? それらが頻繁に変更されない場合は、ここで説明されている「ネストされたセット」メソッドを使用してツリーを表すことができます。

于 2009-01-24T03:43:02.257 に答える