这适用于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
所以这将代表树状结构。我的目标是编写一个SQL查询,给定商店ID为32,类别ID为33,将返回
首先,一组元素是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秒的子节点,最后是根节点的父节点)。因此,如果他们满足第一个条件,首先订购,如果他们满足第二个条件,则命令他们第二个,如果他们满足第三个(和第四个)条件,则命令他们最后。
您可以在此网站上看到类别结构如何运作的示例:
www.eanacortes.net
你可能会注意到它很慢。我目前正在这样做的方式我正在使用magento的原始类别表并对其执行三个特别慢的查询;然后将结果放在PHP中。使用这个新表我正在解决另一个与magento有关的问题,但同时也希望改善我的表现。我认为实现这一目标的最好方法是将所有三个查询放在一起,并通过正确排序结果来减少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;
}
然后我使用在我的类别块中找到的以下代码来使用它:
// 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;
如果我能接受两个答案,我会接受第一个和最后一个,如果我能接受一个“有趣/有用”的答案,我会用第二个答案。 :)
答案 0 :(得分:2)
尝试额外的派生列,例如“weight”:
(适用未经测试)强>
(IF(criteriaA,1,0)) + (IF(criteriaB,1,0)) ... AS weight
....
ORDER BY weight
每个标准都会增加排序的“重量”。 您还可以通过嵌套IF并为组分组特定整数来明确设置权重:
IF(criteriaA,0, IF(criteriaB,1, IF ... )) AS weight
答案 1 :(得分:2)
MySQL是否具有用于组合查询的UNION
SQL关键字?您的三个查询主要是非重叠条件,因此我怀疑最好将它们保留为基本上单独的查询,但使用UNION
或UNION ALL
进行组合。这将节省2次DB往返,并且可能使MySQL的查询规划器更容易“看到”查找每组行的最佳方法。
顺便说一下,通过存储从根到tip的路径来表示树的策略很容易遵循,但是当你需要在所有DB上使用navigation_path like '%XYZ'
形式的WHERE子句时,它的效率很低。我们看到,LIKE
条件必须以非通配符开头才能在该列上使用索引。 (在你的示例代码片段中,如果你还不知道root类别是4,那么你需要这样一个子句(顺便提一下,你是怎么知道的?来自一个单独的早期查询?)
您的类别多久更改一次?如果它们不经常更改,您可以使用“嵌套集”方法表示您的树,描述here,这样可以更快地查询诸如“哪些类别是给定类别的后代/祖先”。< / p>
答案 2 :(得分: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