문제

이것은 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

따라서 이것은 구조와 같은 나무를 나타냅니다. 내 목표는 32의 스토어 ID와 33의 카테고리 ID를 감안할 때 SQL 쿼리를 작성하는 것입니다.

첫째, 카테고리 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 초에서 500ms까지 줄입니다. 지금은 빠른 속도 :)

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;

두 가지 답변을 받아 들일 수 있다면 첫 번째와 마지막 답변을 받아 들일 것입니다. "흥미로운/유용한"대답을 받아 들일 수 있다면 두 번째로 그렇게 할 것입니다. :)

도움이 되었습니까?

해결책

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

다른 팁

"Weight"와 같은 추가 파생 열을 사용해보십시오.

(테스트되지 않은)

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

각 기준은 종류의 "무게"를 증가시킵니다. IFS를 중첩하고 그룹에 특정 정수를 다음과 같이 분류 할 수있게하여 가중치를 뚜렷하게 설정할 수 있습니다.

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

MySQL은 다음과 같습니다 UNION 쿼리 결합을위한 SQL 키워드? 세 쿼리에는 주로 겹치지 않는 기준이 있으므로 본질적으로 별도의 쿼리로 남겨 두는 것이 가장 좋지만 사용을 결합하는 것이 가장 좋습니다. UNION 또는 UNION ALL. 이렇게하면 2dB 왕복 트립을 절약 할 수 있으며 MySQL의 쿼리 플래너가 각 행 세트를 찾는 가장 좋은 방법을 "보기"할 수 있습니다.

그건 그렇고, 루트에서 팁으로 경로를 저장하여 트리를 표현하는 전략은 따라 가기 쉽지만 양식의 where 절을 사용할 때마다 비효율적입니다. navigation_path like '%XYZ' - 내가 본 모든 DBS에서 LIKE 해당 열에서 인덱스를 사용할 수 있도록 조건은 비장으로 시작해야합니다. (예제 코드 스 니펫에서 루트 카테고리가 4라는 것을 아직 알지 못했다면 그러한 조항이 필요할 것입니다 (그건 그렇고 어떻게 그것을 알았습니까? 별도의 이전 쿼리에서))))))))))

카테고리가 얼마나 자주 변경됩니까? 자주 변하지 않으면 "중첩 된 세트"방법을 사용하여 트리를 나타낼 수 있습니다. 여기, "주어진 범주의 후손/조상"과 같은 것들에 대한 쿼리가 훨씬 빠르게 가능합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top