Un plugin de Beekeeper Studio que convierte los planes de ejecución de consultas en un diagrama interactivo, para que encuentres fácilmente la parte lenta de una consulta.

- Diagrama del plan, coloreado según la parte del tiempo de ejecución (o del costo estimado) de cada paso. Los enlaces más anchos llevan más filas.
- Vista de tabla al estilo de
explain.depesz.com, ordenable por tiempo propio. - Detalles de cada paso: tiempo propio y total, filas reales vs. estimadas, loops, costos, condiciones, buffers, uso de memoria y disco, workers.
- Insights que señalan problemas comunes: escaneos completos que descartan la mayoría de las filas, tablas escaneadas una vez por cada fila de un join, sorts y hashes que se desbordan a disco, subconsultas dependientes, índices temporales que SQLite construye en cada ejecución, grandes errores de estimación de filas y más.
- Salida cruda del plan, lista para copiar o guardar.
Más en las capturas de pantalla de abajo.
Bases de datos compatibles
| Base de datos | Explain | Explain Analyze | Pegar un plan |
|---|---|---|---|
| PostgreSQL | Sí | Sí | JSON y texto de psql |
| MySQL | Sí | 8.0.18+, sentencias de lectura | Tree, JSON (ambas versiones) y la tabla clásica |
| MariaDB | Sí | 10.1+, sentencias de lectura | JSON y la tabla clásica |
| SQLite, LibSQL | Sí | No: SQLite no tiene tiempos por paso | El árbol del shell sqlite3, o las filas del resultado |
| SQL Server | Planeado |
Probado con PostgreSQL 12, 17 y 18; MySQL 5.7, 8.0.17, 8.0, 8.4 y 9.7; MariaDB 10.4, 10.6 y 11.4; y SQLite 3.16, 3.27 y 3.45. El plugin está construido sobre un modelo de plan neutral respecto a la base de datos, así que cada base de datos solo necesita un adaptador (consulta Agregar una base de datos).
Cómo usarlo
Hay varias formas de entrar:
- Editor de consultas: haz clic derecho en una consulta y elige Visualize Explain Plan. Se usa el texto seleccionado o la sentencia bajo el cursor.
- Menú de nueva pestaña: abre Explain Visualizer, escribe una sentencia y elige Explain o Explain Analyze.
- Cuadrícula de resultados: después de ejecutar EXPLAIN tú mismo, haz clic derecho en el plan y elige Visualize Explain Plan:
- un plan en una celda (
FORMAT JSONde PostgreSQL, tree o JSON de MySQL): haz clic derecho en la celda; - un plan repartido en filas (salida de texto de PostgreSQL): haz clic derecho en el encabezado de la columna
QUERY PLAN; - una tabla (
EXPLAINclásico de MySQL,EXPLAIN QUERY PLANde SQLite): selecciona todas sus filas y columnas y luego haz clic derecho en la selección. Para SQLite también funciona solo la columna de detalle, pero sin anidamiento.
- un plan en una celda (
- Pegar plan: pega la salida de EXPLAIN, por ejemplo de psql, del shell de mysql o sqlite3, o de un colega.

Explain y Explain Analyze
Explain le pide a la base de datos su plan sin ejecutar la sentencia. Siempre es seguro, así que se ejecuta automáticamente cuando la pestaña se abre desde el editor.
Explain Analyze ejecuta la sentencia y mide cada paso. Cómo se mantiene seguro depende de la base de datos (ver abajo). Si el texto del editor ya empieza con EXPLAIN ANALYZE (o su equivalente en la base de datos), el plugin lo usa; cuando la sentencia no se puede analizar, se hace solo un explain y el motivo se muestra debajo del editor.
PostgreSQL
El plugin envía EXPLAIN ANALYZE ...; ROLLBACK; en una sola solicitud, así que ambas sentencias comparten una transacción implícita y todo lo que la sentencia cambió se revierte. Eso incluye los SELECT simples, que pueden llamar funciones que escriben. Si la sentencia falla, la transacción se aborta sola y la conexión del pool queda limpia. Los efectos fuera de la transacción, como los valores de secuencias, no se deshacen.
Las sentencias que cambian datos (INSERT, UPDATE, DELETE, MERGE, CTEs que modifican datos…) solo se analizan después de una confirmación. El plugin lee la sentencia con un lexer que sigue las reglas propias de PostgreSQL para strings, comentarios y dollar quotes, y rechaza la entrada que contiene más de una sentencia.
En conexiones de solo lectura, Beekeeper Studio no permite el ROLLBACK, así que las sentencias que cambian datos solo se pueden explicar y Explain Analyze nunca se ejecuta automáticamente. Ahí no se detecta un SELECT que llama a una función que escribe, igual que al ejecutarlo en una pestaña de consulta.
El menú Options activa o desactiva BUFFERS, VERBOSE, SETTINGS y WAL. Las opciones que el servidor no soporta se deshabilitan. Las sentencias con parámetros $1 se explican con GENERIC_PLAN en PostgreSQL 16+.
MySQL y MariaDB
El plugin lee la versión del servidor y usa el formato de plan más reciente que tenga:
| Servidor | Explain | Explain Analyze |
|---|---|---|
| MySQL 8.0.18+ | EXPLAIN FORMAT=TREE |
EXPLAIN ANALYZE |
| MySQL 5.6 a 8.0.17 | EXPLAIN FORMAT=JSON |
No disponible |
| MariaDB 10.1+ | EXPLAIN FORMAT=JSON |
ANALYZE FORMAT=JSON |
| Servidores más antiguos | La tabla clásica de EXPLAIN |
No disponible |
Los UPDATE y DELETE de una sola tabla no tienen tree; el plugin recurre a FORMAT=JSON y, en MySQL 9, cuyo JSON tampoco puede mostrarlos, a la tabla clásica. TiDB se reconoce y se rechaza en lugar de leerse mal.
Explain Analyze solo ejecuta sentencias de lectura. A diferencia de PostgreSQL, no se puede confiar en que MySQL deshaga una solicitud fallida: enviada como START TRANSACTION; ...; ROLLBACK;, una sentencia que falle dejaría la transacción abierta en una conexión del pool que luego usan otras pestañas. Un SELECT que llama a una función almacenada que escribe hace commit de lo que hizo la función, igual que al ejecutarlo en una pestaña de consulta. En MariaDB, el modo de solo lectura de Beekeeper Studio rechaza la sentencia ANALYZE, así que Explain Analyze no está disponible en conexiones de solo lectura.
La sentencia se lee con las reglas de MySQL: comentarios # y -- que terminan solo en un salto de línea, escapes con backslash, strings "..." y comentarios /*! ... */, cuyo contenido MySQL ejecuta. NO_BACKSLASH_ESCAPES y ANSI_QUOTES cambian cómo terminan los strings, y una conexión del pool puede tener cualquiera de los dos, así que una sentencia tiene que leerse como una sola sentencia, y como lectura, bajo cada combinación de ellos. El texto que un servidor podría leer de más de una forma se rechaza en lugar de adivinarse: un comentario condicionado por versión /*!50700 ... */ o exclusivo de MariaDB /*M! ... */, un retorno de carro dentro de un comentario --, o un carácter NUL.
MariaDB mide el tiempo de cada tabla en vez de cada paso, así que el tiempo de un join o de una tabla temporal es lo que la consulta tardó además de las tablas que leyó.
SQLite
EXPLAIN QUERY PLAN muestra qué tablas se leen, en qué orden y con qué índice. SQLite no reporta costos, conteos de filas ni tiempos, así que el diagrama muestra los pasos y la vista de tabla sus detalles, y no hay Explain Analyze. Los insights señalan índices temporales que SQLite construye en cada ejecución, tablas escaneadas una vez por fila de un join o de una subconsulta correlacionada, y sorts en un B-tree temporal. Las conexiones LibSQL usan el mismo motor.
Beekeeper Studio divide el texto de SQLite en sentencias con su propio parser antes de ejecutarlas, así que una sentencia con un punto y coma dentro (en un string, nombre o comentario) se rechaza: ningún divisor puede entonces encontrar una segunda sentencia en ella.
Capturas de pantalla
Explain Analyze, con el paso lento seleccionado y un insight sobre él:

El mismo plan como tabla, ordenado por tiempo propio:

Una consulta con un CTE, un InitPlan y un SubPlan, cada uno como su propia rama:

El plugin sigue el tema de Beekeeper Studio:

Desarrollo
Requiere Node.js 22+ y 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 usa @beekeeperstudio/vite-plugin, que crea un symlink de esta carpeta en el directorio de plugins de Beekeeper Studio.
Por defecto, Beekeeper Studio solo carga sus propios plugins permitidos. Para cargar este, agrega lo siguiente a tu archivo de configuración de usuario y reinicia la app:
[pluginSystem]
disabled = false
Luego conéctate a una base de datos: Visualize Explain Plan aparece en el menú contextual del editor de consultas y Explain Visualizer en el menú de nueva pestaña.
Harness
dev/harness.html hace las veces de Beekeeper Studio: carga el plugin en un iframe y responde a sus solicitudes con los mismos mensajes que usa la app, ejecutando consultas en bases de datos reales. Es la forma más rápida de trabajar en la UI, probar cada forma de abrir la pestaña, cambiar de base de datos y cambiar de tema.
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 apunta el MariaDB del harness a otro servidor (si no, usa MYSQL_URL). SQLite no necesita nada: se llena una base de datos temporal desde scripts/fixtures/seed-sqlite.sql la primera vez, o SQLITE_PATH indica una.
Tests
tests/postgresparsea planes reales capturados de PostgreSQL 12, 17 y 18 (tests/fixtures/postgres). Cada fixture tiene el mismo plan en JSON y en texto, y los tests verifican que ambos produzcan el mismo árbol.tests/mysqlparsea planes capturados de MySQL 5.7, 8.0.17, 8.0, 8.4 y 9.7 y MariaDB 10.4, 10.6 y 11.4 (tests/fixtures/mysql). Cada fixture tiene todos los formatos que su servidor ofrece para una sentencia: tree,EXPLAIN ANALYZE, ambas versiones de JSON, elANALYZEde MariaDB y la tabla clásica. Los tests verifican que los formatos coincidan.tests/sqliteparsea filas deEXPLAIN QUERY PLANde SQLite 3.16 (el formato anterior a 3.24), 3.27 y 3.45 (tests/fixtures/sqlite).tests/integrationejecuta EXPLAIN en servidores reales: PostgreSQL cuandoPG_URLestá definido, MySQL o MariaDB cuandoMYSQL_URLestá definido, y SQLite siempre, mediante elnode:sqliteintegrado de Node.tests/statecubre el comportamiento de la pestaña con un host falso.
Para capturar fixtures de otra versión de servidor, llénalo con datos y ejecuta su script de captura:
# 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
Cómo funciona
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
Cada adaptador convierte la salida de EXPLAIN de su base de datos al modelo Plan en src/core/plan.ts. Un PlanNode tiene un nombre de operación, un kind (scan, index, join, filter, sort…), estimaciones opcionales (totalCost, estimatedRows), mediciones opcionales (actualRows, loops, tiempos), los derivados inclusiveTime, exclusiveTime y exclusiveCost, además de condiciones y propiedades agrupadas para el panel de detalles. Cada métrica es opcional y la UI solo muestra lo que ofrece cada motor: un plan de SQLite no tiene números, así que la tabla muestra los detalles de cada paso en lugar de los costos.
El tiempo propio depende del motor, así que lo calcula el adaptador. Las reglas de PostgreSQL están en src/engines/postgres/timing.ts: los tiempos por loop se multiplican por los loops, los workers paralelos se cuentan una vez, el tiempo de un CTE se carga a los CTE scans que lo leen y el tiempo de un InitPlan se carga al nodo que lo evalúa. MySQL también reporta tiempos por loop y usa el computeLoopTimings compartido (src/core/metrics.ts), con las subconsultas de la lista del select sumadas encima de la consulta, que no las incluye. MariaDB mide tablas y consultas completas, no pasos; computeMixedTimings completa el resto.
Los tres formatos de MySQL se leen con los mismos nombres de operación: el texto tree y la versión 2 de JSON listan los mismos iteradores, y el JSON por bloques de consulta (y la tabla clásica, reconstruida en él) se convierte en una cadena de joins en el orden de las tablas. Las reglas de insights trabajan sobre esos nombres, no sobre la salida nativa.
src/core/analysis.ts agrega lo que comparten todos los motores: porcentajes de tiempo y costo, los pasos más lentos e insights de errores de estimación de filas.
Agregar una base de datos
No hace falta cambiar nada fuera de la carpeta del motor y del registro.
- Crea
src/engines/<name>/con un objeto que implementeExplainEngine(src/engines/types.ts):connectionTypes: los tipos de conexión de Beekeeper Studio que maneja, yproductscuando cubre varios (MySQL y MariaDB).inspect(sql, server): divide y clasifica la sentencia (¿cambia datos?).src/engines/shared/lexer.tsrecibe un dialecto: estilos de comentario, comillas, placeholders.analyzePolicy(statement, context): si Explain Analyze puede ejecutarla, qué preguntar antes y qué mostrar debajo del editor.src/engines/shared/analyzePolicy.tstiene las políticas de rollback y de solo lectura.explain(request, runQuery): envía el SQL que necesite la base de datos y parsea el resultado. Puede llamar arunQueryvarias veces, por ejemplo para recurrir a un formato más antiguo.loadServerInfo(runQuery): la versión y cualquier otra cosa que el motor necesite saber (el SQL mode de MySQL). ExtiendeServerInfocon tus propios campos.canParse/parse: lee la salida pegada y los valores de la cuadrícula de resultados. El texto crudo que devuelve un parse debe volver a parsearse en el mismo plan: las pestañas restauradas se reconstruyen a partir de él.options: opciones que se muestran en el menú Options, cada una conunavailable(server)cuando necesita un servidor más reciente.
- Mapea los operadores nativos a
NodeKinds y completa las métricas que existan. Usa los helpers desrc/core/metrics.tspara costos y tiempos donde apliquen, y llama afinishPlan()desrc/core/analysis.tsal final. - Regístralo en
src/engines/registry.ts, agrega fixtures capturados de servidores reales (un script de captura enscripts/fixtures/) y tests como los demás.
Notas para SQL Server, que está planeado:
- El cliente de SQL Server de Beekeeper Studio divide una solicitud en
;y puede ejecutar cada sentencia en una conexión distinta del pool.SET SHOWPLAN_XML ONdebe ir solo en su batch y se aplica al siguiente batch en la misma conexión, así que un plan estimado necesita una nueva API del host que ejecute batches en una sola conexión. SET STATISTICS XML ONcon la sentencia en un solo batch (sin;) da el plan real, pero ejecuta la sentencia y devuelve sus filas al plugin.- El XML de plan pegado desde SSMS (
.sqlplan) no necesita soporte del host.
Publicación
- Ejecuta
bin/make-release.sh. Sugiere la siguiente versión a partir de los tags existentes, defineversionenmanifest.jsonypackage.json, hace commit y sube un tag correspondiente comov0.1.0. - El workflow de release compila, ejecuta los tests y crea un borrador de release en GitHub con
manifest.jsonyexplain-visualizer-<version>.zip. Publica el borrador.
yarn build && yarn package crea el mismo archivo localmente.
Licencia
GPL-3.0. Consulta LICENSE.