Ho trovato un modo un po 'migliore per farlo, usando un anti-join (Magento 1.9).
Vantaggi di questo approccio
Il vantaggio di questo rispetto alla risposta originale è che non otterrai falsi positivi, e di conseguenza è più veloce e meno soggetto a errori. Ad esempio, supponiamo di avere un singolo prodotto:
- Camicia (nelle categorie: 1 | 2)
Vuoi "trovare tutti i prodotti non presenti category 3
, quindi aggiungerli a category 3
" . Quindi esegui una NOT IN
query e restituirà due righe (name | category_id)
:
1. "Shirt" | 1
2. "Shirt" | 2
Nessun problema, Magento restituirà comunque solo il primo risultato, e poi lo aggiungerai. Tranne ! La seconda volta che viene eseguita questa query, si ottengono gli stessi risultati:
1. "Shirt" | 1
2. "Shirt" | 2
E Magento ti dirà che non hai ancora aggiunto questa maglietta a category 3
. Questo perché quando un prodotto appartiene a più categorie, avrà più righe nella tabella "catalog_product_entity" . E così a LEFT JOIN
restituirà più risultati.
Questo è indesiderabile perché
- Il set di risultati sarà più grande del necessario, il che utilizzerà più memoria del necessario. Soprattutto se si dispone di un inventario molto ampio di migliaia di articoli.
- Dovrai effettuare un controllo aggiuntivo in PHP per determinare se i risultati sono falsi positivi (ad esempio,
in_array($categoryThree, $product->getCategories())
), il che significa che eseguirai il ciclo di risultati non necessari. Questo renderà il tuo script / codice più lento, specialmente con grandi inventari.
Soluzione
// All products not in this category ID
$notInThisCatId = '123';
$filteredProducts = Mage::getModel('catalog/product')->getCollection();
$filteredProducts
->joinField('category_id', 'catalog_category_product', 'category_id', 'product_id=entity_id', ['category_id'=>$notInThisCatId], 'left');
$filteredProducts
->addAttributeToFilter('category_id', [
['null' => true]
]);
La query SQL generata sarà simile a:
SELECT
DISTINCT `e`.*, `at_category_id`.`category_id`
FROM `catalog_product_entity` AS `e`
LEFT JOIN `catalog_category_product` AS `at_category_id`
ON (at_category_id.`product_id`=e.entity_id) AND (at_category_id.category_id = '123')
WHERE (at_category_id.category_id IS NULL)
GROUP BY `e`.`entity_id`;
Spiegazione:
Dati il prodotto e le tabelle di relazione categoria <=> prodotto:
catalog_product_entity
+-----------+
| ENTITY_ID |
+-----------+
| 423 |
| 424 |
| 425 |
+-----------+
catalog_category_product
+-------------+------------+
| CATEGORY_ID | PRODUCT_ID |
+-------------+------------+
| 3 | 423 |
| 123 | 424 |
| 3 | 425 |
+-------------+------------+
La tua query sta dicendo "dammi tutte le righe in " catalog_product_entity " e incolla la colonna" category_id "da " catalog_category_product " . Quindi dammi solo le righe che category_id = 124" .
Poiché è un join sinistro, avrà sempre le righe da "catalog_product_entity" . Per tutte le righe che non possono essere abbinate, sarà NULL
:
Risultato
+-------------+-------------+
| ENTITY_ID | CATEGORY_ID |
+-------------+-------------+
| 423 | NULL |
| 424 | 123 |
| 425 | NULL |
+-------------+-------------+
Da lì, la query quindi dice "ok, ora dammi tutto dove il category_id è NULL" .