¿Tiene que ocuparse del formato Base64? Entonces esta página es perfecta para Ud. Utilice nuestra práctica herramienta en línea para codificar o decodificar sus datos.

Codificación Base64 en SQL: una guía completa

De vez en cuando la base de datos tiene que hablar con el mundo exterior, y el mundo exterior no siempre habla bytes. Una API quiere tu logo dentro de una cadena JSON. Una exportación de configuración quiere un secreto que quepa en una línea de YAML sin comillas ni barras invertidas. Un script de mantenimiento quiere enviar un archivo a través de un sistema que solo lleva texto. Ese es el momento en que tus datos se ponen un disfraz de letras, y el nombre del disfraz es base64.

El formato en sí ya está cubierto en la página de inicio (64 caracteres imprimibles, cada cuatro de ellos representan tres bytes de entrada, hasta dos signos = de padding en el grupo final), así que este artículo se salta esa clase y va directo al trabajo de máquina. Dos cosas para aferrarte: codificar es la dirección en la que los datos se hacen más grandes, así que los anchos de columna y los límites de paquete sienten cada byte de ello, y los codificadores de esta familia SQL discrepan sobre las dos cosas que más cuestan de deshacer después: qué bytes leen cuando tu columna guarda texto, y dónde meten los saltos de línea en lo que escriben.

La tabla rápida de codificadores

Quiénes están de guardia, qué comen y dónde rompen su salida. Las dos últimas columnas son las que muerden, porque una cadena llena de saltos de línea no invitados y una cadena con un alfabeto distinto son ambas cadenas base64 perfectamente válidas que tu consumidor de todas formas rechazará:

Dialecto La llamada Tipo de entrada ¿Envuelve a 76? Opción URL-safe Desde cuándo
MySQL 8.x / MariaDB 10.x TO_BASE64(str) cadena (se aplica el conjunto de caracteres) ninguna MySQL 5.6 (2013)
PostgreSQL encode(bytea, 'base64') bytea sí, solo LF ninguna 7.2 (2002)
SQLite (CLI 3.41+) base64(blob) BLOB sí, a 72 ninguna 3.41.0 (2023)
DuckDB to_base64(blob) BLOB no ninguna versiones modernas
ClickHouse 18.16+ base64Encode(x) cualquier cosa, cast a String no base64URLEncode() 18.16 (2018)
SQL Server 2025+ BASE64_ENCODE(bin [, url_safe]) varbinary no segundo argumento 2025
Oracle UTL_ENCODE.BASE64_ENCODE(raw) RAW no ninguna era 9i
Snowflake BASE64_ENCODE(binary) BINARY no ninguna versiones actuales

Lee la tabla de izquierda a derecha y el trabajo cae en dos decisiones. Primero, cómo llegan tus bytes: la columna de tipo de entrada es donde nacen las sorpresas de conjunto de caracteres, porque "el mismo texto" son bytes distintos bajo colaciones distintas. Segundo, qué sale del otro lado: la columna de envoltura decide si tu resultado es una línea plana o un poema con un salto de línea cada 76 caracteres, y la columna URL-safe decide si puedes poner el resultado en un enlace en absoluto.

Primero, decide a qué bytes te refieres

Un codificador empaqueta bytes, pero tu columna suele guardar letras, y las letras solo son bytes si dices qué alfabeto de bytes. El TO_BASE64() de MySQL lee su argumento en el conjunto de caracteres de la conexión, que es una comodidad hasta que no lo es: el mismo 'héllo' viaja como base64 distinto bajo un cliente latin1 y un cliente utf8mb4. Cuando te refieres a los bytes exactos tal como están guardados, fíjalos primero con un cast binario:

SELECT TO_BASE64('hello') AS from_text;
SELECT TO_BASE64(CAST('héllo' AS BINARY)) AS utf8_bytes;

La segunda fila vuelve como aMOpbGxv, y los dos bytes hex C3 A9 del medio son la forma de escribir é en UTF-8. PostgreSQL es más estricto de entrada: encode() se niega a mirar nada que no sea bytea, así que un valor de texto primero debe nombrar su codificación, mientras que los bytes en bruto pueden llegar como literal hex:

SELECT encode(convert_to('héllo', 'UTF8'), 'base64') AS text_as_utf8;
SELECT encode('\x68656c6c6f'::bytea, 'base64') AS raw_bytes;

Cada otro dialecto tiene su propia puerta de entrada a la misma idea, y todos se reducen a "consigue los bytes y luego empaquetarlos":

Dialecto De texto a bytes La llamada del codificador
T-SQL CAST('héllo' AS VARBINARY(8000)) vía la colación de la columna BASE64_ENCODE(bin)
Snowflake TO_BINARY('héllo', 'UTF-8') BASE64_ENCODE(binary)
Oracle UTL_RAW.CAST_TO_RAW('héllo') UTL_ENCODE.BASE64_ENCODE(raw)
DuckDB encode('héllo') da un BLOB to_base64(blob)
SQLite CLI un literal BLOB como X'68656C6C6F' base64(blob)

La regla práctica es la misma que del lado de la decodificación: decide el conjunto de caracteres antes de codificar, escríbelo en la consulta como literal, y pasa un payload acentuado por todo el pipeline antes de fiarte de la columna. Un héllo atrapa cada colación equivocada, y no cuesta nada.

Luego, observa lo que sale

Una vez empaquetados los bytes, los codificadores se separan por los saltos de línea. Tres de ellos envuelven la salida: MySQL y PostgreSQL a los 76 caracteres, el hábito del correo, y la CLI de SQLite a 72; los demás devuelven una línea plana sin importar cuánto se alargue. La diferencia es fácil de pasar por alto y cara de encontrar, porque un campo base64 con saltos de línea ocultos es un campo que rompe un parser JSON a medio parsear:

SELECT LENGTH(TO_BASE64(REPEAT('x', 300))) AS wrapped,
      LENGTH(REPLACE(TO_BASE64(REPEAT('x', 300)), '\n', '')) AS flat;

Trescientos bytes de entrada vuelven como 400 caracteres base64, y la misma llamada mide 405 porque cinco saltos de línea montaron gratis. La aritmética detrás es lo bastante pequeña para llevártela en la cabeza: la longitud plana es la longitud de entrada dividida entre tres, redondeado hacia arriba, por cuatro. Si tu codificador envuelve, suma un salto de línea entre líneas de 76 caracteres, que es la longitud plana dividida entre 76 redondeado hacia arriba, menos uno. Trescientos bytes: 400 plano, 405 envuelto. Ciento once bytes: 148 plano, 149 envuelto. Un salto de línea más del presupuesto es suficiente para que una columna VARCHAR(500) empiece a truncar en silencio un payload VARCHAR(480).

Dos consecuencias que vale la pena anotar. Dimensiona las columnas de texto para la longitud plana más un poco de holgura si el escritor puede envolver, o prohíbe el envoltorio en el escritor y dimensiona para la forma plana. Y recuerda que el límite contra el que pelea tu resultado es el límite de la cadena, no de los bytes: en MySQL el texto envuelto cuenta contra max_allowed_packet (64 MB por defecto en MySQL 8), así que una foto de 50 megabytes codificada a unos 67 megabytes de letras no cabe en el paquete por defecto aunque el archivo en bruto sí.

Base64 URL-safe: el alfabeto viajero

La sección 5 del RFC 4648 definió un segundo alfabeto para base64 porque el original tiene dos caracteres con trabajo en la sintaxis de URLs. El signo más suma parámetros de consulta, la barra separa segmentos de ruta, y el signo de igual del padding se percent-codifica en el momento en que se topa con una cadena de consulta. La variante URL-safe cambia + por - y / por _, y la especificación JWT, por encima de eso, se quita el padding por completo, de modo que un token puede sentarse en un enlace, un segmento de ruta o un nombre de archivo sin un solo signo de porcentaje.

Solo un dialecto de esta familia trae el interruptor de forma nativa. El BASE64_ENCODE() de SQL Server 2025 toma un segundo argumento opcional, y con él activado, el resultado usa - y _ y se salta el padding:

SELECT BASE64_ENCODE(0xCAFECAFE) AS standard;
SELECT BASE64_ENCODE(0xCAFECAFE, TRUE) AS url_safe;

Los mismos cuatro bytes vuelven como yv7K/g== y yv7K_g. ClickHouse mantiene las variantes como funciones separadas, y su forma URL-safe también se quita el padding:

SELECT base64URLEncode('https://clickhouse.com') AS url_safe;

que llega como aHR0cHM6Ly9jbGlja2hvdXNlLmNvbQ, con los dos signos de padding de la forma estándar recortados. En todas partes, la receta es dos traducciones de caracteres y un recorte, y vale la pena escribirla una vez como función de base de datos porque cada pipeline de tokens la necesita. En PostgreSQL se lee así:

SELECT rtrim(replace(replace(
        encode(convert_to('https://clickhouse.com', 'UTF8'), 'base64'),
        '+', '-'),
      '/', '_'),
      '=') AS url_safe;

Traduce + a -, traduce / a _, recorta el padding final, hecho. Una advertencia para la tribu de SQL Server: la salida url_safe no es lo que los propios decodificadores base64 XML y JSON del servidor esperan, así que una columna empaquetada en forma URL-safe para el mundo exterior no se desempaquetará dentro de la base de datos con las funciones integradas. Ten presente al público antes de elegir el alfabeto.

JWTs: acuñar tokens desde la base de datos

Lo más interesante que puedes construir con el codificador es un JSON Web Token, porque un JWT no es más que tres piezas base64 en fila: una cabecera y un payload, ambos objetos JSON empaquetados en URL-safe sin padding, y una firma computada sobre las dos primeras. Cuando un trabajo por lotes necesita acuñar tokens (sembrar un entorno de pruebas, regenerar credenciales API caducadas, construir un feed de auditoría), toda la ceremonia cabe en una sola consulta de PostgreSQL si aceptas pgcrypto para el HMAC (actívalo una vez con CREATE EXTENSION IF NOT EXISTS pgcrypto;):

WITH head AS (
  SELECT encode(convert_to('{"alg":"HS256","typ":"JWT"}', 'UTF8'), 'base64') AS h
),
body AS (
  SELECT encode(convert_to('{"sub":"1234567890","name":"Dev User"}', 'UTF8'), 'base64') AS p
),
joined AS (
  SELECT rtrim(replace(replace(h, '+', '-'), '/', '_'), '=') AS h64u,
         rtrim(replace(replace(p, '+', '-'), '/', '_'), '=') AS p64u
  FROM head, body
)
SELECT h64u || '.' || p64u || '.' ||
       rtrim(replace(replace(
         encode(hmac((h64u || '.' || p64u)::bytea, 'sql-secret-key'::bytea, 'sha256'), 'base64'),
         '+', '-'),
         '/', '_'),
       '=') AS token
FROM joined;

Cada paso es uno de los movimientos que este artículo ya ha mostrado: empaquetar el JSON como base64, darle la forma del alfabeto URL-safe sin padding, luego firmar las dos primeras piezas y darle a la firma la misma forma. Para el JSON de arriba y el secreto sql-secret-key, el resultado es eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkRldiBVc2VyIn0.7_3VdmM8vH0L2bRBNqXXQXzZIR1l8T4_S4QxPPNBEFM, un token que cualquier inspector HS256 acepta. Las advertencias merecen tanto tiempo de antena como el truco: solo cubre algoritmos HMAC (HS256, HS384, HS512), mete un secreto compartido dentro de una sentencia de base de datos, y está hecho para trabajo por lotes y auditoría, no para un servicio de tokens de producción. El lado de la verificación, donde demuestras ese token contra su secreto, es trabajo de la capa de aplicación o de la comprobación de firma del artículo de decodificación.

Imágenes y archivos en una columna de texto

La razón más común para codificar en SQL es un archivo que tiene que viajar como texto: una API que incrusta la imagen en vez de referenciarla, una exportación para un sistema que no lleva binarios, un script de semilla que recrea una base de datos en un servidor nuevo. DuckDB hace el viaje de ida y vuelta casi trivial, porque lee archivos en BLOBs a través de una función de tabla que acepta patrones glob, y el codificador aplana lo que llega:

SELECT filename, to_base64(content) AS b64
FROM read_blob('/data/pics/*.png');

Una fila por archivo, una cadena base64 plana por fila, sin envoltorio que quitar, aunque los habituales uno o dos signos de padding viajan de cola al final. Escribe el resultado en una columna de texto y las imágenes son portables por cualquier canal que mueva texto. Luego ten la conversación del coste con honestidad: una foto de 1 megabyte llega como unos 1,33 megabytes de letras, y desde entonces cada escaneo, ordenación y entrada de índice pagan ese precio. Si controlas el esquema, el diseño mejor es una columna BLOB más un encode en la frontera de la API, donde solo los bytes que de verdad salen del edificio se visten.

Tráfico HTTP, JSON y de API

Las cabeceras y los payloads son donde base64 hace su trabajo silencioso de todos los días. Una cabecera Basic auth es el prefijo literal Basic seguido del base64 de username:password, y construir una en SQL es una concatenación más un encode:

SELECT 'Basic ' || encode(convert_to('alice' || ':' || 's3cret', 'UTF8'), 'base64') AS header;

que construye Basic YWxpY2U6czNjcmV0, la cabecera exacta que enviaría un cliente. Úsala para generar los fixtures contra los que comparan tus pruebas de integración, o para normalizar una columna de cabeceras guardadas antes de auditarlas. Del lado JSON, MySQL puede empaquetar un campo y meterlo dentro de un documento en una sola expresión, sin código de aplicación en el bucle:

SELECT JSON_OBJECT('img', TO_BASE64(CAST('hello file' AS BINARY))) AS doc;

El resultado es {"img": "aGVsbG8gZmlsZQ=="}, un payload listo para enviar. La misma forma funciona para certificados, claves públicas y cualquier otro archivo que tu API decidió incrustar, y es la dirección que importa cuando eres tú quien produce el tráfico, no quien decodifica el de otro.

Archivos de configuración, secretos y variables de entorno

Un hábito de exportación se merece su propio párrafo porque está en todas partes: el secreto guardado como base64 en una tabla de configuración. Kubernetes mantuvo vivo el hábito, donde los valores de secreto son base64 en reposo para que quepan en una línea de YAML sin comillas, sin saltos de línea y sin barras invertidas, y cada sistema de configuración de la casa que se topó con un pipeline de Kubernetes lo adoptó. La dirección de empaquetar es un encode por valor, con el cast binario haciendo el trabajo del conjunto de caracteres para que el texto exportado sea exactamente los bytes guardados:

SELECT name, TO_BASE64(CAST(value AS BINARY)) AS for_config
FROM app_config
WHERE name LIKE '%_secret%';

Cada valor de hasta 57 bytes sale como una cadena plana, lista para pegar; lo más largo necesita un REPLACE() para quitar las líneas de envoltorio antes de ir al YAML, y el nuevo entorno lo decodifica de vuelta del otro lado. Trata el resultado con el cuidado que se merece: acabas de convertir una columna de secretos guardados en una columna de secretos guardados que cualquier humano puede leer en unos diez segundos, y el hábito de base64-en-configuración recibe una segunda mirada en el momento en que estás de pie tan cerca del texto en claro. Base64 es un transporte, no una bóveda. Si el entorno tiene un almacén de secretos de verdad, la columna base64 está a una migración de distancia de ese.

El correo y el hábito de los 76 caracteres

El envoltorio de 76 caracteres es más antiguo que cada base de datos de esta página. MIME, el conjunto de estándares que deja que el correo lleve adjuntos binarios (RFC 2045, sección 6.8, 1996), envuelve la salida base64 a los 76 caracteres y termina cada línea con un retorno de carro y un salto de línea, porque la vieja red de correo no podía confiarse con líneas más largas. Tres codificadores de aquí heredaron el envoltorio como su defecto (MySQL, PostgreSQL, la CLI de SQLite), que es un regalo para todo lo que acabó en un correo y una trampa para todo lo que no. Y lo heredaron a medias: PostgreSQL termina sus líneas con un salto de línea suelto, no con el retorno de carro y salto de línea que especifica el estándar MIME, así que una salida que debería pegar en un adjunto de correo de verdad necesita una pasada más:

WITH t AS (
 SELECT replace(encode(attachment::bytea, 'base64'), chr(10), '') AS s
 FROM email_outbox
)
SELECT regexp_replace(s, '(.{1,76})', '\1' || chr(13) || chr(10), 'g') AS mime_ready
FROM t;

Quita los saltos de línea que PostgreSQL ya añadió, luego vuelve a envolver a 76 con un CRLF completo después de cada línea, y la cadena queda MIME-correcta en una sola expresión. Pasa trescientos bytes por ella y los 400 caracteres que empaquetaste se vuelven 412: seis líneas envueltas, seis pares CRLF, doce caracteres de ceremonia de transporte. La misma forma de problema aparece con los bloques PEM, que envuelven a 64 en vez de 76, y con las APIs que no quieren ningún envoltorio porque su parser JSON no va a toparse con un salto de línea a mitad de un campo. La regla para todos: descubre qué contrato firmó el consumidor antes de codificar, porque re-envolver una columna de base64 guardado es una migración, no una consulta.

Trampas: dónde los codificadores te mienten

Cada trampa de esta lista es una que pone un dialecto específico, no una que pone base64, y cada una tiene al menos un codebase que la encontró en producción:

  • El envoltorio no pedido. MySQL y PostgreSQL envuelven su salida a 76 por defecto, la CLI de SQLite a 72, y nadie en tu consulta lo pidió. El campo base64 de tu JSON ahora contiene saltos de línea, y el consumidor que acepta el formato en la especificación lo rechaza en la vida real. La forma plana es un REPLACE() sobre el carácter de salto de línea, aplicado del lado del escritor para que la columna guarde lo que el lector quiere.
  • El resbalón del conjunto de caracteres. TO_BASE64('héllo') sin el cast binario codifica lo que el conjunto de caracteres de la conexión cree que son las letras, y un cliente latin1 y un cliente utf8mb4 creen cosas distintas. El mismo texto de consulta, dos resultados base64 distintos, y el equivocado se decodifica en mojibake que nadie remonta hasta el codificador. El cast binario, o un convert_to() explícito, es la única versión honesta de la consulta.
  • La puerta BLOB. El to_base64() de DuckDB quiere un BLOB: dale texto y hace el cast implícito, así que una columna varchar con una codificación no UTF-8 puede codificar en silencio los bytes equivocados. La ruta honesta es to_base64(encode(...)), que es por qué los ejemplos de esta página siempre muestran el par para entrada de texto.
  • El cambio de espacios en blanco de 26.7. El base64Decode() y el base64URLDecode() de ClickHouse fueron estrictos con la entrada durante años, pero desde 26.7 ignoran los espacios en blanco (espacio, tabulador, salto de línea, retorno de carro, salto de formulario) en vez de rechazarlos, así que un script que antes daba error en una columna envuelta ahora tiene éxito en silencio, que es su propio tipo de regresión. Comprueba la versión del servidor antes de fiarte de la decodificación.
  • El límite de 6000 bytes. El BASE64_ENCODE() de SQL Server devuelve varchar(8000) cuando la entrada es un varbinary(n) con n de 6000 o menos y varchar(max) por encima; el mapeo se basa en el tamaño declarado, no en el valor, así que una columna varbinary(8000) devuelve varchar(max) incluso a tres bytes. La salida url_safe de la misma función, mientras tanto, no es legible por los propios decodificadores base64 XML y JSON del servidor, que esperan el alfabeto estándar con padding. Elige la variante para el público que la va a leer.
  • El RAW de 2000 bytes. En Oracle, un valor RAW en una sentencia SQL plana llega como máximo a en 2000 bytes. Como el codificador toma RAW y devuelve RAW, un encode de una sola sentencia solo puede aceptar unos 1500 bytes de entrada (sus 2000 caracteres de salida cabrían, más no), y un decode de una sola sentencia solo puede aceptar 2000 caracteres de base64. Los payloads más grandes se mueven a PL/SQL, donde una variable RAW guarda 32767 bytes y una sola llamada carga la mayor parte, y solo los payloads cuya salida base64 excedería ese techo necesitan el bucle de trozos, que data del límite de los 90 que nunca se movió.
  • El impuesto de paquete. MySQL cuenta la cadena codificada contra max_allowed_packet, no los bytes en bruto. Una foto que cabe en la tabla por amplio margen puede desbordar el paquete una vez que es un 33 por ciento más grande y envuelta, y el modo de fallo es un valor truncado o una NULL que parece una corrupción de datos. Comprueba el límite en la misma respiración que el ancho de la columna.
  • El salto de línea final. El base64() de la CLI de SQLite termina su última línea con un salto de línea, el hábito de final de línea aplicado a la línea final también. Pega la salida de la shell en un campo JSON y has enviado una cadena base64 con un salto de línea dentro, el envoltorio no pedido con otro sombrero.
  • La suposición de alfabeto. Un consumidor construido para el alfabeto estándar se topa con tu salida URL-safe (o viceversa) y ve caracteres que no conoce. La mayoría de los decodificadores fallan a gritos con el guion bajo; unos pocos fallan en silencio saltándolo. Documenta el alfabeto de cada columna base64 en el comentario del esquema, porque el siguiente desarrollador no recordará qué pipeline de tokens escribió la fila.

Cuando el tamaño de verdad importa

La aritmética de tamaño es la longitud plana más cualquier envoltorio que tu codificador añada, y la página de inicio hace la derivación completa de la proporción. Lo que vale la pena aquí es recorrer dónde deja de ser una curiosidad el número. Una columna VARCHAR dimensionada al recuento de bytes de la entrada trunca la salida en silencio la primera vez que el payload es lo bastante largo como para necesitar holgura extra, porque tres bytes de entrada cuestan cuatro caracteres. Un índice sobre una columna de texto base64 paga el impuesto dos veces: una en almacenamiento y otra en cada comparación, porque las entradas del índice son las letras envueltas, no los bytes. El max_allowed_packet de MySQL y el techo de bytea de 1 GB de PostgreSQL son los dos muros con los que casi todo el mundo se tropieza primero, y ambos se comprueban contra el texto, que es el lado grande del intercambio. La respuesta de diseño rara vez es elegir un codificador distinto (solo hay un base64); es elegir dónde pasa la codificación. Columna BLOB, columna de hash para búsquedas, encode en la frontera: el base64 existe solo en el tráfico, donde le toca estar.

Seguridad: lo que base64 no es

Base64 no es cifrado, y el único hábito que necesita decirse en voz alta es el de la tabla de configuración de antes: un secreto guardado como base64 es un secreto guardado en otra fuente tipográfica. La transformación es una biyección sin clave, reversible por cada lenguaje de programación de la tierra en una llamada a una función, y su único efecto real es mantener el valor en una línea de YAML. Si el modelo de amenazas incluye a otro usuario de esta base de datos, otro servicio que lee la exportación, o un log que capturó la fila, base64 aporta exactamente cero a la defensa. Ofusca el valor ante el ojo humano durante unos segundos, que es por qué se siente como protección en una revisión de código y por qué falla en un incidente. Cifra lo que debe ser secreto, cífralo con una clave que alguien pueda de verdad mantener secreta, y deja que base64 haga el trabajo en el que es bueno: mover bytes por un canal que solo lleva texto.

Cuándo aprendió cada dialecto a envolver

Las notas de lanzamiento cuentan la misma historia que el lado de la decodificación, solo que las letras van en la otra dirección, y el calendario dice algo sobre cada motor:

2002. PostgreSQL 7.2 lista base64 como un formato de primera clase de encode() y decode(), contemporáneo del UTL_ENCODE de Oracle en la era 9i, y el mecanismo base64 más antiguo de esta familia por un margen corto. Una base de datos con un tipo binario de verdad y un argumento de formato llegó temprano, porque la respuesta estaba a un valor de enum de distancia.

Principios de los 2000. El paquete UTL_ENCODE de Oracle sale en la era 9i con BASE64_ENCODE() junto a sus hermanos de cabecera MIME, quoted-printable y uuecode. Entra RAW y sale RAW, y un cuarto de siglo después el paquete no ha cambiado de opinión.

2013. MySQL 5.6 añade TO_BASE64() y FROM_BASE64() como un par a juego, y MariaDB 10.0 hereda ambos. El contrato del par no se ha movido desde entonces: líneas de 76 caracteres en la salida, tolerancia a espacios en blanco en la entrada.

2018. ClickHouse 18.16 (diciembre de 2018) lanza base64Encode() y base64Decode() junto con el alias al estilo MySQL, porque el mundo columnar estaba importando cargas de trabajo cuyos esquemas de log ya llevaban base64 dentro.

2023. SQLite 3.41.0 añade base64() a la shell de línea de comandos como una función definida por la aplicación. La biblioteca central no recibe nada, como es su costumbre; la shell, donde los humanos de verdad meten el dedo en los archivos SQLite, recibe la herramienta.

2025. SQL Server 2025, que se convirtió en disponibilidad general en noviembre de 2025, por fin lanza BASE64_ENCODE() y BASE64_DECODE(), treinta y seis años después de que el producto saliera al mercado y una generación después de que sus usuarios se aprendieran de memoria la solución alternativa XML.

El patrón es el mismo con el que termina el artículo de decodificación, espejado: los motores con un tipo binario de verdad y un argumento de formato recibieron base64 el día en que la necesidad fue evidente, y los motores donde todo es cadena lo programaron para después.

Rarezas que conviene conocer

  • El base64() de la CLI de SQLite es el camaleón de la familia, y en la dirección de codificación muestra el truco mejor: dale un BLOB y te devuelve texto envuelto con un salto de línea final, dale texto y te devuelve un BLOB. Un nombre, dos trabajos, elegidos por el tipo del argumento, y ningún otro codificador de esta familia lo hará.
  • La familia base64Decode() de ClickHouse se volvió indulgente en 26.7: los espacios en blanco de la entrada ahora se ignoran en vez de rechazarse, así que la misma consulta sobre una columna envuelta falla en el servidor viejo y devuelve un valor en silencio en el nuevo. El decodificador no se rompió; se relajó, que es de alguna manera más difícil de depurar.
  • PostgreSQL envuelve a los 76 caracteres exactamente como el estándar MIME de 1996, excepto que termina las líneas con un salto de línea suelto en vez del retorno de carro y salto de línea del estándar. Veintipocos años después de la especificación, un carácter menos por línea, y la rebelión es invisible a menos que hagas un diff de los bytes.
  • En el cliente mysql, los bytes que codificas se imprimen bien como texto base64, pero en el momento en que miras la columna en bruto con CAST(... AS BINARY), el cliente cambia a visualización hex (binary-as-hex), y un hello perfectamente bueno llega a la pantalla como 0x68656C6C6F. El ajuste ha convencido a miles de desarrolladores de que su codificador está roto.
  • Snowflake muestra los valores BINARY como hex en todos los conjuntos de resultados, así que una columna de entrada TO_BINARY() en tu consulta de encode se lee como un checksum incluso cuando todo funcionó. Dos dialectos, dos visualizaciones hex, una misma sensación de inquietud.
  • El RAW a nivel SQL de Oracle llega como máximo a en 2000 bytes, así que un certificado de 3 kilobytes no se puede pegar ni siquiera en una sentencia SQL como literal RAW. El encode tiene que pasar por PL/SQL, donde una variable RAW guarda 32767 bytes, así que el certificado de 3 kilobytes es una sola llamada, y solo los payloads cuya salida base64 excedería 32 kilobytes necesitan el bucle de trozos, que data de los 90.
  • Una línea de base64 MIME son 76 caracteres, que son 57 bytes en bruto, porque cuatro caracteres llevan tres. El número 76 que aparece en los defectos de tres codificadores no es tanto un límite como una densidad de empaquetado: cada línea envuelta que ves en un viejo adjunto de correo llevaba exactamente 57 bytes de tus datos.

La otra dirección

Este artículo ha tratado de ponerse el disfraz: decidir a qué bytes te refieres, observar lo que sale, elegir el alfabeto para el público, y hacer la aritmética de tamaño antes de que la columna trunque. Quitarse el disfraz es un temperamento completamente distinto, con NULLs silenciosas donde un dialecto se encoge de hombros, errores duros donde otro alza la voz, y un alfabeto URL-safe que media familia no conoce en absoluto. Todo eso, desde FROM_BASE64() hasta decode() hasta BASE64_DECODE(), está cubierto a fondo en el artículo relacionado de decodificación Base64 para SQL, enlazado justo abajo. Codifica aquí, decodifica allí, y todo el viaje de ida y vuelta cabe en una sola tarde.

Última actualización: 2026-09-08

Artículo relacionado: Decodificación Base64 en SQL: una guía completa