it-swarm.com.de

Verbessern der Abfrageleistung mit vielen JOINs

Ich habe eine Abfrage (mit dem Zweck, eine Ansicht zu erstellen), die einige Verknüpfungen verwendet, um jede Spalte abzurufen. Die Leistung verschlechtert sich schnell (exponentiell?) Für jeden hinzugefügten Satz von Joins.

Was wäre ein guter Ansatz, um diese Abfrage zu beschleunigen? Bitte beachten Sie die Kommentare in der Anfrage.

Wenn es hilft, verwendet dies das WordPress DB-Schema.

Hier ist ein Screenshot von EXPLAIN enter image description here

PRODUKTTABELLE

+--+----+
|id|name|
+--+----+
|1 |test|
+--+----+

METADATENTABELLE

+----------+--------+-----+
|product_id|meta_key|value|
+----------+--------+-----+
|1         |price   |9.99 |
+----------+--------+-----+
|1         |sku     |ABC  |
+----------+--------+-----+

TERM_RELATIONSHIPS-TABELLE

+---------+----------------+
|object_id|term_taxonomy_id|
+---------+----------------+
|1        |1               |
+---------+----------------+
|1        |2               |
+---------+----------------+

TERM_TAXONOMY TABLE

+----------------+-------+--------+
|term_taxonomy_id|term_id|taxonomy|
+----------------+-------+--------+
|1               |1      |size    |
+----------------+-------+--------+
|2               |2      |stock   |
+----------------+-------+--------+

BEDINGUNGEN TABELLE

+-------+-----+
|term_id|name |
+-------+-----+
|1      |500mg|
+-------+-----+
|2      |10   |
+-------+-----+

ABFRAGE

SELECT 
  products.id,
  products.name,
  price.value AS price,
  sku.value AS sku,
  size.name AS size
FROM products

/* These joins are performing quickly */

INNER JOIN `metadata` AS price ON products.id = price.product_id AND price.meta_key = 'price'
INNER JOIN `metadata` AS sku ON products.id = sku.product_id AND sku.meta_key = 'sku'

/* Here's the part that is really slowing it down - I run this chunk about 5 times with different strings to match */

INNER JOIN `term_relationships` AS tr ON products.id = tr.object_id
  INNER JOIN `term_taxonomy` AS tt
  ON tr.term_taxonomy_id = tt.term_taxonomy_id AND tt.taxonomy = 'size'
    INNER JOIN `terms` AS size
    ON tt.term_id = size.term_id
17
dloewen

Ihr Leistungsproblem wird höchstwahrscheinlich durch den Join mit der Tabelle 'term_taxonomy' verursacht.
Alle anderen Joins verwenden anscheinend den Primärschlüssel (bei dem Sie wahrscheinlich über Arbeitsindizes verfügen).

Also ist mein Vorschlag, einen zusammengesetzten Index für term_taxonomy_id und term_id hinzuzufügen (oder wenn Sie müssen: taxonomy ). So was:

CREATE UNIQUE INDEX idx_term_taxonomy_id_taxonomy
ON term_taxonomy( term_taxonomy_id, taxonomy);

Hoffe das wird dir helfen.

10
carleson

Stellen Sie sicher, dass alle Spalten, für die es bedingte "ON" -Anweisungen gibt, indiziert werden sollten. Dies wird die Geschwindigkeit erheblich verbessern.

2
akkig

Ich würde diese vorschlagen:

  • Ziehen Sie in Betracht, diese Verknüpfungen von der Unternehmensebene aus zu reduzieren.
  • Wenn dies nicht von "oben" (Unternehmensebene) aus möglich ist und die Daten nicht in Echtzeit vorliegen, würde ich vorschlagen, eine Speichertabelle vorzubereiten (ich weiß, dass die Lösung nicht ideal ist). Und wählen Sie Ihre Daten direkt aus der Speichertabelle.

Durch meine Erfahrung:

  • "Joins" ist der Killer für Leistung. Je größer Ihre Daten sind, desto mehr Schmerzen werden Sie spüren.
  • Versuchen Sie, Verknüpfungen zu entfernen. Versuchen Sie nicht, die Abfrageleistung zu verbessern, indem Sie Verknüpfungen beibehalten, es sei denn, Sie müssen dies tun. Normalerweise werde ich versuchen, diese Probleme von "oben" nach "unten" zu beheben
  • Der letzte Vorschlag ist, wenn alle oben genannten nicht funktionieren. Ich werde "Karte/Verkleinern + Volltextsuche" in Betracht ziehen, wenn sich das lohnt.

(Verzeihen Sie, ich habe keine Lösung zur Verbesserung Ihrer Abfrageleistung bereitgestellt.)

0
Joshua

METADATA_TABLE und TERM_RELATIONSHIP_TABLE haben keinen Hauptschlüssel. Wenn diese Tabellen große Datensätze enthalten, wird Ihre Abfrageleistung beeinträchtigt.

Checkpoints zur Leistungssteigerung.

  1. Alle Tabellen sollten einen Primärschlüssel haben. Dies liegt daran, dass die Zeilen in der Tabelle physisch sortiert werden.
  2. Für kleine und Abfragen mit wenigen Tabellen wäre es ausreichend, den Primärschlüssel in der Tabelle zu belassen. Wenn Sie die Leistung noch verbessern möchten, erstellen Sie einen Nicht-Clustered-Index für Spalten wie * object_Id field of term_relationships table *. Für die Spalten in der Tabelle, die am Join-Vorgang teilnehmen, sollte ein nicht gruppierter Index erstellt werden.

Es ist jedoch zu beachten, dass ein nicht gruppierter Index in den Tabellen, in denen mehrere Einfügungen und Aktualisierungen stattfinden, sehr viel geringer sein sollte. Dies ist keine einfache Frage und kann nicht nur anhand der Laufzeit beantwortet werden. Es gibt andere Faktoren, die die Antwort beeinflussen, insbesondere wenn eine Umgebung, in der eine gespeicherte Prozedur ausgeführt wird, stark transaktionell ist.

Sie finden mehr hier

0
gokul
    Declare @query as NVARCHAR(MAX)
    set @query = ('SELECT 
    products.id,
    products.name,
    price.value AS price,
    sku.value AS sku,
    size.name AS size
    FROM products
    INNER JOIN metadata AS price ON products.id = price.product_id AND price.meta_key = price
    INNER JOIN metadata AS sku ON products.id = sku.product_id AND sku.meta_key = sku
    INNER JOIN term_relationships AS tr ON products.id = tr.object_id
    INNER JOIN term_taxonomy AS tt
    ON tr.term_taxonomy_id = tt.term_taxonomy_id AND tt.taxonomy = size
    INNER JOIN terms AS size
    ON tt.term_id = size.term_id
    into #t')

    exec(@query);
    select * from #t

Ich hoffe, dass der oben beschriebene Weg die Zeitauslastung reduziert oder das Erstellen einer temporären Tabelle mit allen von Ihnen ausgewählten Feldern und das Aktualisieren der temporären Tabelle durch Verbinden der temporären Tabelle mit allen anderen Tabellen auch effektiv sein kann. Ich bin mir nicht sicher Aber auch ich warte auf Ihr Ergebnis, da Ihre Frage interessant erscheint

0
JB9

Das folgende Skript ist gemäß den SQL Server-Regeln formatiert. Sie können dies gemäß den MySQL-Regeln ändern und ausprobieren.

SELECT 
  P.id,
  P.name,
  PIVOT_METADATA.price,
  PIVOT_METADATA.sku,
  size.name AS size
FROM products P (NOLOCK)

INNER JOIN term_relationships AS tr (NOLOCK)
    ON P.id = tr.object_id

INNER JOIN term_taxonomy AS tt (NOLOCK)
    ON tr.term_taxonomy_id = tt.term_taxonomy_id AND tt.taxonomy = 'size'

INNER JOIN terms AS size (NOLOCK)
    ON tt.term_id = size.term_id

INNER JOIN METADATA (NOLOCK)
    PIVOT
    (
        MAX(value)
        FOR [meta_key] IN (price,sku)
    )AS PIVOT_METADATA
    ON P.id = PIVOT_METADATA.product_id

Was ich denke, könnte der Engpass in Ihrer Abfrage sein - Sie werden 2 Mal Metadaten beitreten. Da Ihre Tabellen 1-zu-viele-Beziehungen enthalten, schadet der Metadaten-2-Join nicht. Wenn Sie jedoch weitere Tabellen verknüpfen, nimmt die Anzahl der Zeilen aufgrund der 1-zu-viele-Beziehung zu und damit die Leistung ab .

Was ich versucht habe zu erreichen - Ich stelle sicher, dass so viele 1-zu-1-Beziehungen wie möglich erfüllt werden. Zu diesem Zweck habe ich ein Pivot für Metadaten durchgeführt und price & sku als Spalten erstellt. Jetzt darf meine Produkt-ID nur eine Zeile im Metadaten-Pivot enthalten. außerdem habe ich dafür gesorgt, dass ich mich ganz zum schluss diesem picot anschließe.

Versuche es. Bitte teilen Sie die erwartete Leistung, Anzahl der Datensätze, die Sie haben und auch, welche Leistung Sie mit meinem Asnwer erhalten.

0
Suyash Khandwe

Versuche dies:

SELECT p.id, p.name, MAX(CASE m.meta_key WHEN 'price' THEN m.value ELSE '' END) AS price, 
       MAX(CASE m.meta_key WHEN 'sku' THEN m.value ELSE '' END) AS sku, s.name AS size
FROM products p 
INNER JOIN `metadata` AS m ON p.id = m.product_id  
INNER JOIN `term_relationships` AS tr ON p.id = tr.object_id 
INNER JOIN `term_taxonomy` AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id AND tt.taxonomy = 'size'
INNER JOIN `terms` AS s ON tt.term_id = s.term_id
GROUP BY p.id;

Wenn Sie immer noch feststellen, dass Ihre Abfrage langsam ist, fügen Sie den Plan EXPLAIN meiner Abfrage hinzu, damit ich feststellen kann, welche Spalten INDEX benötigen.

0
Saharsh Shah