ypercube hat das Problem gelöst. Unterabfragen waren völlig unnötig, und das Ganze funktioniert mit einfachen Verknüpfungen. Es ist immer noch seltsam, dass der Optimierer von MySQL meine ursprüngliche Abfrage nicht verwenden konnte. Siehe unten für die Frage und viele Details. Plus eine vollständige Lösung am Ende meiner Frage. Es basiert auf der Antwort von ypercube.
Jede Unterabfrage ist sehr schnell, deutlich unter 1 Sekunde. Die 5-6 Unterabfragen werden zusammengefügt (einige LEFT
, einige INNER
) und die Zeit steigt auf 400 Sekunden.
Die Gesamtabfrage, die ich zum Testen verwende, gibt nur 441 Zeilen zurück.
Ich habe versucht, jede der Unterabfragen in eine "CREATE TABLE" -Abfrage einzufügen. Jeder war in weniger als 1 Sekunde erledigt. Dann habe ich die äußere Abfrage mit diesen neu erstellten Tabellen überarbeitet und sie lief auch in weniger als 1 Sekunde. Es gibt also kein wirkliches Problem mit den Joins. Ich lege Indizes id
für meine erstellten Tabellen an. Alle Tabellen werden beim Matching id
= verknüpft id
.
Wie kann ich MySQL veranlassen, die Abfrage effizient auszuführen? Muss ich temporäre Tabellen verwenden? Ich habe bereits eine Menge PHP-Code geschrieben, um die mehreren Unterabfrage-Joins zusammenzustellen, sodass ich lieber nur herausfinden möchte, wie dies funktioniert, wenn möglich.
Ich habe versucht, das Schlüsselwort "STRAIGHT_JOIN" zu verwenden und das äußere zu entfernen ORDER BY
. Dadurch wurde die Abfragezeit auf 90 Sekunden reduziert. Aber ich sollte maximal 1s bekommen.
Ich habe es STRAIGHT_JOIN
mit versucht ORDER BY
und es dauerte 235 Sekunden. Es scheint also, dass das Äußere ORDER BY
ein großes Leistungsproblem darstellt.
BEARBEITEN:
Getestet mit temporären Tabellen. Die Abfrage läuft sehr schnell. Aber es muss einen Weg geben, MySQL so schnell mit JOINS zu machen.
Das langsame Abfrageprotokoll zeigt außerdem:
Rows_examined: 484006914
484 Millionen Zeilen sehen aus wie ein kartesisches Produkt. Warum werden so viele Zeilen untersucht?
Die Abfrage hat folgende Struktur:
SELECT t0.`id`, t1.`length`, t2.`height`, t3.`family`
FROM
`products` t0
INNER JOIN
(
SELECT t1.`id`, t2.`value` AS `length`
FROM `products` t1
INNER JOIN `product_eav_decimal` t2
ON t1.`id` = t2.`product_id`
WHERE t2.`attribute_id` = 91
AND t2.`value` BETWEEN 15 AND 35
) t1
ON t0.`id` = t1.`id`
LEFT JOIN
(
SELECT t1.`id`, t2.`value` AS `height`
FROM `products` t1
INNER JOIN `product_eav_decimal` t2
ON t1.`id` = t2.`product_id`
WHERE t2.`attribute_id` = 80
# no other conditions
) t2
ON t0.`id` = t2.`id`
INNER JOIN
(
.
.
.
) t6
ON t0.`id` = t6.`id`
ORDER BY t0.`id` ASC
... etc LEFT JOINS werden verwendet, wenn keine anderen Bedingungen in der Unterabfrage als die attribute_id vorhanden sind. INNER JOIN wird verwendet, wenn eine andere Bedingung vorliegt. Dadurch wird ein gültiges Suchergebnis erstellt. Die Abfrage funktioniert, es dauert nur 400 Sekunden anstatt 0,04.
Wenn niemand weiß, wie die JOIN-Syntax funktioniert, verwende ich temporäre Tabellen, da dies zu funktionieren scheint.
TABELLEN:
1.) Produkte
CREATE TABLE `products` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`sku` varchar(127) NOT NULL COMMENT '3char vencode + model',
`model` varchar(127) NOT NULL,
`vendor_id` int(11) DEFAULT NULL,
`updated` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `sku` (`sku`),
KEY `model` (`model`),
KEY `vendor_id` (`vendor_id`),
CONSTRAINT `FK1` FOREIGN KEY (`vendor_id`) REFERENCES `vendors` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=153282 DEFAULT CHARSET=utf8
2.) Dezimalstellen
CREATE TABLE `product_eav_decimal` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`product_id` int(11) NOT NULL,
`attribute_id` int(11) DEFAULT NULL,
`value` decimal(11,3) DEFAULT NULL,
`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `natural_key` (`product_id`,`attribute_id`,`value`),
UNIQUE KEY `product_id_2` (`product_id`,`attribute_id`),
KEY `last_update` (`last_update`),
KEY `product_id` (`product_id`),
KEY `attribute_id` (`attribute_id`),
KEY `value` (`value`),
CONSTRAINT `FK1` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `FK2` FOREIGN KEY (`attribute_id`) REFERENCES `attributes` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=370772 DEFAULT CHARSET=utf8 COLLATE=utf8_bin
3.) varchar (verweist auf eine andere Tabelle, values_varchar
Tabelle für tatsächliche varchar-Werte)
CREATE TABLE `product_eav_varchar` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`product_id` int(11) DEFAULT NULL,
`attribute_id` int(11) DEFAULT NULL,
`value_id` int(11) DEFAULT NULL,
`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `natural_key` (`product_id`,`attribute_id`,`value_id`),
KEY `last_update` (`last_update`),
KEY `product_id` (`product_id`),
KEY `value_id` (`value_id`),
KEY `attribute_id` (`attribute_id`),
CONSTRAINT `FK1` FOREIGN KEY (`value_id`) REFERENCES `values_varchar` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `FK2` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `FK3` FOREIGN KEY (`attribute_id`) REFERENCES `attributes` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=86049 DEFAULT CHARSET=utf8 COLLATE=utf8_bin
Angepasst an die Antwort von ypercube:
SELECT t0.id,
t1.`value` AS length,
t2.`value` AS height,
t3.`value` AS family,
t5.`value` AS type
FROM
products t0
INNER JOIN # INNER used when search criteria
# length (only searched values)
product_eav_decimal t1
ON t1.product_id = t0.id
AND t1.attribute_id = 91
AND t1.`value` BETWEEN 15 AND 35 # search criteria
LEFT JOIN # LEFT used when no search criteria
# height (all, including blank/null)
product_eav_decimal t2
ON t2.product_id = t0.id
AND t2.attribute_id = 80
LEFT JOIN # LEFT - no search critera
# family - varchar type requires extra join to values table
product_eav_varchar t3
ON t3.product_id = t0.id
AND t3.attribute_id = 77
LEFT JOIN # LEFT join to values table matches eav table join
values_varchar t4
ON t3.value_id = t4.id
# search criteria would be here. see next
INNER JOIN # INNER - search criteria below
# type - varchar requires extra join, see below
product_eav_varchar t5
ON t5.product_id = t0.id
AND t5.attribute_id = 76
INNER JOIN # INNER join to values table matches eav table join
values_varchar t6
ON t5.value_id = t6.id
# search criteria
AND (t6.value LIKE "%sofa%" COLLATE utf8_general_ci OR t6.value LIKE "%chair%" COLLATE utf8_general_ci)
ORDER BY t0.id ASC;
Die Abfrage funktioniert. Es läuft in wenigen Millisekunden. Wenn Suchbegriffe oder Bereichsbeschränkungen angegeben werden, werden NUR übereinstimmende Ergebnisse mit INNER JOINs zurückgegeben. Wenn es keine Kriterien gibt, werden LEFT JOINs verwendet, um Werte zurückzugeben (einschließlich NULL / leer).
Update August 2014 - Die products
Tabelle enthält jetzt 400 bis 500.000 Zeilen, und der oben verwendete Abfragestil wird immer noch blitzschnell ausgeführt. Es scheint, dass Joins in MySQL viel schneller sind als Unterabfragen.
quelle
SHOW CREATE TABLE
Ausgabe) für die 2 Tabellen hinzu.Antworten:
Sie benötigen nicht alle abgeleiteten Tabellen. Sie treten
product
zu oft dem basic ( ) bei. Sie können die Abfrage nur einmal schreiben.Zusammengesetzte Indizes sind ein Muss für EAV-Designs. Versuchen Sie, einen Index
(attribute_id, product_id, value)
und dann die Abfrage hinzuzufügen :quelle