2009-01-24 86 views
0

这是MySQL和PHP创建SQL查询,订单结果通过何种条件下它们满足

我有一个包含以下各列的表格:

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的原始类别表并在其上执行三个特别慢的查询;然后将结果放在一起。使用这张新表格,我正在解决另一个与magento有关的问题,但同时也想提高我的表现。我看到这一切的最佳方式是将所有三个查询放在一起,并通过让结果正确排序来减少PHP的工作量。

感谢

编辑

好了,现在它的伟大工程。将其从4秒降至500毫秒。现在大提速:)

这是我在Colleciton类代码:

 // 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; 

如果我能接受:

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; 
    } 

然后,我用我的分类块中发现下面的代码使用它两个答案,我会接受第一个和最后一个,如果我能接受一个答案为“有趣/有用”,我会在第二个答案。 :)

+0

您选择可能将不得不加入本身,或者写一个脚本来将数据库转换为一些关系 – Shawn 2009-01-24 00:30:42

+0

这听起来像你选择了错误的逻辑去摆在首位这一点。三个缓慢的查询,然后与PHP合并?你需要重写那部分。 – dkretz 2009-01-24 03:57:24

+0

这听起来像你选择了错误的逻辑来首先得到这一点。三个缓慢的查询,然后与PHP合并?你需要重写那部分。谁是真正的magento给了你不理想的建议? – dkretz 2009-01-24 03:58:40

回答

2

A 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 
2

尝试一个附加派生列像 “重量”:

(未测试的)

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

每个标准增加了排序的 “权重”。

IF(criteriaA,0, IF(criteriaB,1, IF ...)) AS weight 
2

的MySQL是否有合并查询UNION SQL关键字: 您也可以通过嵌套的IF,并给予组特定的整数像排序设定的权重明显?您的三个查询主要有不重叠的标准,因此我认为最好将它们作为基本上单独的查询,但使用UNIONUNION ALL进行组合。这将节省2个DB往返次数,并且可能使MySQL的查询计划器更容易“查看”查找每组行的最佳方式。

顺便说一句,你从根存储路径给小费表示树的策略是容易执行,而是效率低下,当您需要使用表单navigation_path like '%XYZ'的WHERE子句 - 所有的DB我见过, LIKE条件必须以非通配符开头才能使用该列上的索引。 (在您的示例代码片段中,如果您还不知道根类别是4(您是如何知道这一点的?从单独的早期查询中得知),则需要这样的子句)

经常做你的类别更改?如果它们不经常更改,则可以使用描述为here的“嵌套集”方法来表示您的树,这样可以更快地查询诸如“哪些类别是给定类别的后代/祖先”之类的东西。