Saltar al contenido
funciones.sql · devschool

Funciones en SQL

Lección 6 de 22 · 13 min de lectura · Actualizado el

En esta lección
  1. Funciones de texto
  2. Funciones numéricas
  3. Funciones de fecha
  4. Convertir tipos: CAST
  5. Funciones en el WHERE: un aviso de rendimiento
  6. Errores frecuentes
  7. Resumen

Los datos no siempre están guardados como los quieres mostrar. Puede que necesites el nombre en mayúsculas, solo el apellido, el precio redondeado, el mes de un pedido o cuántos días han pasado desde una fecha. Para eso SQL tiene funciones: reciben uno o varios valores y devuelven otro. Se usan en el SELECT, en el WHERE, en el ORDER BY… en cualquier sitio donde iría un valor.

En esta lección verás las más útiles, agrupadas en texto, números y fechas. Es la parte de SQL que más cambia entre sistemas, así que te indicamos las diferencias entre MySQL, PostgreSQL y SQLite.

Nota: estas funciones trabajan fila a fila: devuelven un resultado por cada fila. Las que resumen muchas filas en un valor (COUNT, SUM, AVG…) son las funciones de agregación.

Funciones de texto

Mayúsculas y minúsculas: UPPER y LOWER

SELECT nombre, UPPER(ciudad) AS mayus, LOWER(ciudad) AS minus
FROM clientes;
nombremayusminus
Ana GarcíaSANTANDERsantander
Luis PérezBILBAObilbao
Marta RuizSANTANDERsantander
Pablo DíazMADRIDmadrid
Eva MartínMADRIDmadrid

Un uso muy habitual es comparar sin que importen las mayúsculas: WHERE LOWER(email) = 'ana@correo.es'.

Cuidado: SQLite solo cambia las letras sin tilde: UPPER('García') devuelve 'GARCíA'. MySQL y PostgreSQL devuelven 'GARCÍA'.

Longitud: LENGTH y CHAR_LENGTH

SELECT ciudad, CHAR_LENGTH(ciudad) AS letras
FROM clientes;
ciudadletras
Santander9
Bilbao6
Santander9
Madrid6
Madrid6

Aquí hay una trampa. En MySQL, LENGTH cuenta bytes, no letras, y con utf8mb4 una letra con tilde ocupa 2 bytes: LENGTH('Ana García') da 11, y CHAR_LENGTH('Ana García'), 10. En PostgreSQL y SQLite, LENGTH ya cuenta caracteres y da 10. Si trabajas con MySQL, usa CHAR_LENGTH para contar letras.

Extraer una parte: SUBSTRING y SUBSTR

SUBSTRING(texto, inicio, cantidad) devuelve cantidad caracteres a partir de la posición inicio. Las posiciones empiezan en 1, no en 0 como en muchos lenguajes de programación:

SELECT nombre, SUBSTRING(nombre, 1, 3) AS inicio
FROM clientes;
nombreinicio
Ana GarcíaAna
Luis PérezLui
Marta RuizMar
Pablo DíazPab
Eva MartínEva
  • Si omites la cantidad, llega hasta el final: SUBSTRING('Santander', 4) → 'tander'.
  • Con un inicio negativo cuenta desde el final (MySQL y SQLite): SUBSTRING('Santander', -3) → 'der'.
  • SUBSTR es un sinónimo en MySQL, SQLite, PostgreSQL y Oracle. LEFT(texto, n) y RIGHT(texto, n) existen en MySQL y PostgreSQL, pero no en SQLite.

Buscar dentro de un texto: INSTR y LOCATE

INSTR(texto, buscado) devuelve la posición donde aparece buscado, o 0 si no está:

SELECT nombre, INSTR(nombre, ' ') AS espacio
FROM clientes;
nombreespacio
Ana García4
Luis Pérez5
Marta Ruiz6
Pablo Díaz6
Eva Martín4

Combinada con SUBSTRING, permite separar el nombre del apellido: todo lo que va antes del espacio y todo lo que va después.

SELECT nombre,
       SUBSTRING(nombre, 1, INSTR(nombre, ' ') - 1) AS nombre_pila,
       SUBSTRING(nombre, INSTR(nombre, ' ') + 1) AS apellido
FROM clientes;
nombrenombre_pilaapellido
Ana GarcíaAnaGarcía
Luis PérezLuisPérez
Marta RuizMartaRuiz
Pablo DíazPabloDíaz
Eva MartínEvaMartín
SistemaPosición de ' ' en nombre
MySQLINSTR(nombre, ' ') o LOCATE(' ', nombre) (ojo: al revés)
PostgreSQLPOSITION(' ' IN nombre) o STRPOS(nombre, ' ')
SQLiteINSTR(nombre, ' ')

POSITION(' ' IN nombre) es la forma estándar y también funciona en MySQL.

Limpiar espacios: TRIM

TRIM quita los espacios del principio y del final; LTRIM, solo los de la izquierda; y RTRIM, solo los de la derecha. Es imprescindible con datos que vienen de formularios, donde un espacio de más hace que 'Madrid ' no sea igual a 'Madrid':

SELECT '[' || TRIM('   hola  ') || ']' AS t,
       '[' || LTRIM('  hola  ') || ']' AS l,
       '[' || RTRIM('  hola  ') || ']' AS r;
tlr
[hola][hola ][ hola]

(Los corchetes solo están para que veas dónde quedan los espacios. En MySQL, usa CONCAT en lugar de ||).

Reemplazar: REPLACE

REPLACE(texto, buscado, nuevo) cambia todas las apariciones:

SELECT REPLACE('600-111-222', '-', '') AS telefono;   -- 600111222

Juntar textos: CONCAT y ||

Ya lo viste en SELECT: CONCAT(a, b, ...) en MySQL y a || b en PostgreSQL y SQLite. Combinando varias funciones puedes generar, por ejemplo, un nombre de usuario con la inicial y el apellido:

SELECT nombre,
       LOWER(CONCAT(SUBSTRING(nombre, 1, 1),
                    SUBSTRING(nombre, INSTR(nombre, ' ') + 1))) AS usuario
FROM clientes;
nombreusuario
Ana Garcíaagarcía
Luis Pérezlpérez
Marta Ruizmruiz
Pablo Díazpdíaz
Eva Martínemartín

Las funciones se leen de dentro hacia fuera: primero se extraen las partes, luego se juntan y al final se pasan a minúsculas.

Funciones numéricas

Redondear: ROUND, CEIL y FLOOR

  • ROUND(x, d) redondea a d decimales (0 si no lo indicas). Con un 5, redondea hacia arriba: ROUND(12.5) → 13.
  • CEIL(x) (o CEILING) redondea hacia arriba al entero siguiente.
  • FLOOR(x) redondea hacia abajo.
SELECT id, importe,
       ROUND(importe * 0.9, 2) AS con_descuento,
       CEIL(importe)  AS arriba,
       FLOOR(importe) AS abajo
FROM pedidos;
idimportecon_descuentoarribaabajo
145.0040.504545
2120.50108.45121120
312.9911.691312
460.0054.006060
533.2529.933433

CEIL es útil para calcular cosas que no pueden ir en fracciones: si caben 6 botellas por caja y hay 20, necesitas CEIL(20 / 6.0) = 4 cajas.

Valor absoluto: ABS

ABS(x) quita el signo: ABS(-7) → 7. Sirve para medir distancias, como en la lección de ORDER BY: ORDER BY ABS(edad - 30).

Resto de una división: MOD y %

MOD(a, b) o a % b devuelven el resto de dividir a entre b. El caso típico es saber si un número es par:

SELECT nombre, edad, edad % 2 AS resto
FROM clientes;
nombreedadresto
Ana García340
Luis Pérez220
Marta Ruiz451
Pablo Díaz171
Eva Martín291

Resto 0: par. Resto 1: impar.

La división entera: una diferencia importante

¿Cuánto es 17 / 5? Depende del sistema:

OperaciónMySQLPostgreSQLSQLite
17 / 53.400033
17 / 5.03.40003.43.4
División entera17 DIV 5 → 317 / 5 → 317 / 5 → 3

En PostgreSQL y SQLite, entero entre entero da entero (se pierden los decimales). MySQL siempre da decimales con / y usa DIV para la división entera. Si quieres decimales en todos los sistemas, haz que uno de los números tenga decimales (17 / 5.0) o usa CAST.

Esto afecta a cálculos como la media de dos columnas enteras o un porcentaje: aprobados / total * 100 puede dar 0 en PostgreSQL. Escribe aprobados * 100.0 / total.

Funciones de fecha

Aquí es donde más cambian los sistemas. Esta tabla recoge las operaciones más comunes:

OperaciónMySQLPostgreSQLSQLite
Fecha de hoyCURDATE()CURRENT_DATEdate('now')
Fecha y hora actualNOW()NOW()datetime('now')
AñoYEAR(fecha)EXTRACT(YEAR FROM fecha)strftime('%Y', fecha)
MesMONTH(fecha)EXTRACT(MONTH FROM fecha)strftime('%m', fecha)
Sumar 30 díasDATE_ADD(fecha, INTERVAL 30 DAY)fecha + INTERVAL '30 days'date(fecha, '+30 days')
Días entre dos fechasDATEDIFF(fin, inicio)fin - iniciojulianday(fin) - julianday(inicio)
FormatearDATE_FORMAT(fecha, '%d/%m/%Y')TO_CHAR(fecha, 'DD/MM/YYYY')strftime('%d/%m/%Y', fecha)

Consejo: CURRENT_DATE y CURRENT_TIMESTAMP son estándar y funcionan en los tres. EXTRACT(YEAR FROM fecha) también es estándar y MySQL lo admite.

Año, mes y día

SELECT id, fecha, YEAR(fecha) AS anio, MONTH(fecha) AS mes, DAY(fecha) AS dia
FROM pedidos;
idfechaaniomesdia
12026-09-01202691
22026-09-03202693
32026-09-102026910
42026-09-122026912
52026-09-152026915

En SQLite, strftime('%m', fecha) devuelve texto con cero delante ('09'), no el número 9. Tenlo en cuenta al comparar: strftime('%m', fecha) = '09'.

Estas funciones son la base para agrupar ventas por mes, como verás en GROUP BY.

Sumar días y calcular diferencias

Supón que cada pedido se puede devolver durante 30 días. ¿Hasta cuándo?

SELECT id, fecha, DATE_ADD(fecha, INTERVAL 30 DAY) AS devolver_hasta
FROM pedidos;
idfechadevolver_hasta
12026-09-012026-10-01
22026-09-032026-10-03
32026-09-102026-10-10
42026-09-122026-10-12
52026-09-152026-10-15

Y ¿cuántos días han pasado desde cada pedido hasta el 30 de septiembre?

SELECT id, fecha, DATEDIFF('2026-09-30', fecha) AS dias
FROM pedidos;
idfechadias
12026-09-0129
22026-09-0327
32026-09-1020
42026-09-1218
52026-09-1515

En una aplicación real usarías CURDATE() en lugar de una fecha fija, y el resultado cambiaría cada día. DATEDIFF recibe primero la fecha final: si las cambias de orden, obtienes números negativos.

Formatear fechas

La base de datos guarda las fechas como AAAA-MM-DD. Para mostrarlas al estilo español:

SELECT id, DATE_FORMAT(fecha, '%d/%m/%Y') AS fecha_es
FROM pedidos;
idfecha_es
101/09/2026
203/09/2026
310/09/2026
412/09/2026
515/09/2026

El resultado ya es texto: sirve para mostrarlo, pero no para ordenar ni comparar fechas ('10/09/2026' va antes que '15/08/2026' alfabéticamente). Ordena siempre por la columna original.

Convertir tipos: CAST

CAST(valor AS tipo) convierte un valor a otro tipo. Sirve para forzar una división con decimales, convertir un texto en número o un número en texto:

SELECT CAST('42' AS DECIMAL(10, 2)) + 1;         -- 43.00
SELECT CAST(17 AS DECIMAL(10, 2)) / 5;           -- 3.4 en todos los sistemas
SELECT CONCAT(CAST(edad AS CHAR), ' años') FROM clientes;   -- '34 años', ...

Los nombres de los tipos cambian un poco: para convertir a entero, MySQL usa SIGNED y PostgreSQL y SQLite INTEGER; para texto, MySQL usa CHAR y los otros TEXT. DECIMAL(p, s) funciona en todos.

Cuidado: al pasar un decimal a entero, MySQL y PostgreSQL redondean (12.99 → 13) y SQLite trunca (12.99 → 12). Si te importa el resultado, usa ROUND o FLOOR, que hacen lo mismo en todas partes.

Funciones en el WHERE: un aviso de rendimiento

Puedes usar funciones para filtrar:

SELECT id, fecha FROM pedidos
WHERE DAY(fecha) > 10;   -- pedidos 4 y 5

Funciona, pero hay un coste: si aplicas una función a una columna del WHERE, la base de datos no puede usar el índice de esa columna y tiene que calcularla fila a fila. Para rangos de fechas, es mejor comparar la columna tal cual: WHERE fecha >= '2026-09-01' AND fecha < '2026-10-01' en lugar de WHERE MONTH(fecha) = 9.

Errores frecuentes

  • Empezar a contar posiciones en 0: en SQL, el primer carácter es el 1.
  • Usar LENGTH en MySQL para contar letras: con tildes y eñes cuenta bytes. Usa CHAR_LENGTH.
  • Dividir enteros en PostgreSQL o SQLite y perder los decimales: 5 / 2 da 2.
  • Ordenar por una fecha formateada (DATE_FORMAT): se ordena como texto.
  • Copiar funciones de un sistema a otro: DATE_ADD, DATEDIFF o IFNULL no existen en PostgreSQL. Mira la tabla de equivalencias.
  • Olvidar que una función con NULL suele devolver NULL: UPPER(NULL) es NULL. Repasa valores NULL.

Resumen

Quieres…MySQLEstándar / otros
Mayúsculas / minúsculasUPPER(x), LOWER(x)Igual
Número de letrasCHAR_LENGTH(x)LENGTH(x)
Parte de un textoSUBSTRING(x, inicio, n)Igual (SUBSTR en SQLite)
Posición de un textoINSTR(x, 'a')POSITION('a' IN x)
Quitar espaciosTRIM(x)Igual
ReemplazarREPLACE(x, 'a', 'b')Igual
RedondearROUND(x, 2), CEIL(x), FLOOR(x)Igual
Restox % 2, MOD(x, 2)Igual
División enteraa DIV ba / b con enteros
Año de una fechaYEAR(f)EXTRACT(YEAR FROM f)
Sumar díasDATE_ADD(f, INTERVAL 30 DAY)Ver tabla de fechas
Diferencia en díasDATEDIFF(fin, inicio)Ver tabla de fechas
Convertir tipoCAST(x AS DECIMAL(10, 2))Igual

En la siguiente lección aprenderás a escribir condiciones dentro de las consultas con CASE.

Pon a prueba lo que has aprendido

[SQL] ¿Qué devuelve esta consulta?
SELECT SUBSTRING('Santander', 4, 3);

[SQL] En PostgreSQL, la tabla notas tiene las columnas enteras aprobados = 18 y total = 24. ¿Qué devuelve aprobados / total * 100?

[SQL] En MySQL con utf8mb4, ¿qué devuelve esta consulta?
SELECT LENGTH('Díaz'), CHAR_LENGTH('Díaz');

[SQL] La columna fecha de pedidos tiene un índice. ¿Qué filtro de pedidos de septiembre de 2026 puede aprovecharlo?

¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.