Implementierung von MySQL Select in der Unterabfrageoptimierung

Implementierung von MySQL Select in der Unterabfrageoptimierung

Die folgende Demonstration basiert auf MySQL Version 5.7.27

1. Einführung in die MySQL-Unterabfrage-Optimierungsstrategie:

Unterabfrage-Optimierungsstrategie

Der Optimierer wählt für verschiedene Arten von Unterabfragen unterschiedliche Strategien.

1. Für IN- und =ANY-Unterabfragen stehen dem Optimierer die folgenden Strategieoptionen zur Verfügung:

  • Halbverbindung
  • Materialisation
  • existiert

2. Für NOT IN- und <> ALL-Unterabfragen stehen dem Optimierer folgende Strategieoptionen zur Verfügung:

  • Materialisation
  • existiert

3. Für abgeleitete Tabellen stehen dem Optimierer folgende Strategieoptionen zur Verfügung:
derived_merge, fügt eine abgeleitete Tabelle in eine äußere Abfrage ein (eingeführt in 5.7);
Materialisieren Sie die abgeleitete Tabelle in eine interne temporäre Tabelle und verwenden Sie sie dann in der externen Abfrage.
Hinweis: Unterabfragen in Update- und Delete-Anweisungen können keine Semijoin- oder Materialisierungsoptimierungsstrategien verwenden.

2. Daten zur Simulationsdemonstration erstellen

Um die Analyse des Problems zu erleichtern, erstellen Sie zwei Tabellen und fügen Sie simulierte Daten ein:

CREATE TABLE `test02` (
 `id` int(11) NICHT NULL,
 `a` int(11) DEFAULT NULL,
 `b` int(11) DEFAULT NULL,
 Primärschlüssel (`id`),
 SCHLÜSSEL `a` (`a`)
)ENGINE=InnoDB;

Prozedur-IDA löschen;
Trennzeichen;;
Prozedur idata() erstellen
beginnen
 deklariere i int;
 setze i=1;
 während(i<=10000)mache
  in test02 Werte (i, i, i) einfügen;
  setze i=i+1;
 Ende während;
Ende;;
Trennzeichen ;
rufen Sie idata() auf;

Erstellen Sie die Tabelle test01 wie test02;
in test01 einfügen (select * from test02 where id<=1000)

3. Beispielanalyse von SQL-Instanzen

Beispiel für eine Unterabfrage:

Wählen Sie * aus test01, wobei test01.a in (Wählen Sie test02.b aus test02, wobei id < 10)

Die meisten Leute würden einfach denken, dass dieses SQL wie folgt ausgeführt wird:

Wählen Sie test02.b aus test02, wobei id < 10 ist.

Ergebnisse: 1, 2, 3, 4, 5, 6, 7, 8, 9

Wählen Sie * aus Test01, wobei Test01.a in (1,2,3,4,5,6,7,8,9) ist.

Aber so funktioniert MySQL eigentlich nicht. MySQL schiebt die zugehörige äußere Tabelle in die Unterabfrage und der Optimierer geht davon aus, dass dies effizienter ist. Mit anderen Worten: Der Optimierer schreibt das obige SQL wie folgt um:

wähle * aus test01, wo existiert (wähle b aus test02, wo ID < 10 und test01.a=test02.b);

Tipp: Für MySQL 5.5 und frühere Versionen

Überprüfen Sie den Ausführungsplan wie folgt und stellen Sie fest, dass dieses SQL 1000 Mal einen vollständigen Tabellenscan für die Tabelle test01 durchführt, was ineffizient ist:

root@localhost [dbtest01]>desc select * from test01, wo vorhanden (select b from test02, wo id < 10 und test01.a=test02.b);
+----+--------------------+--------+------------+-------+---------------+---------+---------+---------+---------+---------+----------+----------+-------------+
| ID | Auswahltyp | Tabelle | Partitionen | Typ | mögliche Schlüssel | Schlüssel | Schlüssellänge | Ref. | Zeilen | gefiltert | Extra |
+----+--------------------+--------+------------+-------+---------------+---------+---------+---------+---------+---------+----------+----------+-------------+
| 1 | PRIMARY | test01 | NULL | ALL | NULL | NULL | NULL | NULL | 1000 | 100.00 | Verwenden von „where“ |
| 2 | ABHÄNGIGE UNTERABFRAGE | test02 | NULL | Bereich | PRIMÄR | PRIMÄR | 4 | NULL | 9 | 10.00 | Verwenden von „where“ |
+----+--------------------+--------+------------+-------+---------------+---------+---------+---------+---------+---------+----------+----------+-------------+
2 Zeilen im Satz, 2 Warnungen (0,00 Sek.)

Aber wenn wir das folgende SQL tatsächlich ausführen, stellen wir fest, dass es überhaupt nicht langsam ist. Ist das nicht ein Widerspruch? Keine Sorge, lasst uns weiter analysieren:

Wählen Sie * aus test01, wobei test01.a in (Wählen Sie test02.b aus test02, wobei id < 10)

Überprüfen Sie den Ausführungsplan dieses SQL wie folgt:

root@localhost [dbtest01]>desc SELECT * FROM test01 WHERE test01.a IN (SELECT test02.b FROM test02 WHERE id < 10);
+----+--------------+----------+-------------+-----------+---------------+--------+---------+------------+---------+-----------+----------+----------+-------------+
| ID | Auswahltyp | Tabelle | Partitionen | Typ | mögliche Schlüssel | Schlüssel | Schlüssellänge | Ref. | Zeilen | gefiltert | Extra |
+----+--------------+----------+-------------+-----------+---------------+--------+---------+------------+---------+-----------+----------+----------+-------------+
| 1 | SIMPLE | <subquery2> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | 100,00 | Verwenden von „where“ |
| 1 | EINFACH | test01 | NULL | ref | a | a | 5 | <Unterabfrage2>.b | 1 | 100,00 | NULL |
| 2 | MATERIALISIERT | test02 | NULL | Bereich | PRIMÄR | PRIMÄR | 4 | NULL | 9 | 100,00 | Verwenden von „where“ |
+----+--------------+----------+-------------+-----------+---------------+--------+---------+------------+---------+-----------+----------+----------+-------------+
3 Zeilen im Satz, 1 Warnung (0,00 Sek.)

Es wurde festgestellt, dass der Optimierer die MATERIALIZED-Strategie verwendet. Also habe ich nach Informationen gesucht und diese Strategie studiert.
https://dev.mysql.com/doc/refman/5.6/en/subquery-optimization.html

Der Grund dafür ist, dass der Optimierer seit MySQL 5.6, einschließlich MySQL 5.6, neue Optimierungsstrategien eingeführt hat: Materialisierung=[aus|ein], Semijoin=[aus|ein], (aus bedeutet, diese Strategie auszuschalten, ein bedeutet, diese Strategie einzuschalten).
Sie können Show-Variablen wie „optimizer_switch“ verwenden, um die von MySQL verwendete Optimierungsstrategie anzuzeigen. Natürlich können diese Strategien online dynamisch geändert werden.
setze global optimizer_switch='materialization=on,semijoin=on'; steht für das Einschalten der Optimierungsstrategien Materialisierung und Semijoin

Die Standard-Optimierungsstrategie für MySQL 5.7.27 ist:

root@localhost [dbtest01]>Variablen wie „optimizer_switch“ anzeigen;                                                               
+------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Variablenname | Wert |
+------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| optimizer_switch | index_merge=ein,index_merge_union=ein,index_merge_sort_union=ein,index_merge_intersection=ein,engine_condition_pushdown=ein,index_condition_pushdown=ein,mrr=ein,mrr_cost_based=ein,block_nested_loop=ein,batched_key_access=aus,materialization=ein,semijoin=ein,loosescan=ein,firstmatch=ein,duplicateweedout=ein,subquery_materialization_cost_based=ein,use_index_extensions=ein,condition_fanout_filter=ein,derived_merge=ein |
+------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

In MySQL 5.6 und höher

Die Ausführung des folgenden SQL wird nicht langsam sein. Da die MySQL-Optimierungsstrategien Materialisierung und Semijoin diese SQL optimieren

Wählen Sie * aus test01, wobei test01.a in (Wählen Sie test02.b aus test02, wobei id < 10)

Wir haben jedoch zum Testen die MySQL-Optimierungsstrategien Materialisierung und Semijoin deaktiviert und festgestellt, dass SQL die gesamte Tabelle von test01 (1000) gescannt hat:

Setzen Sie den globalen Optimiererschalter auf „Materialisierung=aus, Semijoin=aus“.

Der Ausführungsplan sieht wie folgt aus. Die Tabelle test01 wird tatsächlich vollständig gescannt:

root@localhost [dbtest01]>desc SELECT * FROM test01 WHERE test01.a IN (SELECT test02.b FROM test02 WHERE id < 10);
+----+--------------------+--------+------------+-------+---------------+---------+---------+---------+---------+---------+----------+----------+-------------+
| ID | Auswahltyp | Tabelle | Partitionen | Typ | mögliche Schlüssel | Schlüssel | Schlüssellänge | Ref. | Zeilen | gefiltert | Extra |
+----+--------------------+--------+------------+-------+---------------+---------+---------+---------+---------+---------+----------+----------+-------------+
| 1 | PRIMARY | test01 | NULL | ALL | NULL | NULL | NULL | NULL | 1000 | 100.00 | Verwenden von „where“ |
| 2 | ABHÄNGIGE UNTERABFRAGE | test02 | NULL | Bereich | PRIMÄR | PRIMÄR | 4 | NULL | 9 | 10.00 | Verwenden von „where“ |
+----+--------------------+--------+------------+-------+---------------+---------+---------+---------+---------+---------+----------+----------+-------------+
2 Zeilen im Satz, 1 Warnung (0,00 Sek.)

Lassen Sie uns diesen Ausführungsplan analysieren:

! ! ! ! Noch ein Tipp: Wenn Sie MySQL 5.5 oder frühere Versionen oder MySQL 5.6 oder spätere Versionen verwenden und die Optimierungsstrategie materialization=off, semijoin=off deaktivieren, erhalten Sie den folgenden SQL-Ausführungsplan.

root@localhost [dbtest01]>desc select * from test01, wo vorhanden (select b from test02, wo id < 10 und test01.a=test02.b);
+----+--------------------+--------+------------+-------+---------------+---------+---------+---------+------+---------+----------+----------+-------------+
| ID | Auswahltyp | Tabelle | Partitionen | Typ | mögliche Schlüssel | Schlüssel | Schlüssellänge | Ref. | Zeilen | gefiltert | Extra |
+----+--------------------+--------+------------+-------+---------------+---------+---------+---------+------+---------+----------+----------+-------------+
| 1 | PRIMARY | test01 | NULL | ALL | NULL | NULL | NULL | NULL | 1000 | 100.00 | Verwenden von „where“ |
| 2 | ABHÄNGIGE UNTERABFRAGE | test02 | NULL | Bereich | PRIMÄR | PRIMÄR | 4 | NULL | 9 | 10.00 | Verwenden von „where“ |
+----+--------------------+--------+------------+-------+---------------+---------+---------+---------+------+---------+----------+----------+-------------+
2 Zeilen im Satz, 2 Warnungen (0,00 Sek.)

Die nicht korrelierte Unterabfrage wird zu einer korrelierten Unterabfrage (select_type:DEPENDENT SUBQUERY). Die Unterabfrage muss mit der äußeren Tabelle test01 basierend auf b verknüpft werden. Da das Feld test01 der äußeren Tabelle erforderlich ist, kann die Unterabfrage nicht zuerst ausgeführt werden. Der Ausführungsprozess ist:

  1. Scannen Sie test01 und extrahieren Sie eine Datenzeile R aus test01;
  2. Aus der Datenzeile R entnehmen wir das Feld a und führen die Unterabfrage aus. Wenn das Ergebnis WAHR ist, fügen wir diese Datenzeile R in den Ergebnissatz ein.
  3. Wiederholen Sie 1 und 2 bis zum Ende.

Die Gesamtzahl der gescannten Zeilen beträgt 1000+1000*9=10000 (das ist ein theoretischer Wert, aber der tatsächliche Wert ist kleiner als 10000. Ich habe nie herausgefunden, wie das zustande kam. Die Regel ist, dass für jede zusätzliche Zeile im Ergebnissatz der Unterabfrage die Gesamtzahl der gescannten Zeilen um einige Zeilen reduziert wird).

Semi-Join-Optimierer:

Dies führt zu einem Problem. Wenn die äußere Tabelle sehr groß ist, muss die Unterabfrage einmal für jede Zeile der äußeren Abfrage ausgeführt werden, und die Leistung dieser Abfrage wird sehr schlecht sein. Zur Verbesserung der Effizienz kann man leicht daran denken, es in „Join“ umzuschreiben:

wähle test01.* aus test01, führe test02 aus mit test01.a=test02.b und test02.id<10;

# Zeigen Sie den Ausführungsplan dieses SQL an:

desc wähle test01.* aus test01, schließe dich test02 an auf test01.a=test02.b und test02.id<10;

root@localhost [dbtest01]>EXPLAIN erweiterte Auswahl test01.* von test01 verbinden test02 auf test01.a=test02.b und test02.id<10;
+----+----------+--------+------------+-------+---------------+--------+---------+-------------------+----------+----------+-------------+
| ID | Auswahltyp | Tabelle | Partitionen | Typ | mögliche Schlüssel | Schlüssel | Schlüssellänge | Ref. | Zeilen | gefiltert | Extra |
+----+----------+--------+------------+-------+---------------+--------+---------+-------------------+----------+----------+-------------+
| 1 | SIMPLE | test02 | NULL | Bereich | PRIMARY | PRIMARY | 4 | NULL | 9 | 100,00 | Verwenden von „where“ |
| 1 | EINFACH | test01 | NULL | ref | a | a | 5 | dbtest01.test02.b | 1 | 100,00 | NULL |
+----+----------+--------+------------+-------+---------------+--------+---------+-------------------+----------+----------+-------------+
2 Zeilen im Satz, 2 Warnungen (0,00 Sek.)

Durch diese Optimierung kann die Tabelle t2 als treibende Tabelle fungieren und die zugehörigen Felder der Tabelle t1 verfügen über Indizes, wodurch die Suchleistung sehr hoch wird.

Hier gibt es jedoch ein Problem. Join kann zu doppelten Ergebnissen führen, während die Unterabfragesemantik (select ...) keine doppelten Werte liefert.
Semijoin ist ein spezieller Join, der das Problem doppelter Werte löst.
Bei einer Unterabfrage kann der Optimierer erkennen, dass für jede Gruppe in der in-Klausel nur ein Wert zurückgegeben werden muss. In diesem Fall kann Semijoin verwendet werden, um die Unterabfrage zu optimieren und die Abfrageeffizienz zu verbessern.
Dies ist eine neue Funktion, die in MySQL 5.6 hinzugefügt wurde. Vor MySQL 5.6 verfügte der Optimierer nur über die Exist-Strategie, um Unterabfragen zu „optimieren“.

Der SQL- und Ausführungsplan nach der Semijoin-Optimierung lautet wie folgt:

root@localhost [dbtest01]>desc SELECT * FROM test01 WHERE test01.a IN (SELECT test02.b FROM test02 WHERE id < 10);
+----+--------------+----------+-------------+-----------+---------------+--------+---------+------------+---------+-----------+----------+----------+-------------+
| ID | Auswahltyp | Tabelle | Partitionen | Typ | mögliche Schlüssel | Schlüssel | Schlüssellänge | Ref. | Zeilen | gefiltert | Extra |
+----+--------------+----------+-------------+-----------+---------------+--------+---------+------------+---------+-----------+----------+----------+-------------+
| 1 | SIMPLE | <subquery2> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | 100,00 | Verwenden von „where“ |
| 1 | EINFACH | test01 | NULL | ref | a | a | 5 | <Unterabfrage2>.b | 1 | 100,00 | NULL |
| 2 | MATERIALISIERT | test02 | NULL | Bereich | PRIMÄR | PRIMÄR | 4 | NULL | 9 | 100,00 | Verwenden von „where“ |
+----+--------------+----------+-------------+-----------+---------------+--------+---------+------------+---------+-----------+----------+----------+-------------+
3 Zeilen im Satz, 1 Warnung (0,00 Sek.)
wählen 
  `test01`.`id`,`test01`.`a`,`test01`.`b` 
von `test01` Semi-Join `test02` 
Wo
  ((`test01`.`a` = `<Unterabfrage2>`.`b`) 
  und (`test02`.`id` < 10)); 

##Beachten Sie, dass dies das vom Optimierer neu geschriebene SQL ist. Die Semi-Join-Syntax kann auf dem Client nicht verwendet werden.

Die Optimierungsimplementierung von Semijoin ist relativ komplex und wird in Strategien wie FirstMatch und Materialize unterteilt. Im obigen Ausführungsplan bedeutet select_type=MATERIALIZED, dass Semijoin mit der Materialize-Strategie implementiert wird.
Hier ist der Ausführungsfluss des Semijoins nach der Optimierung:

Führen Sie zunächst die Unterabfrage aus und speichern Sie das Ergebnis in einer temporären Tabelle. Diese temporäre Tabelle verfügt über einen Primärschlüssel zur Deduplizierung.
Nehmen Sie eine Datenzeile R aus der temporären Tabelle;
Aus der Datenzeile R wird das Feld b herausgenommen und in der getriebenen Tabelle t1 gesucht. Wenn es die Bedingungen erfüllt, wird es in den Ergebnissatz eingefügt.
Wiederholen Sie die Schritte 2 und 3 bis zum Ende.
Auf diese Weise hat das Unterabfrageergebnis 9 Zeilen, dh die temporäre Tabelle hat auch 9 Zeilen (hier gibt es keine doppelten Werte), und die Gesamtzahl der gescannten Zeilen beträgt 9 + 9 + 9 * 1 = 27 Zeilen, was viel weniger ist als die ursprünglichen 10.000 Zeilen.

Eine weitere in MySQL 5.6 hinzugefügte Optimierungsfunktion ist die Materialisierung, die die Ergebnisse der Unterabfrage in einer temporären Tabelle materialisiert und sie dann in die äußere Suchabfrage einsetzt, um die Abfrageausführung zu beschleunigen. Die temporäre Tabelle im Arbeitsspeicher enthält den Primärschlüssel (Hash-Index), eliminiert doppelte Zeilen und verkleinert die Tabelle.
Wenn das Ergebnis der Unterabfrage zu groß ist und die tmp_table_size überschreitet, wird es zu einer temporären Festplattentabelle degeneriert. Auf diese Weise muss die Unterabfrage nur einmal ausgeführt werden und nicht für jede Zeile der äußeren Abfrage.
Es ist jedoch zu beachten, dass solche externen Abfragen berechtigte Daten immer noch nicht schnell über Indizes finden können und nur über vollständige Tabellenscans oder vollständige Indexscans durchgeführt werden können.

Das Öffnen von Semijoin und Materialisierung wird durch die Flags semijoin={on|off} und materialization={on|off} im Parameter optimizer_switch gesteuert.
Die oben genannten verschiedenen Ausführungspläne werden durch Ein- und Ausschalten von Semijoin und Materialisierung generiert. Generell gilt: Bei Unterabfragen prüfen Sie zunächst, ob die Bedingungen verschiedener Optimierungsstrategien erfüllt sind (wenn die Unterabfrage beispielsweise eine Union enthält, kann die Semijoin-Optimierung nicht verwendet werden).
Der Optimierer trifft dann eine kostenbasierte Auswahl. Wenn keine andere Wahl möglich ist, wird die Exist-Strategie verwendet, um die Unterabfrage zu „optimieren“. Es gibt keinen Parameter zum Aktivieren oder Deaktivieren der Exist-Strategie.

Hier ist ein Beispiel für eine löschbezogene Unterabfrage:

Füllen Sie die beiden obigen Testtabellen mit 3,5 Millionen Daten bzw. 500.000 Daten, um die Löschanweisung zu testen

root@localhost [dbtest01]>Wählen Sie count(*) aus test02 aus.
+----------+
| Anzahl(*) |
+----------+
|3532986|
+----------+
1 Zeile im Satz (0,64 Sek.)
root@localhost [dbtest01]>Tabelle test01 wie test02 erstellen;
Abfrage OK, 0 Zeilen betroffen (0,01 Sek.)

root@localhost [dbtest01]>insert in test01 (wählen Sie * aus test02, wo id<=500000)

root@localhost [dbtest01]>Wählen Sie count(*) aus test01;
+----------+
| Anzahl(*) |
+----------+
| 500000 |

Die Ausführung der Löschanweisung dauerte 4 Sekunden.

root@localhost [dbtest01]>löschen FROM test01 WHERE test01.a IN (SELECT test02.b FROM test02 WHERE id < 10);
Abfrage OK, 9 Zeilen betroffen (4,86 Sek.)

Wenn wir uns den Ausführungsplan ansehen, können wir sehen, dass die Tabelle test01 fast vollständig gescannt ist:

root@localhost [dbtest01]>desc löschen FROM test01 WHERE test01.a IN (SELECT test02.b FROM test02 WHERE id < 10);
+----+--------------------+--------+------------+-------+---------------+---------+---------+---------+---------+---------+----------+----------+-------------+
| ID | Auswahltyp | Tabelle | Partitionen | Typ | mögliche Schlüssel | Schlüssel | Schlüssellänge | Ref. | Zeilen | gefiltert | Extra |
+----+--------------------+--------+------------+-------+---------------+---------+---------+---------+---------+---------+----------+----------+-------------+
| 1 | LÖSCHEN | test01 | NULL | ALLE | NULL | NULL | NULL | NULL | 499343 | 100,00 | Verwenden von „where“ |
| 2 | ABHÄNGIGE UNTERABFRAGE | test02 | NULL | Bereich | PRIMÄR | PRIMÄR | 4 | NULL | 9 | 10.00 | Verwenden von „where“ |
+----+--------------------+--------+------------+-------+---------------+---------+---------+---------+---------+---------+----------+----------+-------------+
2 Zeilen im Satz (0,00 Sek.)

Ändern Sie daher die obige SQL-Anweisung zum Löschen in eine Pseudo-Join-Anweisung.

root@localhost [dbtest01]>desc lösche test01.* von test01, führe test02 mit test01.a=test02.b und test02.id<10 durch;
+----+----------+--------+------------+-------+---------------+--------+---------+-------------------+----------+----------+-------------+
| ID | Auswahltyp | Tabelle | Partitionen | Typ | mögliche Schlüssel | Schlüssel | Schlüssellänge | Ref. | Zeilen | gefiltert | Extra |
+----+----------+--------+------------+-------+---------------+--------+---------+-------------------+----------+----------+-------------+
| 1 | SIMPLE | test02 | NULL | Bereich | PRIMARY | PRIMARY | 4 | NULL | 9 | 100,00 | Verwenden von „where“ |
| 1 | LÖSCHEN | test01 | NULL | ref | a | a | 5 | dbtest01.test02.b | 1 | 100.00 | NULL |
+----+----------+--------+------------+-------+---------------+--------+---------+-------------------+----------+----------+-------------+
2 Zeilen im Satz (0,01 Sek.)

Die Ausführung ist sehr schnell. root@localhost [dbtest01]>delete test01.* from test01 join test02 on test01.a=test02.b and test02.id<10;
Abfrage OK, 9 Zeilen betroffen (0,01 Sek.)

root@localhost [dbtest01]>Wählen Sie test01.* aus test01 aus und verbinden Sie test02 mit test01.a=test02.b und test02.id<10;
Leerer Satz (0,00 Sek.)

Die folgende Tabellenausführung erfordert einen vollständigen Tabellenscan, der sehr langsam ist. Im Wesentlichen wird ein vollständiger Tabellenscan für die Tabelle test01 durchgeführt:

root@lcalhost [dbtest01]>desc löschen FROM test01 WHERE id IN (SELECT id FROM test02 WHERE id='350000');
+----+--------------------+--------+------------+-------+---------------+---------+---------+---------+-----------+--------+-----------+-------------+
| ID | Auswahltyp | Tabelle | Partitionen | Typ | mögliche Schlüssel | Schlüssel | Schlüssellänge | Ref. | Zeilen | gefiltert | Extra |
+----+--------------------+--------+------------+-------+---------------+---------+---------+---------+-----------+--------+-----------+-------------+
| 1 | LÖSCHEN | test01 | NULL | ALLE | NULL | NULL | NULL | NULL | 499343 | 100,00 | Verwenden von „where“ |
| 2 | ABHÄNGIGE UNTERABFRAGE | test02 | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | Index wird verwendet |
+----+--------------------+--------+------------+-------+---------------+---------+---------+---------+-----------+--------+-----------+-------------+
2 Zeilen im Satz (0,00 Sek.)

Wenn jedoch Join verwendet wird, ist die Effizienz sehr hoch:

root@localhost [dbtest01]>desc lösche test01.* FROM test01 inner join test02 WHERE test01.id=test02.id und test02.id=350000;
+----+----------+--------+------------+-------+---------------+--------+---------+---------+----------+-------+----------+-------------+
| ID | Auswahltyp | Tabelle | Partitionen | Typ | mögliche Schlüssel | Schlüssel | Schlüssellänge | Ref. | Zeilen | gefiltert | Extra |
+----+----------+--------+------------+-------+---------------+--------+---------+---------+----------+-------+----------+-------------+
| 1 | LÖSCHEN | test01 | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100,00 | NULL |
| 1 | SIMPLE | test02 | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | Index wird verwendet |
+----+----------+--------+------------+-------+---------------+--------+---------+---------+----------+-------+----------+-------------+
2 Zeilen im Satz (0,01 Sek.)

 
root@localhost [dbtest01]> desc lösche test01.* von test01, führe test02 mit test01.a=test02.b und test02.id=350000 zusammen;
+----+----------+--------+------------+-------+---------------+--------+---------+---------+-------+------+------+------+------+------+
| ID | Auswahltyp | Tabelle | Partitionen | Typ | mögliche Schlüssel | Schlüssel | Schlüssellänge | Ref. | Zeilen | gefiltert | Extra |
+----+----------+--------+------------+-------+---------------+--------+---------+---------+-------+------+------+------+------+------+
| 1 | EINFACH | test02 | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100,00 | NULL |
| 1 | LÖSCHEN | test01 | NULL | ref | a | a | 5 | const | 1 | 100,00 | NULL |
+----+----------+--------+------------+-------+---------------+--------+---------+---------+-------+------+------+------+------+------+
2 Zeilen im Satz (0,00 Sek.)

Referenzdokumente:

https://www.cnblogs.com/zhengyun_ustc/p/slowquery1.html
https://www.jianshu.com/p/3989222f7084
https://dev.mysql.com/doc/refman/5.6/en/subquery-optimization.html

Dies ist das Ende dieses Artikels über die Implementierung von MySQL Select in der Unterabfrageoptimierung. Weitere relevante Inhalte zu MySQL Select in der Unterabfrageoptimierung finden Sie in den vorherigen Artikeln von 123WORDPRESS.COM oder in den folgenden verwandten Artikeln. Ich hoffe, dass jeder 123WORDPRESS.COM in Zukunft unterstützen wird!

Das könnte Sie auch interessieren:
  • Eine kurze Diskussion zur MySQL-Select-Optimierungslösung
  • MySQL-Beispiel-Tutorial: Ergebnisse auswählen, um ein Update durchzuführen
  • Beheben Sie das Problem, dass die Lese-/Schreibtrennung von MySQL dazu führt, dass Daten nach dem Einfügen nicht ausgewählt werden
  • Wie die MySQL Select-Anweisung ausgeführt wird
  • MySQL-Lernhinweise: Vollständiges Verwendungsbeispiel für Select-Anweisungen, detaillierte Erläuterung
  • Codebeispiele für MySQL-Batchoperationsanweisungen „Select“, „Insert“ und „Update“
  • Ein kurzes Verständnis der MySQL SELECT-Ausführungsreihenfolge
  • Erläuterung der MySQL-Transaktionsauswahl für die Aktualisierung und Datenkonsistenzverarbeitung
  • Der Unterschied zwischen Update und Select in MySQL für einzelne und mehrere Tabellen sowie Ansichten und temporäre Tabellen
  • Detaillierte Erläuterung der Verwendung des MySQL-Auswahl-Cache-Mechanismus
  • Zusammenfassung der Select-Verwendung in der MySql-Datenbank
  • So wird eine Select-Anweisung in MySQL ausgeführt

<<:  Detaillierte Erläuterung des Docker-Datensicherungs- und Wiederherstellungsprozesses

>>:  Rendering-Funktion und JSX-Details

Artikel empfehlen

Eine kurze Diskussion über die Semantik von HTML und einige einfache Optimierungen

1. Was ist Semantisierung? Erklärung zum Bing-Wör...

Natives JS realisiert zusammengesetzte Bewegungen verschiedener Bewegungen

In diesem Artikel erfahren Sie mehr über eine zus...

Eine kurze Analyse des MySQL-Index

Ein Datenbankindex ist eine Datenstruktur, deren ...

Einfaches Setup von VMware ESXi6.7 (mit Bildern und Text)

1. Einführung in VMware vSphere VMware vSphere is...

Definition und Verwendung des MySQL-Cursors

Erstellen eines Cursors Erstellen Sie zunächst ei...

JavaScript zum Erzielen eines ausgewogenen digitalen Scroll-Effekts

Inhaltsverzeichnis 1. Hintergrund zur Umsetzung 2...

Vue3 AST Parser-Quellcode-Analyse

Inhaltsverzeichnis 1. Generieren Sie einen abstra...

Detaillierte Beispiele für die Ausführung von Zabbix-Remotebefehlen

Inhaltsverzeichnis eins. Umfeld zwei. Vorsichtsma...

Das Miniprogramm implementiert nativ das linksseitige Schubladenmenü

Inhaltsverzeichnis WXS-Antwortereignis Plan A Sei...

Zentrieren des Formulars in HTML

Ich bin einmal auf eine Aufgabe gestoßen, bei der...