代码之家  ›  专栏  ›  技术社区  ›  nlaq

创建SQL查询,按满足的条件对结果排序

  •  0
  • nlaq  · 技术社区  · 15 年前

    这是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中。使用这个新的表,我正在解决另一个问题,我与马根托,但也希望提高我的表现在同一时间。我认为实现这一点的最好方法是将所有三个查询放在一起,通过正确地对结果进行排序,让PHP工作得更少。

    谢谢

    编辑

    好吧,现在效果很好。把它从4秒减到500毫秒。现在速度很快:)

    这是我在大学课堂上的代码:

        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;
    

    如果我能接受两个答案,我会接受第一个和最后一个,如果我能接受一个“有趣/有用”的答案,我会接受第二个。 :)

    3 回复  |  直到 15 年前
        1
  •  2
  •   Jeffrey Hantin    15 年前

    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
  •  2
  •   willoller    15 年前

    尝试其他派生列,如“weight”:

    (未经测试)

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

    每一个标准都会增加该类型的“权重”。 您还可以通过嵌套ifs并给组一个特定的整数来明确设置权重,如下所示:

    IF(criteriaA,0, IF(criteriaB,1, IF ... )) AS weight
    
        3
  •  2
  •   j_random_hacker    15 年前

    MySQL有 UNION 用于组合查询的SQL关键字?您的三个查询主要有不重叠的条件,因此我认为最好将它们保留为基本上独立的查询,但使用 联合 UNION ALL .这将节省2 db的往返行程,并且可能使MySQL的查询规划器更容易“看到”每一组行的最佳查找方式是。

    顺便说一下,通过存储从根到尖端的路径来表示树的策略很容易遵循,但在需要使用表单的WHERE子句时效率相当低 navigation_path like '%XYZ' --在我见过的所有星展上, LIKE 条件必须以非通配符开头,才能对该列启用索引。(在示例代码片段中,如果您还不知道根类别是4(顺便问一下,您是如何知道的),那么就需要这样一个子句。从单独的早期查询中?)

    您的类别多久更改一次?如果它们不经常更改,可以使用“嵌套集”方法表示树,如前所述 here 它可以更快地查询“哪些类别是给定类别的后代/祖先”之类的内容。