繁体   English   中英

创建对结果进行排序的SQL查询

[英]Create SQL query that orders results by which condition they meet

这适用于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;
    }

然后,使用在我的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;

如果我可以接受两个答案,那么我将接受第一个和最后一个答案;如果我可以接受一个答案“有趣/有用”,那么我将接受第二个答案。 :)

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

(未试)

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

每个标准都会增加排序的“权重”。 您还可以通过嵌套IF并为组指定特定的整数来按如下方式分别设置权重:

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

MySQL是否具有UNION SQL关键字来组合查询? 您的三个查询主要具有不重叠的条件,因此我怀疑最好将它们保留为本质上独立的查询,而应使用UNIONUNION ALL 这将节省2次DB往返,并且可能使MySQL的查询计划程序更轻松地“查找”查找每组行的最佳方法。

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM