Hacer que SQL se ejecute es una cosa. Hacer que otros humanos lo lean es otra. Formatear es lo segundo, no lo primero. El hábito de formateo de la mayoría de desarrolladores es “presionar Tab manualmente para ajustar la sangría en el editor” — eso resuelve el 30% del problema. El 70% restante (diferencias de dialecto, capitalización de palabras clave, punto y coma dentro de cadenas, comentarios que se tragan, CTE no recursivo, subconsultas que no saben cuándo saltar de línea) no se resuelve con pulsaciones manuales de Tab.

El valor central de un formateador SQL no es “hacer que SELECT se vea bonito” — es hacer que las consultas sean legibles por humanos durante la revisión de código, el análisis post-mortem y la inspección de logs de consultas lentas. Eso es infraestructura, no embellecimiento. Este artículo cubre 8 foot-guns reales en formato “síntoma + porqué + cómo arreglar”, luego presenta el Piick SQL Formatter — 5 dialectos, 3 modos de capitalización, 4 modos de sangría, coma al inicio/final, resaltado de sintaxis, salto con clic a posición de error, 5 estadísticas, procesamiento 100% local, todo en 1 minuto.

Resumen en 30 segundos

  • Formateador SQL != embellecedor: el primero es infraestructura de legibilidad para auditorías de consultas / revisión de código / análisis de logs de consultas lentas; el segundo es solo “hacer que el código se vea ordenado”

  • El dialecto lo decide todo: el mismo SELECT * FROM "my column" es una cadena en MySQL, un identificador entre comillas en PostgreSQL y un error de sintaxis en T-SQL. Un formateador sin dialecto reescribirá y romperá cosas inevitablemente

  • 8 foot-guns comunes: desajuste de dialecto, capitalización mixta de palabras clave, punto y coma dentro de cadenas disparando splits falsos, formateador ingenuo tragándose comentarios, comillas de identificador reescritas automáticamente, formateo no recursivo de WITH CTE, todas las subconsultas en línea en vez de tablas derivadas con su propia línea, SQL minificado perdiendo toda estructura para depuración

  • Dirección de la solución: usa siempre un formateador con opciones de dialecto, nunca reescribas comillas automáticamente, trata siempre comentarios / CTEs / tablas derivadas como ciudadanos de primera clase

  • Usa el Piick SQL Formatter online para formatear / minificar / embellecer — 5 dialectos, resaltado de sintaxis, salto con clic a posición de error, procesamiento 100% local, seguro para pegar DDL de producción

8 Foot-Guns Comunes

Foot-Gun 1: Desajuste de Dialecto (Backtick de MySQL se ejecuta en PG y rompe)

Síntoma: Una consulta PostgreSQL, pasada por un formateador estilo MySQL, sale con todos los "my column" reescritos como `my column`. Pégala de vuelta en psql y obtienes ERROR: column "my column" does not exist (porque PG usa comillas dobles para identificadores, y los backticks no son sintaxis legal).

Por qué: El carácter de comilla de identificador de cada dialecto es diferente — MySQL usa ` backtick, PostgreSQL / SQLite / Standard SQL usan " comilla doble, T-SQL usa [ ] corchetes o comillas dobles. Un formateador ingenuo ve una cadena y la trata como cadena, sin considerar el contexto de dialecto, así que reescribe un identificador PG bajo las reglas de MySQL en un backtick.

Solución: El formateador DEBE tener un selector de dialecto, y el dialecto decide 2 cosas — (a) el conjunto de reconocimiento de palabras clave (~120 palabras reservadas, diferente por dialecto), y (b) el carácter de comilla de identificador. El Piick SQL Formatter cambia entre 5 dialectos; pega SQL PG y selecciona PostgreSQL, y el formateador no tocará tus comillas.

Escenario real: Al migrar PG → MySQL, una operación inversa ingenua reescribe el "my column" de PG al `my column` de MySQL, pero la columna JSONB de PG se convierte en JSON de MySQL, el SERIAL de PG se convierte en AUTO_INCREMENT de MySQL — eso no lo puede arreglar el formateador (tienes que reescribir el esquema manualmente). Pero los “errores de sintaxis causados por el formateador mismo al reescribir comillas” deben ser tolerancia cero.

Foot-Gun 2: Capitalización Mixta de Palabras Clave (Grep no encuentra cláusulas)

Síntoma: Durante la revisión de código, git blame muestra un SELECT como Select * From users Where id = 1. Quieres grepear todas las cláusulas WHERE en el código con la regex \bWHERE\b (case-sensitive) — cero coincidencias. Cambias a -i, y encuentras 70% where en minúscula y 30% WHERE en mayúscula, porque 5 plantillas de formateador usan UPPER, 3 usan lower, y el resto lo ignora.

Por qué: Cuando el formateador no aplica capitalización de palabras clave, los desarrolladores escriben lo que su config del editor (camelCase, snake_case) sugiere, y el código termina con 4 estilos de capitalización mezclados — Select / SELECT / select / sElEcT (sí, la gente realmente escribe eso). Las palabras clave SQL son case-insensitive, así que la semántica funciona, pero la revisión de código, grep y herramientas de análisis AST se rompen.

Solución: El formateador debería ofrecer una opción de capitalización de palabras clave (UPPER / lower / Preserve), el equipo debería bloquear un estilo en un archivo de config estilo ESLint, y CI debería correr el formateador para aplicarlo automáticamente. El Piick SQL Formatter cambia entre 3 modos de capitalización — elige UPPER y todas las palabras clave se vuelven SELECT FROM WHERE; elige Preserve y se mantiene la capitalización original (bueno para proyectos que ya tienen un estilo establecido).

Escenario real: Al aceptar un PR en un proyecto open-source, un nuevo colaborador mezcla capitalización; el revisor comenta “por favor corre el formateador antes de enviar”; el colaborador instala el prettier-plugin-sql recomendado del proyecto y lo corre, lo cual reformatea 200 líneas no relacionadas, y el revisor tiene que revisar de nuevo. La unificación de capitalización debería ocurrir una vez antes del commit, no repetidamente durante la revisión.

Foot-Gun 3: Punto y Coma Dentro de Cadenas Dispara Splits Falsos (SQL Entero se Despedaza)

Síntoma: Un INSERT VALUES contiene datos sucios con ; dentro de una cadena de entrada del usuario (por ejemplo notes = 'Error: connection refused; retry';). El formateador ingenuo divide por ;, generando 2 pseudo-sentencias; pégalo de vuelta para ejecutar, la segunda “sentencia” son caracteres basura y la base de datos lanza un error de sintaxis.

Por qué: El formateador ingenuo usa src.split(';') para dividir SQL multi-sentencia, ignorando completamente el estado de comillas — el ; dentro de una cadena es textualmente indistinguible de un terminador de sentencia. El enfoque correcto es que el tokenizer primero reconozca comillas / comentarios / anidamiento, luego divida por ;, cortando solo en estado no-cadena / no-comentario.

Solución: La primera puerta de cualquier herramienta de procesamiento SQL es el tokenizer. Un split(';') ingenuo funciona en un demo, pero se rompe en el momento en que llegan datos de producción (con entradas de usuario que contienen ;). El tokenizer del Piick SQL Formatter es a nivel de carácter, reconociendo '...', "..." (identificador PG), `...` (identificador MySQL), comentarios de bloque anidados /* ... */, comentarios de línea -- ..., y comentarios de línea MySQL # ... — dentro de todos estos contextos el ; nunca es un terminador de sentencia.

Escenario real: Un script de migración de datos exporta INSERTs desde CSV; el CSV contiene ; en entradas de usuario como dato de negocio legítimo (por ejemplo, campos de texto de sentencias SQL, campos de descripción CSV). El script ingenuo se rompe inmediatamente; el enfoque correcto es usar una herramienta con tokenizer para dividir, o usar el protocolo COPY (no ensambles INSERTs).

Foot-Gun 4: Comentarios Tragados en Silencio por Formateador Ingenuo (Condiciones WHERE Desaparecen)

Síntoma: SELECT * FROM users WHERE active = 1 -- solo usuarios activos — después de que el formateador ingenuo corre, el comentario desaparece, dejando SELECT * FROM users WHERE active = 1, que se ve bien. Pero revisas el git diff y ves que el comentario fue eliminado silenciosamente — el comentario era una explicación de negocio para “por qué esta cláusula WHERE está escrita así”, y una vez eliminado, 6 meses después no tienes idea de por qué active=1.

Por qué: Muchos formateadores fusionan los dos pasos “formatear = eliminar comentarios + re-distribuir tokens”. Los tokens de comentario no afectan la semántica, así que se desechan como basura. Pero los comentarios son evidencia clave de la evolución del esquema (“por qué este campo está hardcodeado a 1 en vez de config”, “por qué se une esta tabla deprecated”), y eliminarlos equivale a eliminar el historial del proyecto.

Solución: El formateador debe tratar los tokens de comentario como ciudadanos de primera clase, preservando posición y contenido; solo el modo minify puede eliminarlos (porque minify es para ejecución en producción, no para lectura humana). El Piick SQL Formatter preserva comentarios de línea -- y comentarios de bloque /* */ (incluyendo anidados) en modo formato, y solo elimina comentarios en modo “Minify”.

Escenario real: Durante el análisis de logs de consultas lentas, ves SELECT * FROM orders WHERE created_at < '2020-01-01' -- consulta temporal pre-archivo, eliminar después de limpieza; después de que el formateador corre una vez, el comentario desaparece; 6 meses después miras este SQL y no sabes “por qué created_at está hardcodeado a 2020-01-01 — ¿bug o consulta temporal?” — tienes que preguntar a un colega. Los comentarios son la máquina del tiempo del proyecto; eliminarlos equivale a eliminar el historial del proyecto.

Foot-Gun 5: Comillas de Identificador Reescritas Automáticamente (Portabilidad Cross-Dialect Roto)

Síntoma: El SELECT "userId" FROM "users" de PG se convierte en SELECT \userId` FROM `users“ (backticks MySQL) después de que el formateador ingenuo corre. Pégalo de vuelta en PG y obtienes un error de sintaxis — PG no reconoce backticks como comillas de identificador.

Por qué: El formateador ve una comilla doble y la reescribe bajo las “reglas de comilla de identificador” de su dialecto sin considerar qué tipo de comilla escribió originalmente el usuario. Si el usuario tenía comillas dobles PG / Standard / SQLite, reescribir a backticks es un error de sintaxis. Si el usuario tenía backticks MySQL, reescribir a comillas dobles también está mal en MySQL (las comillas dobles son cadenas en MySQL, no identificadores, a menos que el modo SQL ANSI_QUOTES esté activo).

Solución: El formateador debería por defecto preservar los caracteres de comilla de entrada y nunca reescribirlos activamente. Si el usuario genuinamente quiere reescribir el dialecto (por ejemplo, PG → MySQL), ese es el trabajo de otra herramienta — una herramienta de migración de esquema (Prisma / SQLAlchemy / Flyway), no el formateador. El Piick SQL Formatter preserva estrictamente los caracteres de comilla de entrada — las comillas que pegas son las comillas en la salida, nunca reescritas.

Escenario real: Un equipo de ciencia de datos exporta una consulta de PG a una herramienta de BI, y la herramienta de BI usa un motor compatible con MySQL. El científico de datos manualmente cambia SELECT "userId" a SELECT userId (quita las comillas, ya que minúscula no las necesita). Esto es juicio humano, no trabajo del formateador. Que el formateador rompa SQL ya correcto es un bug de lo más básico.

Foot-Gun 6: Formateo No Recursivo de WITH (CTE) (Múltiples CTEs se Acumulan en Una Línea)

Síntoma: WITH active_users AS (SELECT id FROM users WHERE active=1), recent_orders AS (SELECT user_id FROM orders WHERE created_at > NOW() - INTERVAL '7 days') SELECT COUNT(*) FROM active_users a JOIN recent_orders r ON a.id = r.user_id — después del formateador ingenuo, todos los nombres de CTE se quedan en la primera línea después de WITH, los bodies de CTE no están indentados, y se lee igual que la entrada sin formatear.

Por qué: WITH (CTE) es el mecanismo de nombrado de subconsultas introducido en el estándar SQL. Un WITH puede tener múltiples nombres de CTE (separados por comas), cada uno seguido por una definición (SELECT ...). Un formateador ingenuo distribuye “WITH … SELECT” como una línea y ni siquiera considera que CTE es una lista.

Solución: El formateador debe reconocer la lista de CTE, darle a cada nombre de CTE su propia línea, indentar el body 1 nivel (y el body del SELECT otro 1 nivel). Cuando múltiples CTEs anidan (el body de un CTE usa WITH de nuevo), recurrir por cada capa. El Piick SQL Formatter formatea WITH recursivamente — cada nombre de CTE en su propia línea + body indentado, CTEs anidados recurridos automáticamente.

Escenario real: Una consulta de analytics del equipo de datos típicamente encadena 5-10 CTEs en un pipeline. El formateador ingenuo la aplana en 3 líneas; el revisor no puede leer qué hace cada paso. Con formateo CTE recursivo, cada CTE es como una mini-función independiente, mejorando la legibilidad 10x.

Foot-Gun 7: Todas las Subconsultas en Línea (Tablas Derivadas Pierden su Estructura)

Síntoma: SELECT u.name, o.total FROM users u JOIN (SELECT user_id, SUM(amount) AS total FROM orders GROUP BY user_id) o ON u.id = o.user_id — el formateador ingenuo pone (SELECT user_id, ... GROUP BY user_id) en línea en la línea del JOIN, y el panel derecho no puede mostrar una sola línea de 80 caracteres.

Por qué: Las subconsultas tienen dos semánticas — subconsulta escalar (como parte de una expresión, por ejemplo, WHERE id IN (SELECT ...)) debería estar en línea porque es en sí misma un valor; tabla derivada (en la cláusula FROM, FROM (SELECT ...) AS t) debería estar en su propia línea porque es una tabla, y solo una línea independiente puede mostrar su estructura claramente. El formateador ingenuo no distingue y pone todo en línea.

Solución: El formateador debe distinguir subconsulta escalar vs tabla derivada — las tablas derivadas que aparecen en la cláusula FROM van en su propia línea + indentadas; las subconsultas escalares quedan en línea. El Piick SQL Formatter sigue esta regla — las tablas derivadas automáticamente obtienen su propia línea, las escalares quedan en línea.

Escenario real: En una consulta BI compleja, la cláusula FROM tiene 3-4 tablas derivadas (cada una una versión materializada de un CTE); después de poner en línea, todo el SQL se amontona más allá de 80 caracteres y tienes que hacer scroll manualmente para ver el resto. Después del formateo apropiado, cada tabla derivada parece una mini-tabla, y la legibilidad salta al instante.

Foot-Gun 8: SQL Minificado Pierde Legibilidad (Depuración no Puede Ver Estructura)

Síntoma: El SQL de producción es generado por ORM y se ejecuta bien después de la minificación. Pero cuando lo sacas del log de consultas lentas, obtienes un SQL de una sola línea de 5KB y no puedes grepear WHERE porque el SQL minificado no tiene saltos de línea y lee como 5000 caracteres en una línea al ojo humano.

Por qué: El propósito de la minificación es reducir bytes de transmisión de red + dejar que el parser de la base de datos parsee más rápido (aunque este beneficio es insignificante en DBs modernas), no para lectura humana. Los desarrolladores usan SQL minificado en producción y eso está bien, pero para depurar debes ver la versión pretty-printed para localizar problemas.

Solución: Los despliegues de producción usan SQL minificado (ahorra transmisión); la depuración usa SQL pretty-printed (legibilidad). Esto significa necesitas 2 versiones — el ORM genera minificado, la herramienta formateadora genera pretty-printed para depuración. El sistema de logs también debería formatear las consultas lentas antes de grabar, no grabar el minificado crudo. El Piick SQL Formatter soporta modo Minify + 5 estadísticas (sentencia / palabra clave / identificador / cadena / byte). Pega el SQL minificado de producción para pretty-print instantáneo + ver recuentos de sentencias y palabras clave.

Escenario real: Un DBA investiga una consulta lenta; el log da un SQL minificado generado por ORM; el DBA no puede ver qué JOIN es N+1. Tira este SQL al Piick SQL Formatter, selecciona Standard SQL + sangría de 2 espacios + coma al inicio, ve la estructura en 1 segundo, y localiza LEFT JOIN order_items oi ON o.id = oi.order_id WHERE oi.id IS NULL como la fuente del N+1.

Decisiones de Selección de Herramientas

Opción A: Plugin del Editor (SQLTools / DataGrip / DBeaver)

Mejor para: Personas que escriben SQL cada día y ya usan un IDE.

Pros: Formateo en tiempo real, sin salir del editor, integración automática con autocompletado de esquema. Contras: Solo SQL local, no puede sincronizar entre dialectos (tu IDE tiene un formateador MySQL, producción es PG, reescritura de comillas rompe), sin procesamiento por lotes (auto-formato en CI requiere setup separado).

Opción B: Paquete npm sql-formatter

Mejor para: Auto-formateo en CI / pipeline de build, proyectos con entorno Node.js.

Pros: Se puede integrar en CI, CI falla directamente con error, versión fijada para asegurar consistencia del equipo. Contras: ~200KB de dependencia + un montón de deps transitivas, las actualizaciones de versión ocasionalmente rompen SQL antiguo, requiere entorno Node (proyectos puramente frontend no pueden usarlo), sin resaltado de sintaxis + salto con clic a posición de error (salida de texto puro).

Opción C: Piick SQL Formatter

Mejor para: Procesamiento cross-dialect (el mismo SQL cambiando entre 5 dialectos para ver cuál es correcto), proyectos que no quieren paquetes npm (puro frontend), datos sensibles (DDL de producción, consultas con PII) que no deberían pasar por ningún servidor de terceros, DBAs / revisión de código en equipo necesitando vista estructural rápida.

Pros: Cero dependencia (procesamiento puro frontend token + AST), 5 dialectos (Standard / MySQL / PostgreSQL / SQLite / T-SQL), 3 modos de capitalización de palabras clave, 4 modos de sangría, coma al inicio/final, resaltado de sintaxis, salto con clic a posición de error, 5 estadísticas, procesamiento 100% local. Contras: No se puede integrar en CI (usa Opción B para eso), no puede auto-guardar (herramienta puramente frontend, hay que copiar manualmente).

Árbol de decisión:

  • Ya escribes SQL a nivel de proyecto → Opción A (plugin IDE)

  • Integración CI + entorno Node → Opción B (paquete npm)

  • Depuración cross-dialect + datos sensibles + no instalar nada → Opción C (Piick)

5 Escenarios Reales

Escenario 1: Migración de Datos Cross-Dialect (PG → MySQL)

El SELECT "userId", COUNT(*) FROM "users" WHERE "createdAt" > NOW() - INTERVAL '7 days' GROUP BY "userId" de PG — quieres ejecutarlo en MySQL (el entorno de test no tiene PG, usa MySQL temporalmente). Pasos:

  1. Usa el Piick SQL Formatter y selecciona PostgreSQL; primero formatea la versión PG (preservando comillas dobles + palabras clave PG)

  2. Ajusta manualmente elementos relacionados con esquema: "users" -> users (quita comillas, ya que minúscula no las necesita), INTERVAL '7 days' se convierte en DATE_SUB(NOW(), INTERVAL 7 DAY)

  3. Vuelve a Piick y selecciona MySQL; reformatea el SQL modificado (para que el reconocimiento de palabras clave de MySQL se alinee)

  4. Pega en MySQL para ejecutar

No hagas: Usar operaciones inversas ingenuas (el formateador reescribe automáticamente comillas dobles PG a backticks MySQL), el SQL tiene errores de sintaxis + errores de nombre de campo apilados, el tiempo de depuración se duplica.

Escenario 2: Revisión de Código no Puede Leer el Orden de JOIN

El PR del equipo tiene SELECT * FROM a LEFT JOIN b ON a.id = b.a_id INNER JOIN c ON b.c_id = c.id WHERE ..., el revisor lo mira durante 5 minutos sin entender la semántica del orden de JOIN. Usa el Piick SQL Formatter con Standard SQL + sangría de 4 espacios; pégalo y ve la estructura en 1 segundo:

SELECT

FROM

a

LEFT JOIN b

ON a.id = b.a_id

INNER JOIN c

ON b.c_id = c.id

WHERE

El revisor inmediatamente ve “primero LEFT JOIN b (puede expandir filas), luego INNER JOIN c (filtra filas donde b no coincidió)”, el problema de rendimiento es claro de un vistazo.

Escenario 3: SQL Minificado del Log de Consultas Lentas Restaurado

El DBA obtiene un SQL de una sola línea de 5KB generado por ORM, no puede ver el N+1. Pega en el Piick SQL Formatter, cambia de Minify a sangría de 4 espacios; ve la estructura en 1 segundo, localiza LEFT JOIN order_items oi ON o.id = oi.order_id WHERE oi.id IS NULL como la fuente del N+1. Combinado con el Regex Tester de Piick para grepear palabras clave en la salida EXPLAIN ANALYZE, localiza la subconsulta lenta específica.

Escenario 4: Pipeline CTE Anidado (Consulta Analytics del Equipo de Datos)

La consulta del equipo de datos encadena 8 CTEs en un pipeline; el editor ingenuo la aplana en one-liners de 80 caracteres, y el revisor no puede decir qué hace cada paso. Usa el Piick SQL Formatter para formateo WITH recursivo, cada CTE se convierte en una mini-función independiente:

WITH

step1_active_users AS (

SELECT

id

FROM

users

WHERE

active = 1

),

step2_recent_orders AS (

),

step3_joined AS (

SELECT

FROM

step1_active_users a

JOIN step2_recent_orders r

ON a.id = r.user_id

)

SELECT

FROM

step3_joined

El revisor puede leer el pipeline como código.

Escenario 5: DDL de Producción Nunca Sale del Navegador

El equipo de datos necesita sincronizar el CREATE TABLE users (...) de PG a staging; el DDL contiene campos sensibles (esquema de campo de ID de usuario encriptado). No puede usar un formateador online (miedo a que el SQL se suba); usa el Piick SQL Formatter para procesamiento puro-frontend, 100% local, pega, copia el resultado, cierra la página, ni rastro del SQL. Combinado con el URL Encoder/Decoder de Piick para manejar nombres de campos URL-encoded en DDL, y el JSON Formatter para defaults JSON / JSONB en DDL.

Prácticas Recomendadas

  • Usa siempre un formateador con opciones de dialecto; al menos cubre Standard / MySQL / PG / SQLite / T-SQL entre los 5 dialectos. Un formateador ingenuo inevitablemente romperá en proyectos cross-dialect

  • Preserva siempre los caracteres de comilla de entrada (comilla doble / backtick / corchete), nunca reescribas activamente. Reescribir es trabajo de la herramienta de migración de esquema, no del formateador

  • Trata los comentarios como ciudadanos de primera clase; el formateador no elimina comentarios — los comentarios son la máquina del tiempo del proyecto, la única evidencia 6 meses después que explica “por qué se escribió así”

  • Procesamiento recursivo para WITH (CTE); cada CTE obtiene su propia línea, body indentado, CTEs anidados recurren de nuevo

  • Distingue subconsulta escalar vs tabla derivada: escalares en línea (son valores), tablas derivadas en su propia línea (son tablas)

  • Producción usa minificado, depuración usa pretty-printed; 2 versiones cada una sirve a su propio propósito

  • Integración CI: SQL a nivel de proyecto usa el paquete npm sql-formatter (o equivalente) para formateo unificado, para evitar cambios de formato repetidos durante la revisión

  • DDL sensible usa una herramienta puramente frontend como Piick: 100% local, pega → copia → cierra página, sin rastro

  • Las estadísticas son esenciales: después de pegar, mira las 5 métricas (sentencias / palabras clave / identificadores / cadenas / bytes) para un chequeo rápido de la estructura SQL (por ejemplo, el recuento de identificadores se dispara de repente, lo que significa que FROM JOIN añadió un montón de tablas, posiblemente el ORM generó mal)

El formateo SQL es infraestructura de legibilidad para auditorías de consultas y revisión de código, no decoración. Guarda el Piick SQL Formatter en los marcadores — para depuración cross-dialect / restauración de logs de consultas lentas / investigación de DDL de producción, ábrelo y terminas en 1 minuto. Combinado con el Piick JSON Formatter para columnas JSON / JSONB, el Piick URL Encoder/Decoder para campos URL en cláusulas WHERE, el Piick Regex Tester para grepear palabras clave en la salida EXPLAIN, y el Piick Cron Generator para expresiones cron programando SQL — estas 5 herramientas juntas cubren los escenarios completos upstream/downstream de SQL.