Formatear, minificar, validar, analizar y traducir consultas SQL entre dialectos.
Por qué el formato de una consulta revela errores lógicos, en qué se separan los cinco motores más usados y qué protege realmente una sentencia preparada.
La condición WHERE status = 'active' OR role = 'admin' AND deleted_at IS NULL no filtra lo que parece: AND liga más fuerte que OR, así que el motor la interpreta como status = 'active' OR (role = 'admin' AND deleted_at IS NULL). Escribir cada predicado en su propia línea con paréntesis explícitos convierte ese error en algo visible durante la revisión. El mismo efecto aparece al mover un filtro sobre la tabla derecha de un LEFT JOIN desde ON hacia WHERE: las filas rellenadas con NULL no superan la comparación y el LEFT JOIN se degrada en un INNER JOIN silencioso. Y como SQL usa lógica de tres valores, columna <> NULL nunca es verdadero: hay que escribir IS NOT NULL.
El estándar ISO/IEC 9075 define el núcleo, pero cada motor añade su propia sintaxis. Para citar identificadores con espacios o palabras reservadas, PostgreSQL y Oracle usan comillas dobles, MySQL acentos graves y SQL Server corchetes. La paginación es LIMIT 10 OFFSET 20 en PostgreSQL, MySQL y SQLite; OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY en SQL Server y en Oracle 12c o superior; y TOP 10, sin desplazamiento, en las versiones antiguas de SQL Server. La concatenación es || en PostgreSQL, Oracle y SQLite, mientras que MySQL exige CONCAT() porque allí || significa OR lógico salvo con el modo PIPES_AS_CONCAT. Además PostgreSQL pliega los identificadores sin comillas a minúsculas y Oracle a mayúsculas.
Una sentencia preparada manda primero la plantilla al motor, que compila el plan, y después los valores por un canal aparte: un parámetro nunca puede convertirse en sintaxis ejecutable. Los marcadores cambian según el motor: $1 y $2 en PostgreSQL, ? en MySQL y SQLite, @nombre en SQL Server y :nombre en Oracle. Lo que los parámetros no cubren son los identificadores: no se puede parametrizar un nombre de tabla, una columna ni la dirección de un ORDER BY, así que un ORDER BY armado por concatenación sigue siendo inyectable y se resuelve con una lista blanca. Tampoco sirven de nada si el valor termina concatenado dentro de un EXECUTE o de sp_executesql.
Lo más frecuente es envolver la columna en una función: WHERE DATE(created_at) = '2026-01-01' o WHERE UPPER(email) = ... impiden usar un índice B-tree normal, porque el índice guarda el valor de la columna y no el de la expresión. La solución es reescribir el predicado como un rango sobre la columna desnuda o crear un índice por expresión. También rompen el índice la comparación entre tipos distintos, que fuerza una conversión implícita, y usar un índice compuesto (a, b, c) sin filtrar por su prefijo izquierdo. EXPLAIN ANALYZE muestra qué camino eligió el planificador.
El analizador sintáctico descarta los espacios en blanco, así que el tiempo de ejecución no cambia. Sí importa en un caso concreto: SQL Server y Oracle identifican las entradas de la caché de planes mediante un hash del texto literal de la sentencia, de modo que la misma consulta con distinta indentación ocupa entradas separadas. Minificar también ayuda cuando la consulta tiene que caber en una sola línea dentro de un archivo de configuración, un log estructurado o un campo JSON.
Por legibilidad, cuando el mismo cálculo intermedio se usa varias veces o cuando hace falta recursión con WITH RECURSIVE, la única forma de recorrer jerarquías. Ojo con el rendimiento: hasta PostgreSQL 11 toda CTE se materializaba y actuaba como barrera para el optimizador; desde la versión 12 las CTE no recursivas usadas una sola vez se integran en la consulta principal, salvo que se escriba AS MATERIALIZED. MySQL solo admite CTE desde la versión 8.0.
Inspeccioná el contenido de columnas JSON y JSONB o respuestas de API.
Abrir herramienta →DATAPasá el resultado de un SELECT a CSV o convertí CSV en filas para un INSERT.
Abrir herramienta →DEVDatasets sintéticos para poblar tablas y medir planes de ejecución.
Abrir herramienta →