Créer une requête SQL ordonnant les résultats en fonction de la condition rencontrée
Question
Ceci est pour MySQL et PHP
J'ai une table contenant les colonnes suivantes:
navigation_id (unsigned int primary key)
navigation_category (unsigned int)
navigation_path (varchar (256))
navigation_is_active (bool)
navigation_store_id (unsigned int index)
Les données seront remplies comme suit:
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
Cela représentera donc une structure semblable à un arbre. Mon but est d'écrire une requête SQL qui, étant donné l'ID de magasin de 32 et l'ID de catégorie de 33, renverra
Tout d’abord, un groupe d’éléments qui sont les parents de la catégorie 33 (en l’occurrence 4 et 32)
Ensuite, un groupe d'éléments qui est un enfant de la catégorie 33 (dans ce cas, 34, 35 et 36)
Ensuite, le reste de la "racine". catégories de la catégorie 4 (dans ce cas 37).
La requête suivante renvoie donc les résultats corrects:
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))
Mon problème est que je souhaite commander les "groupes". des catégories dans l’ordre indiqué ci-dessus (parents de 33 ans en premier, enfants de 33 ans en second et parents du noeud racine en dernier). Donc, s'ils remplissent la première condition, commandez-les d'abord, s'ils répondent à la deuxième condition, commandez-les ensuite et s'ils répondent à la troisième (et quatrième) condition, commandez-les en dernier.
Vous pouvez voir un exemple du fonctionnement de la structure de catégories sur ce site:
www.eanacortes.net
Vous remarquerez peut-être que c'est assez lent. Actuellement, j'utilise la table des catégories d'origine de Magento et j'exécute trois requêtes particulièrement lentes. puis rassembler les résultats en PHP. En utilisant cette nouvelle table, je résous un autre problème que j'ai avec magento, mais j'aimerais également améliorer mes performances en même temps. Pour ce faire, la meilleure solution consiste à regrouper les trois requêtes et à réduire le travail de PHP en veillant à ce que les résultats soient correctement triés.
Merci
MODIFIER
D'accord, cela fonctionne très bien maintenant. Réduisez-le de 4 secondes à 500 MS. Grande vitesse maintenant:)
Voici mon code dans la 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;
}
Ensuite, je le consomme avec le code suivant trouvé dans mon bloc de catégories:
// 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;
Si je pouvais accepter deux réponses, j'accepterais la première et la dernière et si je pouvais accepter une réponse comme "intéressante / utile" Je ferais ça avec le second. :)
La solution
Une expression CASE
devrait faire l'affaire.
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
Autres conseils
Essayez une colonne dérivée supplémentaire telle que "poids":
(non testé)
(IF(criteriaA,1,0)) + (IF(criteriaB,1,0)) ... AS weight
....
ORDER BY weight
Chaque critère augmente le " poids " de la sorte. Vous pouvez également définir les pondérations de manière distincte en imbriquant des FI et en donnant aux groupes un entier particulier à trier comme:
IF(criteriaA,0, IF(criteriaB,1, IF ... )) AS weight
MySQL utilise-t-il le mot clé SQL UNION
pour combiner des requêtes? Vos trois requêtes ont principalement des critères qui ne se chevauchent pas, alors je suppose qu'il est préférable de les laisser comme des requêtes essentiellement séparées, mais combinez-les en utilisant UNION
ou UNION ALL
. Cela économisera 2 allers-retours à la base de données et facilitera probablement le planificateur de requêtes de MySQL à la "voir". la meilleure façon de trouver chaque ensemble de lignes est.
Soit dit en passant, votre stratégie consistant à représenter l’arborescence en stockant les chemins de la racine au sommet est facile à suivre, mais plutôt inefficace chaque fois que vous devez utiliser une clause WHERE de la forme chemin de navigation comme '% XYZ'
- Sur toutes les bases de données que j'ai vues, les conditions LIKE
doivent commencer par un caractère non générique pour permettre l'utilisation d'un index sur cette colonne. (Dans votre exemple d'extrait de code, vous auriez besoin d'une telle clause si vous ne saviez pas déjà que la catégorie racine était 4 (Comment le saviez-vous en passant? A partir d'une requête séparée et antérieure?)
À quelle fréquence vos catégories changent-elles? S'ils ne changent pas souvent, vous pouvez représenter votre arbre à l'aide des "ensembles imbriqués". méthode, décrite ici , ce qui permet d'effectuer des requêtes beaucoup plus rapidement, par exemple, " quelles sont les catégories descendants / ancêtres d'une catégorie donnée ".