Domanda

Questo è per MySQL e PHP

Ho una tabella che contiene le seguenti colonne:

navigation_id (unsigned int primary key)
navigation_category (unsigned int)
navigation_path (varchar (256))
navigation_is_active (bool)
navigation_store_id (unsigned int index)

I dati saranno riempiti come:

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

Quindi questo rappresenterà una struttura ad albero. Il mio obiettivo è scrivere una query SQL che, dato l'ID negozio di 32 e l'ID categoria di 33, restituirà

Innanzitutto, un gruppo di elementi che sono i genitori della categoria 33 (in questo caso 4 e 32)

Quindi, un gruppo di elementi che sono figli della categoria 33 (in questo caso 34, 35 e 36)

Quindi il resto di " root " categorie nella categoria 4 (in questo caso 37).

Quindi la seguente query restituirà i risultati corretti:

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

Il mio problema è che voglio ordinare i " gruppi " delle categorie nell'ordine sopra elencato (genitori di 33 primi, figli di 33 secondi e ultimi del nodo radice). Quindi, se soddisfano la prima condizione, ordinale prima, se soddisfano la seconda condizione, ordinale per seconda e se soddisfano la terza (e quarta) condizione, ordinale per ultime.

Puoi vedere un esempio di come funziona la struttura delle categorie in questo sito:

www.eanacortes.net

Potresti notare che è abbastanza lento. Nel modo in cui lo sto facendo, sto usando la tabella delle categorie originali di magento ed eseguendo tre query particolarmente lente su di essa; quindi mettendo insieme i risultati in PHP. Usando questa nuova tabella sto risolvendo un altro problema che ho con Magento ma vorrei anche migliorare le mie prestazioni allo stesso tempo. Il modo migliore in cui vedo che questo è realizzato è mettere insieme tutte e tre le query e fare in modo che PHP lavori meno avendo i risultati ordinati correttamente.

Grazie

Modifica

Bene, ora funziona benissimo. Riducilo da 4 secondi a 500 MS. Grande velocità ora :)

Ecco il mio codice nella 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;
    }

Quindi lo uso con il seguente codice trovato nel mio blocco 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 potessi accettare due risposte accetterei la prima e l'ultima, e se potessi accettare una risposta come "interessante / utile"; Lo farei con il secondo. :)

È stato utile?

Soluzione

Un'espressione CASE dovrebbe fare il trucco.

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

Altri suggerimenti

Prova un'altra colonna derivata come " peso " ;:

(non testato)

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

Ogni criterio aumenta il "peso" del genere. Puoi anche impostare i pesi distintamente annidando gli IF e dando ai gruppi un numero intero particolare da ordinare in base a:

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

MySQL ha la parola chiave UNION SQL per combinare le query? Le tue tre query hanno criteri principalmente non sovrapposti, quindi sospetto che sia meglio lasciarle come query essenzialmente separate, ma combinarle usando UNION o UNION ALL . Ciò consentirà di risparmiare 2 round trip di DB, e probabilmente rendere più semplice per il pianificatore di query di MySQL "vedere". il modo migliore per trovare ogni set di righe è.

A proposito, la tua strategia di rappresentare l'albero memorizzando i percorsi dalla radice alla punta è facile da seguire ma piuttosto inefficiente ogni volta che devi usare una clausola WHERE del modulo navigation_path come '% XYZ' - su tutti i DB che ho visto, le condizioni LIKE devono iniziare con un carattere non jolly per consentire l'uso di un indice su quella colonna. (Nel tuo frammento di codice di esempio, avresti bisogno di una clausola del genere se non sapessi già che la categoria principale era 4 (Come hai fatto a saperlo? Da una query separata precedente?))

Con quale frequenza cambiano le tue categorie? Se non cambiano spesso, puoi rappresentare il tuo albero usando " set nidificati " metodo, descritto qui , che consente query molto più veloci su cose come " Quali categorie sono discendenti / antenati di una determinata categoria " ;.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top