Eine Möglichkeit zu versuchen, ist die Verwendung einer Verschlusstabelle.
CREATE TABLE category_closure (
`a_catagory_id` SMALLINT UNSIGNED NOT NULL,
`d_category_id` SMALLINT UNSIGNED NOT NULL,
PRIMARY KEY (`a_category_id`,`d_category_id`)
) ENGINE=InnoDB
Diese Tabelle enthält die Beziehungen zwischen den verschiedenen Ebenen der Kategorien in der Hierarchie. Die a_category_id
bezieht sich auf den Vorfahren in einer Beziehung, während sich die d_category_id
auf den Nachkommen bezieht.
Damit die Tabelle ordnungsgemäß funktioniert, muss jede Kategorie als eigener Vor- und Nachfahre in die Tabelle eingegeben werden.
INSERT INTO category_closure
(a_category_id, d_category_id)
SELECT
id_category,
id_category
FROM ps_category
Dann können Sie Ihre bekannten Beziehungen von der id_parent
Spalte auf jeder Kategorie ein.
INSERT INTO category_closure
(a_category_id, d_category_id)
SELECT
id_parent,
id_category
FROM ps_category
WHERE id_parent IS NOT NULL
Schließlich müssen Sie die Punkte verbinden. Führen Sie das folgende SELECT in einer do..while
-Schleife aus, und so lange wie Zeilen von ihm zurückgegeben werden, fügen Sie diese Zeilen in der Verschlusstabelle ein, und fahren Sie mit der Schleife fort.
SELECT
cc1.a_category_id,
cc2.d_category_id
FROM category_closure cc1
INNER JOIN category_closure cc2
ON cc2.a_category_id = cc1.d_category_id
LEFT OUTER JOIN category_closure missing_cc
ON missing_cc.a_category_id = cc1.a_category_id
AND missing_cc.d_category_id = cc2.d_category_id
WHERE missing_cc.a_category_id IS NULL
Mit dieser Abfrage werden alle vorhandenen Beziehungen abgefragt und gefunden, was auch vorhanden sein soll. Zum Beispiel haben Sie die folgende Kette:
53> 67> 50
Das bedeutet, dass Sie die folgenden Datensätze haben, aus den ersten beiden INSERTs: (50,50) (67,67) (53,53) (50,67) (67,53) (und andere).
Nun, was wir brauchen, ist (50,53), da 53 ein Nachkomme von 50 ist. In der obigen SELECT-Abfrage entspricht cc1
dem (50,67) Datensatz. cc2
entspricht dem Datensatz (67,53). Das bedeutet, missing_cc
versucht, mit 50 aus cc1
(a_category_id
) und 53 aus cc2
(d_category_id
) zusammen zu finden.
Da anfänglich kein solcher Datensatz existiert, gibt die SELECT-Anweisung diese zwei Zeilen zurück, Sie können sie einfügen und wiederholen. Diesmal weiter in der Kette.Sie brauchen nicht (oder Ihr Programm) zu wissen, wie viele Ebenen es gibt, fahren Sie einfach fort, bis das SELECT keine Ergebnisse mehr findet.
Schließlich, sobald Ihre Schließung Tisch aufgebaut ist, können Sie die entsprechenden Informationen SELECT:
SELECT
p.id_product,
c.id_category,
GROUP_CONCAT(cc.a_category_id) AS parent_category_ids
FROM ps_category c
LEFT OUTER JOIN ps_product p
ON p.id_category_default = c.id_category
LEFT OUTER JOIN category_closure cc
ON cc.d_category_id = c.id_category
AND cc.a_category_id != c.id_category
GROUP BY c.id_category, p.id_product
Dadurch werden alle Kategorien auswählen, werden alle Produkte in jeder Kategorie, und dann eine durch Kommata getrennte Liste der Vorfahren Kategorien liefern für jede dieser Kombinationen.
Nun, das ist tatsächlich ziemlich viel wiederholte Informationen, da Sie die Kategorien und ihre Vorfahren getrennt von den Produkten ausführen können, aber es läuft wirklich darauf hinaus, wie Sie diese Daten verwenden möchten, ob Sie trennen können die Abfragen oder nicht.
Hinweis: Wenn Sie Kategorien hinzufügen und entfernen, müssen Sie diesen Vorgang jedes Mal wiederholen, nachdem Sie alle aktuellen Abschlussdaten gelöscht haben. Es gibt bessere Möglichkeiten, dies zu tun, als hier gezeigt, und auf diese Weise können Sie sogar die Verwendung der Spalte id_parent
löschen (insbesondere wenn die Hierarchie nicht zyklisch ist), aber solche Dinge liegen außerhalb des Bereichs dieser Frage.
Diese Antwort sollte zumindest in der Lage sein, Sie etwas ausprobieren zu lassen, ohne Ihre Anwendung oder vorhandene Daten ändern zu müssen.
0,8 Mikrosekunde ist sehr schnell. Warum suchen Sie nach einer optimierten Lösung? – Shadow
@Shadow, änderte die Statistik –
Ihre Abfrage ist in Ordnung für was Sie wollen und kann kaum besser optimiert werden. Abhängig von Ihrer Anwendung (wie oft neue Produkte zur Datenbank hinzugefügt werden) können Sie eine MATERIALIZED VIEW einrichten. Das Konzept existiert nicht in MySQL, aber Sie können es "nachahmen": Überprüfen Sie diese http://www.fromdual.com/mysql-materialized-views –