Ein Beekeeper Studio-Plugin, das Ausführungspläne von Abfragen in ein interaktives Diagramm verwandelt, damit du den langsamen Teil einer Abfrage leicht findest.

- Diagramm des Plans, eingefärbt nach dem Anteil jedes Schritts an der Ausführungszeit (oder den geschätzten Kosten). Breitere Verbindungen transportieren mehr Zeilen.
- Tabellenansicht im Stil von
explain.depesz.com, sortierbar nach Eigenzeit. - Details zu jedem Schritt: Eigen- und Gesamtzeit, tatsächliche vs. geschätzte Zeilen, Loops, Kosten, Bedingungen, Buffer, Speicher- und Festplattennutzung, Worker.
- Insights, die auf typische Probleme hinweisen: Full Scans, die die meisten Zeilen verwerfen, Tabellen, die pro Zeile eines Joins einmal gescannt werden, Sortierungen und Hashes, die auf die Festplatte auslagern, abhängige Subqueries, temporäre Indizes, die SQLite bei jedem Lauf neu baut, große Fehlschätzungen der Zeilenzahl und mehr.
- Rohausgabe des Plans, bereit zum Kopieren oder Speichern.
Mehr dazu in den Screenshots unten.
Unterstützte Datenbanken
| Datenbank | Explain | Explain Analyze | Plan einfügen |
|---|---|---|---|
| PostgreSQL | Ja | Ja | JSON und psql-Text |
| MySQL | Ja | 8.0.18+, lesende Anweisungen | Tree, JSON (beide Versionen) und die klassische Tabelle |
| MariaDB | Ja | 10.1+, lesende Anweisungen | JSON und die klassische Tabelle |
| SQLite, LibSQL | Ja | Nein: SQLite liefert keine Zeiten pro Schritt | Der Baum aus der sqlite3-Shell oder die Ergebniszeilen |
| SQL Server | Geplant |
Getestet mit PostgreSQL 12, 17 und 18; MySQL 5.7, 8.0.17, 8.0, 8.4 und 9.7; MariaDB 10.4, 10.6 und 11.4; sowie SQLite 3.16, 3.27 und 3.45. Das Plugin basiert auf einem datenbankneutralen Planmodell, sodass jede Datenbank nur einen Adapter braucht (siehe Eine Datenbank hinzufügen).
So nutzt du es
Es gibt mehrere Wege:
- Abfrage-Editor: Klicke mit rechts auf eine Abfrage und wähle Visualize Explain Plan. Verwendet wird der markierte Text oder die Anweisung unter dem Cursor.
- Menü „Neuer Tab“: Öffne Explain Visualizer, schreib eine Anweisung und wähle Explain oder Explain Analyze.
- Ergebnistabelle: wenn du EXPLAIN selbst ausgeführt hast, klicke mit rechts auf den Plan und wähle Visualize Explain Plan:
- ein Plan in einer Zelle (
FORMAT JSONvon PostgreSQL, Tree oder JSON von MySQL): Rechtsklick auf die Zelle; - ein Plan über mehrere Zeilen (Textausgabe von PostgreSQL): Rechtsklick auf den Spaltenkopf
QUERY PLAN; - eine Tabelle (klassisches
EXPLAINvon MySQL,EXPLAIN QUERY PLANvon SQLite): Markiere alle Zeilen und Spalten und klicke dann mit rechts auf die Auswahl. Bei SQLite reicht auch die detail-Spalte allein, dann aber ohne Verschachtelung.
- ein Plan in einer Zelle (
- Paste Plan: Füge EXPLAIN-Ausgabe ein, zum Beispiel aus psql, der mysql- oder sqlite3-Shell oder von Kollegen.

Explain und Explain Analyze
Explain fragt die Datenbank nach ihrem Plan, ohne die Anweisung auszuführen. Das ist immer sicher, deshalb läuft es automatisch, wenn der Tab aus dem Editor geöffnet wird.
Explain Analyze führt die Anweisung aus und misst jeden Schritt. Wie das sicher bleibt, hängt von der Datenbank ab (siehe unten). Beginnt der Editortext bereits mit EXPLAIN ANALYZE (oder dem Gegenstück der Datenbank), verwendet das Plugin ihn; lässt sich die Anweisung nicht analysieren, wird stattdessen Explain ausgeführt und der Grund unter dem Editor angezeigt.
PostgreSQL
Das Plugin sendet EXPLAIN ANALYZE ...; ROLLBACK; als eine einzige Anfrage, sodass beide Anweisungen eine implizite Transaktion teilen und alles, was die Anweisung geändert hat, zurückgerollt wird. Das gilt auch für einfache SELECTs, die schreibende Funktionen aufrufen können. Schlägt die Anweisung fehl, wird die Transaktion von selbst abgebrochen und die Pool-Verbindung bleibt sauber. Effekte außerhalb der Transaktion, etwa Sequenzwerte, werden nicht rückgängig gemacht.
Anweisungen, die Daten ändern (INSERT, UPDATE, DELETE, MERGE, datenändernde CTEs…), werden erst nach einer Bestätigung analysiert. Das Plugin liest die Anweisung mit einem Lexer, der PostgreSQLs eigenen Regeln für Strings, Kommentare und Dollar-Quotes folgt, und lehnt Eingaben mit mehr als einer Anweisung ab.
Bei schreibgeschützten Verbindungen erlaubt Beekeeper Studio kein ROLLBACK, daher können datenändernde Anweisungen nur mit Explain betrachtet werden, und Explain Analyze läuft nie automatisch. Ein SELECT, das eine schreibende Funktion aufruft, wird dort nicht erkannt – genau wie beim Ausführen in einem Abfrage-Tab.
Das Menü Options schaltet BUFFERS, VERBOSE, SETTINGS und WAL um. Optionen, die der Server nicht unterstützt, sind deaktiviert. Anweisungen mit $1-Parametern werden ab PostgreSQL 16 mit GENERIC_PLAN erklärt.
MySQL und MariaDB
Das Plugin liest die Version des Servers aus und verwendet das neueste verfügbare Planformat:
| Server | Explain | Explain Analyze |
|---|---|---|
| MySQL 8.0.18+ | EXPLAIN FORMAT=TREE |
EXPLAIN ANALYZE |
| MySQL 5.6 bis 8.0.17 | EXPLAIN FORMAT=JSON |
Nicht verfügbar |
| MariaDB 10.1+ | EXPLAIN FORMAT=JSON |
ANALYZE FORMAT=JSON |
| Ältere Server | Die klassische EXPLAIN-Tabelle |
Nicht verfügbar |
UPDATE und DELETE auf eine einzelne Tabelle haben keinen Tree; das Plugin weicht auf FORMAT=JSON aus und unter MySQL 9, dessen JSON sie ebenfalls nicht darstellen kann, auf die klassische Tabelle. TiDB wird erkannt und abgelehnt, statt falsch gelesen zu werden.
Explain Analyze führt nur lesende Anweisungen aus. Anders als bei PostgreSQL kann man sich bei MySQL nicht darauf verlassen, dass eine fehlgeschlagene Anfrage rückgängig gemacht wird: Als START TRANSACTION; ...; ROLLBACK; gesendet, würde eine fehlgeschlagene Anweisung die Transaktion auf einer Pool-Verbindung offen lassen, die danach andere Tabs nutzen. Ein SELECT, das eine schreibende Stored Function aufruft, committet, was die Funktion getan hat – genau wie in einem Abfrage-Tab. Unter MariaDB lehnt der schreibgeschützte Modus von Beekeeper Studio die ANALYZE-Anweisung ab, daher ist Explain Analyze bei schreibgeschützten Verbindungen nicht verfügbar.
Die Anweisung wird nach MySQLs Regeln gelesen: #- und -- -Kommentare, die erst bei einem Zeilenvorschub enden, Backslash-Escapes, "..."-Strings und /*! ... */-Kommentare, deren Inhalt MySQL ausführt. NO_BACKSLASH_ESCAPES und ANSI_QUOTES ändern, wo Strings enden, und eine Pool-Verbindung kann beides gesetzt haben – deshalb muss eine Anweisung unter jeder Kombination als eine einzige, lesende Anweisung gelten. Text, den ein Server auf mehr als eine Art lesen kann, wird abgelehnt statt erraten: ein versionsabhängiger /*!50700 ... */- oder ein reiner MariaDB-/*M! ... */-Kommentar, ein Wagenrücklauf in einem ---Kommentar oder ein NUL-Zeichen.
MariaDB misst die Zeit pro Tabelle statt pro Schritt, daher ist die Zeit eines Joins oder einer temporären Tabelle das, was die Abfrage über die gelesenen Tabellen hinaus gebraucht hat.
SQLite
EXPLAIN QUERY PLAN zeigt, welche Tabellen in welcher Reihenfolge und mit welchem Index gelesen werden. SQLite liefert keine Kosten, Zeilenzahlen oder Zeiten, daher zeigt das Diagramm die Schritte und die Tabellenansicht deren Details, und es gibt kein Explain Analyze. Insights weisen auf temporäre Indizes hin, die SQLite bei jedem Lauf neu baut, auf Tabellen, die pro Zeile eines Joins oder einer korrelierten Subquery einmal gescannt werden, und auf Sortierungen in einem temporären B-Tree. LibSQL-Verbindungen nutzen dieselbe Engine.
Beekeeper Studio zerlegt SQLite-Text vor dem Ausführen mit einem eigenen Parser in Anweisungen. Eine Anweisung mit einem Semikolon darin (in einem String, Namen oder Kommentar) wird daher abgelehnt: So kann kein Splitter darin eine zweite Anweisung finden.
Screenshots
Explain Analyze mit ausgewähltem langsamen Schritt und einem Insight dazu:

Derselbe Plan als Tabelle, sortiert nach Eigenzeit:

Eine Abfrage mit einer CTE, einem InitPlan und einem SubPlan, jeweils als eigener Zweig dargestellt:

Das Plugin übernimmt das Theme von Beekeeper Studio:

Entwicklung
Benötigt Node.js 22+ und Yarn 1.
yarn install
yarn dev # builds with hot reload and links the plugin into Beekeeper Studio
yarn test # unit tests
yarn build # type-check and build into dist/
yarn dev nutzt @beekeeperstudio/vite-plugin, das diesen Ordner per Symlink in das Plugin-Verzeichnis von Beekeeper Studio einbindet.
Standardmäßig lädt Beekeeper Studio nur seine eigenen, freigegebenen Plugins. Um dieses zu laden, füge Folgendes zu deiner Benutzerkonfigurationsdatei hinzu und starte die App neu:
[pluginSystem]
disabled = false
Verbinde dich dann mit einer Datenbank: Visualize Explain Plan erscheint im Kontextmenü des Abfrage-Editors und Explain Visualizer im Menü „Neuer Tab“.
Harness
dev/harness.html ersetzt Beekeeper Studio: Es lädt das Plugin in einem iframe und beantwortet seine Anfragen mit denselben Nachrichten wie die App, wobei Abfragen gegen echte Datenbanken laufen. So arbeitest du am schnellsten an der UI, probierst jeden Weg aus, den Tab zu öffnen, und wechselst Datenbanken und Themes.
docker run -d --name pg -e POSTGRES_PASSWORD=pw -p 5432:5432 postgres:17
psql postgres://postgres:pw@localhost:5432/postgres -f scripts/fixtures/seed.sql
docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=pw -e MYSQL_DATABASE=shop -p 3306:3306 mysql:8.4
mysql -h127.0.0.1 -uroot -ppw shop < scripts/fixtures/seed-mysql.sql
PG_URL=postgres://postgres:pw@localhost:5432/postgres \
MYSQL_URL=mysql://root:pw@127.0.0.1:3306/shop \
yarn harness
MARIADB_URL lässt die MariaDB des Harness auf einen anderen Server zeigen (sonst wird MYSQL_URL verwendet). SQLite braucht nichts: Beim ersten Einsatz wird eine temporäre Datenbank aus scripts/fixtures/seed-sqlite.sql befüllt, oder SQLITE_PATH gibt eine an.
Tests
tests/postgresparst echte Pläne aus PostgreSQL 12, 17 und 18 (tests/fixtures/postgres). Jede Fixture enthält denselben Plan als JSON und als Text, und die Tests prüfen, dass beide denselben Baum ergeben.tests/mysqlparst Pläne aus MySQL 5.7, 8.0.17, 8.0, 8.4 und 9.7 sowie MariaDB 10.4, 10.6 und 11.4 (tests/fixtures/mysql). Jede Fixture enthält für eine Anweisung jedes Format, das ihr Server bietet: Tree,EXPLAIN ANALYZE, beide JSON-Versionen, MariaDBsANALYZEund die klassische Tabelle. Die Tests prüfen, dass die Formate übereinstimmen.tests/sqliteparstEXPLAIN QUERY PLAN-Zeilen aus SQLite 3.16 (das Format vor 3.24), 3.27 und 3.45 (tests/fixtures/sqlite).tests/integrationführt EXPLAIN gegen laufende Server aus: PostgreSQL, wennPG_URLgesetzt ist, MySQL oder MariaDB, wennMYSQL_URLgesetzt ist, und SQLite immer, über das in Node eingebautenode:sqlite.tests/statedeckt das Verhalten des Tabs mit einem Fake-Host ab.
Um Fixtures von einer anderen Serverversion aufzuzeichnen, befülle sie mit Testdaten und führe ihr Capture-Skript aus:
# PostgreSQL: seed with scripts/fixtures/seed.sql
PG_URL=postgres://... FIXTURE_PREFIX=pg16- node scripts/fixtures/capture-postgres.mjs
# MySQL or MariaDB: seed with scripts/fixtures/seed-mysql.sql; named after the server
MYSQL_URL=mysql://root:pw@127.0.0.1:3306/shop node scripts/fixtures/capture-mysql.mjs
# SQLite: uses Python's bundled SQLite, so older Python images give older SQLite
python3 scripts/fixtures/capture-sqlite.py
docker run --rm -v "$PWD:/w" -w /w python:3.6-stretch python3 scripts/fixtures/capture-sqlite.py
So funktioniert es
src/
core/ database-neutral plan model, analysis, metrics, layout, formatting
engines/ one adapter per database family, plus the registry
postgres/ EXPLAIN builder, JSON and text parsers, timing rules, insights
mysql/ version detection; tree, JSON (both versions) and table readers; insights
sqlite/ EXPLAIN QUERY PLAN rows and shell text; insights
shared/ helpers adapters build on: a configurable SQL lexer, statement
checks, Explain Analyze policies, result readers, a JSON parser
that keeps repeated keys
host/ everything that talks to Beekeeper Studio
state/ the tab's state and actions
components/ Vue components: diagram, table, details, raw view
Jeder Adapter wandelt die EXPLAIN-Ausgabe seiner Datenbank in das Plan-Modell in src/core/plan.ts um. Ein PlanNode hat einen Operationsnamen, einen kind (Scan, Index, Join, Filter, Sortierung…), optionale Schätzungen (totalCost, estimatedRows), optionale Messwerte (actualRows, loops, Zeiten), abgeleitete inclusiveTime, exclusiveTime und exclusiveCost sowie Bedingungen und gruppierte Eigenschaften für das Detail-Panel. Jede Metrik ist optional, und die UI zeigt nur, was eine Engine liefert: Ein SQLite-Plan hat keine Zahlen, daher zeigt die Tabelle statt Kosten die Details jedes Schritts.
Die Eigenzeit ist Engine-spezifisch, deshalb berechnet sie der Adapter. Die Regeln für PostgreSQL stehen in src/engines/postgres/timing.ts: Zeiten pro Loop werden mit der Anzahl der Loops multipliziert, parallele Worker werden einmal gezählt, CTE-Zeit wird den CTE-Scans zugerechnet, die sie lesen, und die Zeit eines InitPlans dem Knoten, der ihn auswertet. MySQL meldet Zeiten ebenfalls pro Loop und nutzt das gemeinsame computeLoopTimings (src/core/metrics.ts), wobei Subqueries in der Select-Liste zur Abfrage hinzugerechnet werden, da sie darin nicht enthalten sind. MariaDB misst Tabellen und ganze Abfragen, nicht Schritte; computeMixedTimings ergänzt den Rest.
Die drei Formate von MySQL werden auf dieselben Operationsnamen abgebildet: Tree-Text und JSON-Version 2 listen dieselben Iteratoren, und das Query-Block-JSON (sowie die klassische Tabelle, die darin umgebaut wird) wird zu einer Kette von Joins in der Reihenfolge der Tabellen. Die Insight-Regeln arbeiten mit diesen Namen, nicht mit der nativen Ausgabe.
src/core/analysis.ts ergänzt, was alle Engines gemeinsam haben: Zeit- und Kostenanteile, die langsamsten Schritte und Insights zu Fehlschätzungen der Zeilenzahl.
Eine Datenbank hinzufügen
Außerhalb des Engine-Ordners und der Registry muss nichts geändert werden.
- Erstelle
src/engines/<name>/mit einem Objekt vom TypExplainEngine(src/engines/types.ts):connectionTypes: die Verbindungstypen von Beekeeper Studio, die sie unterstützt, undproducts, wenn sie mehrere abdeckt (MySQL und MariaDB).inspect(sql, server): die Anweisung zerlegen und klassifizieren (ändert sie Daten?).src/engines/shared/lexer.tsnimmt einen Dialekt entgegen: Kommentarstile, Quotes, Platzhalter.analyzePolicy(statement, context): ob Explain Analyze sie ausführen darf, was vorher abgefragt wird und was unter dem Editor steht.src/engines/shared/analyzePolicy.tsenthält die Rollback- und die Nur-Lesen-Policy.explain(request, runQuery): das SQL senden, das die Datenbank braucht, und das Ergebnis parsen.runQuerydarf mehrfach aufgerufen werden, etwa um auf ein älteres Format auszuweichen.loadServerInfo(runQuery): die Version und alles, was die Engine sonst noch wissen muss (bei MySQL den SQL-Modus). ErweitereServerInfoum eigene Felder.canParse/parse: eingefügte Ausgabe und Werte aus der Ergebnistabelle lesen. Der Rohtext, den ein Parse zurückgibt, muss sich wieder in denselben Plan parsen lassen: Wiederhergestellte Tabs werden daraus neu aufgebaut.options: Schalter im Menü Options, jeweils mitunavailable(server), falls ein neuerer Server nötig ist.
- Ordne native Operatoren
NodeKinds zu und fülle die vorhandenen Metriken aus. Nutze für Kosten und Zeiten die Helfer insrc/core/metrics.ts, wo sie passen, und rufe am EndefinishPlan()aussrc/core/analysis.tsauf. - Registriere sie in
src/engines/registry.ts, füge Fixtures von echten Servern hinzu (ein Capture-Skript inscripts/fixtures/) und schreib Tests wie bei den anderen.
Hinweise zu SQL Server, das geplant ist:
- Der SQL-Server-Client von Beekeeper Studio teilt eine Anfrage bei
;auf und kann jede Anweisung auf einer anderen Pool-Verbindung ausführen.SET SHOWPLAN_XML ONmuss allein in seinem Batch stehen und gilt für den nächsten Batch auf derselben Verbindung, daher braucht ein geschätzter Plan eine neue Host-API, die Batches auf einer Verbindung ausführt. SET STATISTICS XML ONmit der Anweisung in einem Batch (ohne;) liefert den tatsächlichen Plan, führt die Anweisung aber aus und gibt ihre Zeilen an das Plugin zurück.- Aus SSMS eingefügtes Plan-XML (
.sqlplan) braucht keine Host-Unterstützung.
Release
- Führe
bin/make-release.shaus. Es schlägt anhand der vorhandenen Tags die nächste Version vor, setztversioninmanifest.jsonundpackage.json, committet und pusht einen passenden Tag wiev0.1.0. - Der Release-Workflow baut, testet und erstellt einen GitHub-Release-Entwurf mit
manifest.jsonundexplain-visualizer-<version>.zip. Veröffentliche den Entwurf.
yarn build && yarn package erstellt dasselbe Archiv lokal.
Lizenz
GPL-3.0. Siehe LICENSE.