충족하는 조건으로 결과를 주문하는 SQL 쿼리 작성
문제
이것은 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라는 것을 아직 알지 못했다면 그러한 조항이 필요할 것입니다 (그건 그렇고 어떻게 그것을 알았습니까? 별도의 이전 쿼리에서))))))))))
카테고리가 얼마나 자주 변경됩니까? 자주 변하지 않으면 "중첩 된 세트"방법을 사용하여 트리를 나타낼 수 있습니다. 여기, "주어진 범주의 후손/조상"과 같은 것들에 대한 쿼리가 훨씬 빠르게 가능합니다.