Создайте SQL-запрос, упорядочивающий результаты, по каким условиям - PullRequest
0 голосов
/ 24 января 2009

Это для 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-запрос, который с учетом идентификатора магазина 32 и идентификатора категории 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 работать меньше, если результаты отсортированы правильно.

Спасибо

EDIT

Хорошо, теперь это прекрасно работает. Сократите его с 4 секунд до 500 мс. Большая скорость сейчас:)

Вот мой код в классе 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;

Если бы я мог принять два ответа, я бы принял первый и последний, и если бы я мог принять ответ как "интересный / полезный", я бы сделал это со вторым. :)

Ответы [ 3 ]

2 голосов
/ 24 января 2009

Есть ли в MySQL ключевое слово SQL UNION для объединения запросов? Ваши три запроса имеют в основном непересекающиеся критерии, поэтому я подозреваю, что лучше оставить их как отдельные запросы, но объединить их, используя UNION или UNION ALL. Это сэкономит 2 обхода БД и, возможно, упростит для планировщика запросов MySQL «увидеть» лучший способ найти каждый набор строк:

Кстати, ваша стратегия представления дерева путем хранения путей от корня к вершине проста в использовании, но довольно неэффективна, когда вам нужно использовать выражение WHERE вида navigation_path like '%XYZ' - на всех БД, которые я видел , LIKE условия должны начинаться с не подстановочного знака, чтобы разрешить использование индекса для этого столбца. (В вашем примере фрагмента кода вам понадобится такое предложение, если вы еще не знали, что корневой категорией было 4 (Как вы узнали это, кстати? Из отдельного, более раннего запроса?))

Как часто меняются ваши категории? Если они не меняются часто, вы можете представить свое дерево, используя метод «вложенных множеств», описанный здесь , который позволяет намного быстрее выполнять запросы на такие вещи, как «Какие категории являются потомками / предками данной категории» .

2 голосов
/ 24 января 2009

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 голосов
/ 24 января 2009

Попробуйте дополнительный производный столбец, такой как "weight":

(непроверенные)

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

Каждый критерий увеличивает «вес» сорта. Вы также можете установить весовые коэффициенты, вложив IF и присвоив группам определенное целое число для сортировки, например:

IF(criteriaA,0, IF(criteriaB,1, IF ... )) AS weight
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...