Créer une requête SQL ordonnant les résultats en fonction de la condition rencontrée

StackOverflow https://stackoverflow.com/questions/475178

  •  19-08-2019
  •  | 
  •  

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. :)

Était-ce utile?

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 ".

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top