Criar consulta SQL que classifica os resultados pelo qual condição se encontram
Pergunta
Isto é para MySQL e PHP
Eu tenho uma tabela que contém as seguintes colunas:
navigation_id (unsigned int primary key)
navigation_category (unsigned int)
navigation_path (varchar (256))
navigation_is_active (bool)
navigation_store_id (unsigned int index)
Os dados serão preenchidos como:
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
Então, isso vai representar uma árvore como estrutura. Meu objetivo é escrever uma consulta SQL que, dada a ID de armazenamento de 32 e ID da categoria de 33, vai voltar
Primeiro, um grupo de elementos que são os pais da categoria 33 (neste caso 4 e 32)
Em seguida, um grupo de elementos que são uma criança de categoria 33 (neste caso 34, 35, e 36)
Em seguida, o resto das categorias "raiz" na categoria 4 (neste caso 37).
Assim, a seguinte consulta retornará os resultados corretos:
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))
O meu problema é que eu quiser encomendar os "grupos" de categorias na ordem listada acima (pais de 33 primeiramente, crianças de 33 segundo, e os pais do nó raiz passado). Então, se eles preenchem a primeira condição, encomendá-los em primeiro lugar, se cumprirem a segunda ordem condição los segundo e se cumprirem o terceiro (e quarto) ordem condição deles passado.
Você pode ver um exemplo de como a estrutura de categoria trabalha neste site:
www.eanacortes.net
Você pode notar que é bastante lento. A forma atual que estou fazendo isso eu estou usando a tabela categoria original do magento e execução de três consultas particularmente lenta em que; em seguida, colocar os resultados juntos em PHP. Usando esta nova tabela estou resolvendo outro problema que eu tenho com o Magento, mas também gostaria de melhorar o meu desempenho, ao mesmo tempo. A melhor maneira que eu vejo esta sendo realizado é colocar todas as três consultas junto e que têm trabalho PHP menos por ter os resultados classificadas corretamente.
Graças
Editar
Tudo bem, ele funciona muito bem agora. Cortá-la a partir de 4 segundos para baixo para 500 ms. Grande velocidade agora:)
Aqui está o meu código na classe 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;
}
Então eu consumi-lo com o seguinte código encontrado no meu bloco Categoria:
// 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;
Se eu pudesse aceitar duas respostas eu aceitaria a primeira e última, e se eu poderia aceitar uma resposta como "interessante / útil" Eu faria isso com o segundo. :)
Solução
A expressão CASE
deve fazer o truque.
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
Outras dicas
Tente uma coluna derivada adicional como "peso":
(não testado)
(IF(criteriaA,1,0)) + (IF(criteriaB,1,0)) ... AS weight
....
ORDER BY weight
Cada critério aumenta o "peso" do tipo. Você também pode definir os pesos distintamente pelos FI nidificação e dando os grupos um inteiro particular para ordenar por como:
IF(criteriaA,0, IF(criteriaB,1, IF ... )) AS weight
O MySQL tem a palavra-chave SQL UNION
para combinar consultas? Seus três consultas têm principalmente critérios não sobrepostas, então eu suspeito que é melhor deixá-los como consultas essencialmente separados, mas combiná-los usando UNION
ou UNION ALL
. Isto vai poupar 2 dB round-trips, e possivelmente torná-lo mais fácil para planejador de consultas do MySQL para "ver" a melhor maneira de encontrar cada conjunto de linhas é.
A propósito, a sua estratégia de representar a árvore armazenando caminhos da raiz à ponta é fácil de seguir, mas bastante ineficiente sempre que você precisa usar uma cláusula WHERE da navigation_path like '%XYZ'
forma - em todos os bancos de dados que eu vi, LIKE
condições devem começar com um não-universal para permitir a utilização de um índice em que coluna. (No seu exemplo trecho de código, você precisaria de uma cláusula desse tipo, se você já não soubesse que a categoria raiz foi de 4 (Como você sabia que pelo caminho? De uma consulta separada, mais cedo?))
Como costumam fazer sua mudança de categorias? Se eles não mudam com frequência, pode representar sua árvore usando o método de "conjuntos aninhados", descreveu aqui , que permite muito mais rápida consulta em coisas como "que categorias são descendentes / ancestrais de uma determinada categoria".