条件を満たす条件で結果を並べるSQLクエリを作成する
質問
これは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
これは、構造のようなツリーを表します。私の目標は、32のストアIDと33のカテゴリIDが返されるSQLクエリを記述することです
まず、カテゴリ33(この場合は4と32)の親である要素のグループ
次に、カテゴリ33(この場合は34、35、および36)の子である要素のグループ
次に、残りの<!> quot; root <!> quot;カテゴリ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))
私の問題は、<!> quot; groups <!> quot;を注文したいことです。上記のカテゴリのカテゴリ(最初の33の親、33秒の子、およびルートノードの親)。したがって、最初の条件を満たす場合は最初に、2番目の条件を満たす場合は2番目、3番目(および4番目)の条件を満たす場合は最後に注文します。
このサイトでカテゴリ構造がどのように機能するかの例を見ることができます:
www.eanacortes.net
かなり遅いことに気づくかもしれません。これを行う現在の方法では、magentoの元のカテゴリテーブルを使用し、3つの特に遅いクエリを実行しています。次に、PHPで結果をまとめます。この新しいテーブルを使用して、magentoで抱えている別の問題を解決していますが、同時にパフォーマンスも改善したいと考えています。これを達成するための最良の方法は、3つのクエリをすべて組み合わせ、結果を適切にソートすることでPHPの動作を抑えることです。
ありがとう
編集
さて、今はうまく機能します。 4秒から500 MSまで削減します。素晴らしい速度になりました:)
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つの回答を受け入れることができれば、最初と最後の回答を受け入れ、<!> quot; interesting / useful <!> quot;として回答を受け入れることができます。私は2番目でそれをします。 :)
解決
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
他のヒント
<!> quot; weight <!> quot;のような追加の派生列を試してください:
(未テスト)
(IF(criteriaA,1,0)) + (IF(criteriaB,1,0)) ... AS weight
....
ORDER BY weight
各基準により、<!> quot; weight <!> quot;が増加します。ソートの。 また、IFをネストし、グループに特定の整数を与えてソートすることにより、重みを明確に設定することもできます。
IF(criteriaA,0, IF(criteriaB,1, IF ... )) AS weight
MySQLにはクエリを結合するためのUNION
SQLキーワードがありますか? 3つのクエリには主に重複しない基準があるため、本質的に別個のクエリのままにして、UNION ALL
またはnavigation_path like '%XYZ'
を使用して結合することをお勧めします。これにより、2回のDBラウンドトリップが節約され、MySQLのクエリプランナーが<!> quot; see <!> quot;行の各セットを見つける最良の方法です。
ところで、ルートからチップまでのパスを保存することでツリーを表現する戦略は従うのは簡単ですが、私が見たすべてのDBでLIKE
の形式のWHERE句を使用する必要がある場合は非効率的です、<=>条件は、その列でインデックスの使用を有効にするために、ワイルドカード以外で始まる必要があります。 (サンプルコードスニペットでは、ルートカテゴリが4であることをまだ知らなかった場合、このような句が必要になります(どうしてそれを知ったのですか?別の以前のクエリから?)
カテゴリはどのくらいの頻度で変更されますか?頻繁に変更されない場合は、<!> quot;ネストセット<!> quot;を使用してツリーを表すことができます。 こちらで説明されているメソッド。<!> quot;指定されたカテゴリの子孫/祖先とはどのようなカテゴリ<!> quot;。