INSERT, UPDATE y DELETE en SQL: modificar datos de forma segura

Una base de datos no solo se consulta: también necesitamos crear, modificar y eliminar registros. Para eso usamos INSERT, UPDATE y DELETE.

Antes de modificar datos: pensar en el efecto

Las sentencias de escritura cambian el estado de la base. A diferencia de SELECT, un error puede modificar o eliminar información.

Por eso conviene pensar siempre:

  • qué filas van a cambiar;
  • qué restricciones pueden impedir la operación;
  • si hace falta una transacción;
  • si debemos probar primero la condición con SELECT.

INSERT: agregar datos

INSERT INTO agrega nuevas filas.

INSERT INTO alumnos (nombre, apellido, edad, email)
VALUES ('Ana', 'Pérez', 22, 'ana@email.com');

El orden de los valores debe coincidir con el orden de las columnas indicadas.

Es recomendable escribir siempre la lista de columnas. Así la consulta queda más clara y no depende del orden físico de la tabla.

Insertar varios registros

INSERT INTO alumnos (nombre, apellido, edad)
VALUES
('Juan', 'Gómez', 25),
('Lucía', 'Martínez', 21),
('Pedro', 'López', 30);

Omitir AUTO_INCREMENT

Si id es AUTO_INCREMENT, normalmente no lo enviamos. MySQL lo genera.

¿Qué pasa si falta una columna?

Depende de su definición:

  • si tiene DEFAULT, se usa ese valor;
  • si acepta NULL, puede quedar NULL;
  • si es NOT NULL y no tiene valor por defecto, el INSERT puede fallar.

DEFAULT

Cuando una columna tiene un valor predeterminado podemos omitirla.

NULL

NULL significa ausencia de valor. No es lo mismo que cero ni que una cadena vacía.

INSERT INTO alumnos (nombre, apellido, edad)
VALUES ('Pedro', 'López', NULL);

UPDATE: modificar registros

UPDATE alumnos
SET edad = 23
WHERE id = 1;

SET indica qué columnas cambian y WHERE cuáles filas serán afectadas.

Si WHERE identifica una única fila, normalmente modificamos un registro. Si la condición coincide con 200 filas, se modifican las 200.

Actualizar varias columnas

UPDATE alumnos
SET edad = 24,
    email = 'nuevo@email.com'
WHERE id = 1;

Filas afectadas

Después de un UPDATE o DELETE, MySQL informa cuántas filas fueron afectadas. Ese dato es una verificación muy útil: si esperábamos cambiar una fila y aparecen 10.000, algo salió mal.

El peligro de UPDATE sin WHERE

UPDATE alumnos
SET activo = FALSE;

Eso modifica todas las filas.

Probar primero con SELECT

Una práctica segura es verificar primero qué filas cumplen la condición.

SELECT *
FROM alumnos
WHERE edad < 18;

UPDATE con cálculos

UPDATE productos
SET precio = precio * 1.10
WHERE categoria_id = 3;

El nuevo valor puede calcularse a partir del anterior.

UPDATE no cambia la estructura

UPDATE modifica los valores almacenados. No agrega ni elimina columnas; para cambiar la estructura usamos ALTER TABLE.

DELETE: eliminar filas

DELETE FROM alumnos
WHERE id = 5;

DELETE sin WHERE

DELETE FROM alumnos;

Elimina todas las filas de la tabla.

La tabla sigue existiendo, pero queda vacía. Por eso una omisión accidental de WHERE puede ser muy grave.

DELETE y claves foráneas

Una relación puede impedir borrar un registro padre o aplicar reglas como CASCADE o SET NULL.

DELETE vs TRUNCATE

DELETE puede filtrar con WHERE. TRUNCATE vacía toda la tabla.

TRUNCATE suele ser más rápido para vaciar completamente una tabla y normalmente reinicia el contador AUTO_INCREMENT, pero no debe usarse cuando necesitamos borrar selectivamente.

TRUNCATE TABLE alumnos;

INSERT … SELECT

Permite copiar datos desde otra consulta.

INSERT INTO alumnos_backup (nombre, apellido, edad)
SELECT nombre, apellido, edad
FROM alumnos
WHERE activo = TRUE;

UPDATE con JOIN

MySQL permite actualizar según datos relacionados.

UPDATE alumnos AS a
INNER JOIN cursos AS c
    ON a.curso_id = c.id
SET a.activo = FALSE
WHERE c.nombre = 'Curso finalizado';

Buenas prácticas

  • probar condiciones con SELECT;
  • hacer backups antes de cambios masivos;
  • usar transacciones cuando corresponda;
  • revisar filas afectadas;
  • evitar UPDATE o DELETE sin WHERE por accidente.

Ejercicios

  1. insertar cinco productos;
  2. actualizar un precio;
  3. aumentar 10% una categoría;
  4. desactivar productos sin stock;
  5. eliminar productos inactivos.

¿Qué sigue?

Transacciones para proteger operaciones compuestas.

¿Te sirvió esta guía? ☕

Si este contenido te ayudó y querés apoyar a Club Programador para seguir publicando ejercicios, proyectos y guías gratuitas, podés colaborar mediante:

☕ Apoyar con Mercado Pago

🌎 Apoyar con PayPal


Ruta SQL / MySQL

Ver todos los contenidos de SQL / MySQL

← CREATE TABLE y ALTER TABLE

Transacciones en SQL →

CREATE TABLE y ALTER TABLE en SQL: tipos de datos, claves y restricciones

Hasta ahora trabajamos con tablas ya existentes. En esta guía vamos a entender cómo se diseña una tabla desde cero y qué significa cada parte de su definición.

Diseñar antes de escribir SQL

Antes de crear una tabla conviene pensar qué representa. Una tabla debería tener una responsabilidad clara: alumnos, cursos, productos, ventas, etc.

El diseño comienza identificando:

  • la entidad;
  • sus atributos;
  • qué dato identifica cada fila;
  • qué campos son obligatorios;
  • qué reglas deben cumplirse;
  • con qué otras entidades se relaciona.

¿Qué hace CREATE TABLE?

CREATE TABLE crea una nueva estructura donde luego podremos guardar registros.

CREATE TABLE alumnos (
    id INT AUTO_INCREMENT PRIMARY KEY,
    nombre VARCHAR(100),
    apellido VARCHAR(100),
    edad INT,
    email VARCHAR(150)
);

Dentro de los paréntesis definimos las columnas, su tipo y las reglas que deben cumplir.

En otras palabras, CREATE TABLE no crea “datos”: crea el molde que determina cómo podrán almacenarse esos datos.

¿Qué es un tipo de dato?

El tipo de dato define qué clase de valor puede guardarse en una columna y cuánto espacio o precisión necesita.

INT

Se usa para números enteros. Es apropiado para edades, cantidades, identificadores o contadores, pero no para teléfonos o documentos si no vamos a hacer operaciones matemáticas con ellos.

edad INT

VARCHAR

Texto de longitud variable. El número entre paréntesis indica la longitud máxima permitida, no la cantidad fija de espacio que necesariamente ocupará cada valor.

nombre VARCHAR(100)

TEXT

Texto largo. Es útil para descripciones extensas, observaciones o contenido donde VARCHAR puede quedarse corto.

descripcion TEXT

DECIMAL

Valores decimales exactos, ideal para dinero.

precio DECIMAL(10,2)

El 10 indica la cantidad total de dígitos y el 2 la cantidad de decimales.

DATE y DATETIME

fecha_nacimiento DATE
fecha_creacion DATETIME

BOOLEAN

Representa valores lógicos verdadero/falso. En MySQL suele mapearse internamente a un entero pequeño.

activo BOOLEAN

PRIMARY KEY

Identifica de forma única cada fila.

id INT PRIMARY KEY

Una buena clave primaria debe ser única y estable.

Además, no debería depender de un dato que pueda cambiar frecuentemente. Por eso muchas tablas utilizan un identificador numérico independiente del negocio.

AUTO_INCREMENT

Hace que MySQL genere el siguiente número automáticamente.

id INT AUTO_INCREMENT PRIMARY KEY

NOT NULL

Indica que el campo es obligatorio.

Sin NOT NULL, la columna puede contener NULL, es decir, ausencia de valor. Eso no es lo mismo que una cadena vacía ni que cero.

nombre VARCHAR(100) NOT NULL

UNIQUE

Impide valores duplicados.

Es apropiado para datos que deben ser exclusivos, como un email, número de matrícula o código interno, siempre que las reglas del negocio realmente exijan unicidad.

email VARCHAR(150) UNIQUE

DEFAULT

Define un valor predeterminado cuando no enviamos uno.

activo BOOLEAN DEFAULT TRUE

CHECK

Permite imponer una condición.

Por ejemplo, podemos impedir edades negativas o porcentajes fuera del rango esperado. Esto mueve parte de la validación al propio motor de base de datos.

edad INT CHECK (edad >= 0)

FOREIGN KEY

Una clave foránea conecta una tabla con otra.

FOREIGN KEY (curso_id)
REFERENCES cursos(id)

Esto significa que curso_id debe apuntar a un curso válido.

La columna relacionada debería tener un tipo compatible con la clave referenciada. Si cursos.id es INT, normalmente alumnos.curso_id también debe ser INT.

Integridad referencial

Gracias a las claves foráneas, la base puede impedir relaciones inválidas.

Qué ocurre si borramos el registro relacionado

Imaginemos que un alumno apunta al curso 5 y queremos eliminar ese curso. MySQL necesita saber qué hacer con esa relación.

Las opciones típicas son:

  • RESTRICT o comportamiento equivalente: impedir la eliminación;
  • CASCADE: propagar la eliminación;
  • SET NULL: mantener el registro hijo pero quitar la referencia.

ON DELETE y ON UPDATE

Definen qué ocurre si cambia o se elimina el registro relacionado.

FOREIGN KEY (curso_id)
REFERENCES cursos(id)
ON DELETE SET NULL
ON UPDATE CASCADE

Modificar estructura no es lo mismo que modificar datos

UPDATE cambia valores dentro de filas. ALTER TABLE cambia la estructura de la tabla: columnas, tipos o restricciones.

ALTER TABLE

ALTER TABLE modifica una tabla ya creada.

Agregar una columna

ALTER TABLE alumnos
ADD telefono VARCHAR(30);

Modificar una columna

ALTER TABLE alumnos
MODIFY telefono VARCHAR(50) NOT NULL;

Renombrar una columna

ALTER TABLE alumnos
RENAME COLUMN telefono TO celular;

Eliminar una columna

ALTER TABLE alumnos
DROP COLUMN celular;

DROP TABLE

Elimina la tabla completa: estructura y datos.

DROP TABLE alumnos;

TRUNCATE TABLE

Vacía la tabla, pero conserva su estructura.

Es una operación mucho más drástica que un DELETE con WHERE: no sirve para eliminar selectivamente algunas filas.

TRUNCATE TABLE alumnos;

IF NOT EXISTS

Evita error si la tabla ya existe.

CREATE TABLE IF NOT EXISTS cursos (
    id INT AUTO_INCREMENT PRIMARY KEY,
    nombre VARCHAR(100) NOT NULL
);

Cómo diseñar una tabla correctamente

Antes de crearla conviene responder:

  • ¿qué entidad representa?
  • ¿cuál es su clave primaria?
  • ¿qué campos son obligatorios?
  • ¿qué datos deben ser únicos?
  • ¿con qué otras tablas se relaciona?

Errores comunes

  • usar VARCHAR para todo;
  • no definir PRIMARY KEY;
  • no usar NOT NULL donde corresponde;
  • crear relaciones con tipos incompatibles;
  • eliminar columnas sin revisar datos.

Ejercicio

Creá tablas categorias y productos con clave primaria, nombre obligatorio, precio, stock, activo y una clave foránea desde productos a categorías.

¿Qué sigue?

INSERT, UPDATE y DELETE para modificar datos.

¿Te sirvió esta guía? ☕

Si este contenido te ayudó y querés apoyar a Club Programador para seguir publicando ejercicios, proyectos y guías gratuitas, podés colaborar mediante:

☕ Apoyar con Mercado Pago

🌎 Apoyar con PayPal


Ruta SQL / MySQL

Ver todos los contenidos de SQL / MySQL

← Subconsultas en SQL

INSERT, UPDATE y DELETE →

Subconsultas en SQL: IN, EXISTS, consultas anidadas y ejemplos prácticos

Una subconsulta es una consulta que aparece dentro de otra consulta. Se utiliza cuando necesitamos calcular un valor o conjunto intermedio para resolver una consulta principal.

¿Qué problema resuelve?

Supongamos que queremos mostrar alumnos mayores que la edad promedio. Primero necesitamos conocer el promedio y luego compararlo con cada alumno.

Cómo se ejecuta mentalmente una subconsulta

Una buena forma de entenderla es leer desde adentro hacia afuera.

  1. ejecutamos primero la consulta interna;
  2. observamos qué devuelve;
  3. ese resultado se utiliza en la consulta externa.

Subconsulta escalar

Una subconsulta escalar devuelve un único valor.

SELECT nombre, edad
FROM alumnos
WHERE edad > (
    SELECT AVG(edad)
    FROM alumnos
);

Primero se calcula el promedio. Después la consulta externa compara cada edad contra ese valor.

Si el promedio fuera 22, la consulta externa se comportaría conceptualmente como si hubiéramos escrito WHERE edad > 22.

Subconsulta con IN

IN se usa cuando la subconsulta devuelve varios valores.

Si la consulta interna devuelve los ids 1, 3, 5, la condición externa equivale conceptualmente a curso_id IN (1, 3, 5).

SELECT nombre, apellido
FROM alumnos
WHERE curso_id IN (
    SELECT id
    FROM cursos
    WHERE nombre LIKE '%Programación%'
);

NOT IN

Excluye los valores devueltos por la subconsulta.

SELECT nombre, apellido
FROM alumnos
WHERE curso_id NOT IN (
    SELECT id
    FROM cursos
    WHERE nombre = 'Bases de Datos'
);

Hay que tener cuidado con NULL porque puede cambiar el resultado esperado.

Si la subconsulta de un NOT IN devuelve algún NULL, la lógica de tres valores de SQL puede hacer que no obtengamos filas. Para búsquedas de ausencia, NOT EXISTS suele ser más seguro y más expresivo.

EXISTS

EXISTS no necesita devolver un valor concreto: solo pregunta si existe al menos una fila.

Por eso suele escribirse SELECT 1: el valor concreto no importa. EXISTS se fija únicamente en si la subconsulta encontró o no una fila.

SELECT c.nombre
FROM cursos AS c
WHERE EXISTS (
    SELECT 1
    FROM alumnos AS a
    WHERE a.curso_id = c.id
);

NOT EXISTS

Sirve para buscar casos en los que no existe relación.

SELECT c.nombre
FROM cursos AS c
WHERE NOT EXISTS (
    SELECT 1
    FROM alumnos AS a
    WHERE a.curso_id = c.id
);

Subconsulta correlacionada

Una subconsulta correlacionada usa valores de la fila que está procesando la consulta externa.

SELECT a.nombre, a.edad
FROM alumnos AS a
WHERE a.edad > (
    SELECT AVG(a2.edad)
    FROM alumnos AS a2
    WHERE a2.curso_id = a.curso_id
);

Aquí el promedio se calcula para el curso correspondiente a cada alumno.

Eso significa que la subconsulta depende de la fila externa. Conceptualmente puede ejecutarse muchas veces, una por cada alumno evaluado, aunque el optimizador puede aplicar estrategias internas para resolverla mejor.

Subconsulta dentro de SELECT

SELECT c.nombre,
       (
           SELECT COUNT(*)
           FROM alumnos AS a
           WHERE a.curso_id = c.id
       ) AS cantidad_alumnos
FROM cursos AS c;

Subconsulta dentro de FROM

La consulta interna se comporta como una tabla derivada.

SELECT datos.promedio
FROM (
    SELECT AVG(edad) AS promedio
    FROM alumnos
) AS datos;

Cuándo una subconsulta devuelve demasiadas filas

Si usamos un operador que espera un único valor, como =, pero la subconsulta devuelve varias filas, MySQL generará un error.

En esos casos debemos usar un operador adecuado, como IN, o reformular la consulta.

Probar la subconsulta por separado

Cuando una consulta anidada no funciona, una técnica muy útil es ejecutar primero solo la parte interna. Así comprobamos qué columnas y cuántas filas devuelve antes de integrarla.

JOIN o subconsulta

Muchas consultas pueden resolverse de ambas formas. Conviene priorizar claridad, mantenibilidad y rendimiento.

Por ejemplo, “cursos con alumnos” puede resolverse con EXISTS o con INNER JOIN. Ninguna forma es siempre mejor: depende del problema y del plan de ejecución.

Errores comunes

  • usar = cuando la subconsulta devuelve varias filas;
  • usar NOT IN sin pensar en NULL;
  • crear subconsultas innecesariamente complejas;
  • no probar primero la consulta interna por separado.

Ejercicios

  1. alumnos mayores al promedio;
  2. cursos con alumnos;
  3. cursos sin alumnos;
  4. alumnos con la edad máxima;
  5. alumnos mayores al promedio de su curso.

¿Qué sigue?

Creación y modificación de tablas.

¿Te sirvió esta guía? ☕

Si este contenido te ayudó y querés apoyar a Club Programador para seguir publicando ejercicios, proyectos y guías gratuitas, podés colaborar mediante:

☕ Apoyar con Mercado Pago

🌎 Apoyar con PayPal


Ruta SQL / MySQL

Ver todos los contenidos de SQL / MySQL

← JOIN en SQL

CREATE TABLE y ALTER TABLE →

JOIN en SQL: INNER JOIN, LEFT JOIN y RIGHT JOIN con ejemplos claros

En una base de datos relacional, la información normalmente está separada en varias tablas. JOIN permite volver a reunirla cuando necesitamos consultar datos relacionados.

¿Por qué separar tablas?

Supongamos que cada alumno pertenece a un curso. Si guardamos el nombre del curso dentro de cada alumno, repetimos el mismo texto muchas veces.

Una mejor estructura es tener:

  • tabla alumnos;
  • tabla cursos;
  • en alumnos, una columna curso_id.

Clave primaria y clave foránea

cursos.id identifica un curso. alumnos.curso_id guarda el identificador del curso relacionado.

Eso crea una relación entre ambas tablas.

Tablas de ejemplo

CREATE TABLE cursos (
    id INT AUTO_INCREMENT PRIMARY KEY,
    nombre VARCHAR(100)
);
CREATE TABLE alumnos (
    id INT AUTO_INCREMENT PRIMARY KEY,
    nombre VARCHAR(100),
    apellido VARCHAR(100),
    curso_id INT,
    FOREIGN KEY (curso_id) REFERENCES cursos(id)
);

Una relación concreta con datos

Supongamos que tenemos estos cursos:

cursos
id | nombre
1  | Programación
2  | Bases de Datos

Y estos alumnos:

alumnos
id | nombre | curso_id
1  | Ana    | 1
2  | Juan   | 2
3  | Lucía  | NULL

El valor curso_id no es el nombre del curso: es una referencia al id de la tabla cursos. JOIN nos permite reconstruir la información legible.

¿Qué hace JOIN?

JOIN combina filas de dos tablas usando una condición de relación.

La idea mental es: “por cada fila de la tabla principal, buscá la fila relacionada en la otra tabla”.

La cláusula ON

ON alumnos.curso_id = cursos.id

ON indica qué columnas deben coincidir.

En este caso, el valor guardado en alumnos.curso_id debe ser igual al identificador cursos.id.

Si la condición de ON está mal, el resultado también estará mal, aunque la consulta no genere ningún error sintáctico.

INNER JOIN

Devuelve solo las filas que tienen coincidencia en ambas tablas.

SELECT a.nombre, a.apellido, c.nombre AS curso
FROM alumnos AS a
INNER JOIN cursos AS c
    ON a.curso_id = c.id;

Si un alumno no tiene curso asignado, no aparece.

Con los datos de ejemplo, Ana y Juan aparecerían porque tienen curso; Lucía no, porque su curso_id es NULL.

LEFT JOIN

Devuelve todos los registros de la tabla izquierda, aunque no exista coincidencia.

SELECT a.nombre, c.nombre AS curso
FROM alumnos AS a
LEFT JOIN cursos AS c
    ON a.curso_id = c.id;

Si el alumno no tiene curso, la columna curso aparece como NULL.

Por eso LEFT JOIN es muy útil cuando queremos conservar todos los registros principales aunque la relación todavía no exista.

La tabla izquierda y la tabla derecha

En SQL, “izquierda” y “derecha” dependen de cómo está escrita la consulta:

FROM alumnos
LEFT JOIN cursos ...

alumnos es la tabla izquierda y cursos la derecha.

RIGHT JOIN

Conserva todos los registros de la tabla derecha.

SELECT a.nombre, c.nombre AS curso
FROM alumnos AS a
RIGHT JOIN cursos AS c
    ON a.curso_id = c.id;

Alias

Los alias acortan nombres y hacen más legibles las consultas.

FROM alumnos AS a
INNER JOIN cursos AS c

ON y WHERE no cumplen la misma función

ON define cómo se relacionan las tablas. WHERE filtra el resultado.

Esta diferencia es especialmente importante con LEFT JOIN, porque mover una condición de ON a WHERE puede cambiar qué filas sobreviven.

JOIN con WHERE

SELECT a.nombre, c.nombre AS curso
FROM alumnos AS a
INNER JOIN cursos AS c
    ON a.curso_id = c.id
WHERE c.nombre = 'Programación';

Encontrar registros sin relación

LEFT JOIN combinado con IS NULL es muy útil.

SELECT a.nombre
FROM alumnos AS a
LEFT JOIN cursos AS c
    ON a.curso_id = c.id
WHERE c.id IS NULL;

JOIN con agregaciones

SELECT c.nombre, COUNT(a.id) AS cantidad_alumnos
FROM cursos AS c
LEFT JOIN alumnos AS a
    ON c.id = a.curso_id
GROUP BY c.id, c.nombre;

¿Por qué pueden aparecer filas repetidas?

JOIN no “pega una fila con una fila” necesariamente. En una relación uno a muchos, una fila de la tabla izquierda puede producir varias filas en el resultado.

Por ejemplo, si un curso tiene 30 alumnos, una consulta que une cursos con alumnos puede devolver 30 filas para ese curso.

Más de dos tablas

Una consulta puede encadenar varios JOIN. Por ejemplo: alumno → curso → profesor.

INNER, LEFT o RIGHT: ¿cuál usar?

  • INNER JOIN: solo coincidencias.
  • LEFT JOIN: todos los de la izquierda.
  • RIGHT JOIN: todos los de la derecha.

Errores comunes

  • olvidar ON;
  • relacionar columnas incorrectas;
  • confundir LEFT con INNER;
  • no entender los NULL;
  • usar SELECT * cuando varias tablas tienen columnas del mismo nombre.

Ejercicios

  1. mostrar alumno y curso;
  2. mostrar todos los alumnos aunque no tengan curso;
  3. mostrar cursos sin alumnos;
  4. contar alumnos por curso.

¿Qué sigue?

Subconsultas en SQL.

¿Te sirvió esta guía? ☕

Si este contenido te ayudó y querés apoyar a Club Programador para seguir publicando ejercicios, proyectos y guías gratuitas, podés colaborar mediante:

☕ Apoyar con Mercado Pago

🌎 Apoyar con PayPal


Ruta SQL / MySQL

Ver todos los contenidos de SQL / MySQL

← Funciones de agregación

Subconsultas en SQL →

Funciones de agregación en SQL: COUNT, SUM, AVG, MIN, MAX, GROUP BY y HAVING

Hasta ahora cada fila aparecía de forma individual. Pero muchas veces queremos obtener resúmenes: cuántos registros hay, cuánto suman, cuál es el promedio o cuál es el valor máximo.

¿Qué es una función de agregación?

Una función de agregación recibe varias filas y devuelve un valor resumido.

Las más usadas son:

  • COUNT()
  • SUM()
  • AVG()
  • MIN()
  • MAX()

Una tabla de ejemplo

Imaginemos estos alumnos:

id | nombre | ciudad       | edad | cuota
1  | Ana    | Santa Rosa   | 20   | 10000
2  | Juan   | Santa Rosa   | 24   | 12000
3  | Lucía  | General Pico | 22   | 11000

Las funciones de agregación toman varias filas como estas y producen un resumen.

COUNT()

Cuenta filas. En el ejemplo anterior, COUNT(*) devuelve 3.

SELECT COUNT(*) AS total_alumnos
FROM alumnos;

El alias AS total_alumnos cambia el nombre de la columna del resultado.

COUNT(columna)

A diferencia de COUNT(*), ignora valores NULL.

SELECT COUNT(email)
FROM alumnos;

SUM()

Suma valores numéricos. Si las cuotas son 10000, 12000 y 11000, SUM(cuota) devuelve 33000.

SELECT SUM(cuota) AS total_cuotas
FROM alumnos;

AVG()

Calcula el promedio. Para edades 20, 24 y 22, AVG(edad) devuelve 22.

SELECT AVG(edad) AS edad_promedio
FROM alumnos;

MIN() y MAX()

Devuelven el menor y mayor valor.

SELECT MIN(edad) AS menor_edad,
       MAX(edad) AS mayor_edad
FROM alumnos;

AS: dar nombre al resultado agregado

Las expresiones como COUNT(*) o AVG(edad) pueden quedar con nombres poco cómodos. Un alias mejora la lectura:

SELECT AVG(edad) AS edad_promedio
FROM alumnos;

GROUP BY

GROUP BY divide los registros en grupos y aplica las funciones a cada grupo.

SELECT ciudad, COUNT(*) AS cantidad
FROM alumnos
GROUP BY ciudad;

Ahora ya no obtenemos un total general, sino un total por cada ciudad.

Con los datos del ejemplo, el resultado sería conceptualmente:

ciudad       | cantidad
Santa Rosa   | 2
General Pico | 1

La clave para entender GROUP BY es preguntarse: “¿por qué dimensión quiero resumir los datos?”. Puede ser por ciudad, curso, categoría, mes, cliente, etc.

Promedio por grupo

SELECT ciudad, AVG(edad) AS edad_promedio
FROM alumnos
GROUP BY ciudad;

Qué columnas pueden aparecer junto a GROUP BY

Cuando agrupamos, cada fila del resultado representa un grupo. Por eso, las columnas seleccionadas deberían ser:

  • columnas incluidas en GROUP BY; o
  • resultados de funciones de agregación.

Por ejemplo, esta consulta tiene sentido porque ciudad define el grupo y COUNT(*) resume sus filas.

WHERE antes de agrupar

WHERE elimina filas antes de formar los grupos.

SELECT ciudad, COUNT(*) AS cantidad
FROM alumnos
WHERE edad >= 22
GROUP BY ciudad;

HAVING

HAVING filtra grupos ya calculados.

SELECT ciudad, COUNT(*) AS cantidad
FROM alumnos
GROUP BY ciudad
HAVING COUNT(*) > 1;

El orden lógico: WHERE → GROUP BY → HAVING

Una forma sencilla de pensarlo es:

  1. WHERE decide qué filas entran al análisis;
  2. GROUP BY forma los grupos;
  3. las funciones calculan sus valores;
  4. HAVING decide qué grupos permanecen.

WHERE vs HAVING

  • WHERE: filtra filas antes de agrupar.
  • HAVING: filtra grupos después de agrupar.

COUNT(DISTINCT …)

Cuenta valores distintos.

SELECT COUNT(DISTINCT ciudad) AS cantidad_ciudades
FROM alumnos;

NULL en funciones de agregación

Funciones como AVG(), SUM(), MIN(), MAX() y COUNT(columna) normalmente ignoran NULL.

COUNT(*), en cambio, cuenta filas aunque alguna de sus columnas contenga NULL.

Esto importa mucho en promedios: un NULL no se interpreta como cero, simplemente no participa del cálculo.

Ejemplo completo

SELECT ciudad,
       COUNT(*) AS cantidad,
       AVG(edad) AS edad_promedio
FROM alumnos
WHERE edad > 20
GROUP BY ciudad
HAVING COUNT(*) >= 2
ORDER BY edad_promedio DESC;

Errores comunes

  • confundir WHERE con HAVING;
  • olvidar GROUP BY;
  • suponer que COUNT(columna) cuenta NULL;
  • usar SUM() sobre campos que no representan cantidades.

Ejercicios

  1. contar alumnos;
  2. calcular edad promedio;
  3. obtener edad mínima y máxima;
  4. contar por ciudad;
  5. mostrar ciudades con más de un alumno.

¿Qué sigue?

JOIN y relaciones entre tablas.

¿Te sirvió esta guía? ☕

Si este contenido te ayudó y querés apoyar a Club Programador para seguir publicando ejercicios, proyectos y guías gratuitas, podés colaborar mediante:

☕ Apoyar con Mercado Pago

🌎 Apoyar con PayPal


Ruta SQL / MySQL

Ver todos los contenidos de SQL / MySQL

← SELECT en SQL

JOIN en SQL →

SELECT en SQL: consultas, WHERE, ORDER BY, LIMIT y DISTINCT con ejemplos

SELECT es la sentencia más utilizada para leer información de una base de datos. Antes de combinar consultas complejas, conviene entender bien qué hace cada parte.

¿Qué problema resuelve SELECT?

Una tabla puede contener miles o millones de registros. SELECT nos permite pedir exactamente los datos que necesitamos.

Cómo pensar una consulta SELECT

Antes de escribir SQL conviene hacerse cuatro preguntas:

  1. ¿de qué tabla salen los datos?
  2. ¿qué columnas necesito?
  3. ¿qué filas quiero conservar?
  4. ¿cómo quiero ordenar o limitar el resultado?

Con ese razonamiento, la consulta deja de ser una lista de palabras reservadas y pasa a representar una pregunta concreta a la base de datos.

La estructura básica

SELECT columnas
FROM tabla;

SELECT indica qué columnas queremos obtener y FROM de qué tabla provienen.

SELECT *

SELECT *
FROM alumnos;

El asterisco significa “todas las columnas”. Es cómodo para aprender, pero en sistemas reales conviene seleccionar solo lo necesario.

Seleccionar columnas concretas

SELECT nombre, apellido
FROM alumnos;

El resultado contiene únicamente esas dos columnas.

WHERE: filtrar filas

WHERE define una condición. Solo se devuelven las filas que la cumplen.

SELECT *
FROM alumnos
WHERE edad = 25;

Operadores de comparación

  • = igual
  • <> o != distinto
  • > mayor
  • < menor
  • >= mayor o igual
  • <= menor o igual

Comparar texto

SELECT *
FROM alumnos
WHERE ciudad = 'Santa Rosa';

Los valores de texto se escriben entre comillas.

NULL: ausencia de valor

NULL significa que no hay un valor conocido. No equivale a cero ni a una cadena vacía.

Por eso no se compara con = NULL. Debemos usar:

SELECT *
FROM alumnos
WHERE email IS NULL;

Y para buscar registros con valor:

WHERE email IS NOT NULL

AND

AND exige que se cumplan todas las condiciones.

SELECT *
FROM alumnos
WHERE ciudad = 'Santa Rosa'
AND edad >= 22;

OR

OR acepta una condición u otra.

SELECT *
FROM alumnos
WHERE ciudad = 'Santa Rosa'
OR ciudad = 'General Pico';

AND y OR juntos: usar paréntesis

Cuando combinamos AND y OR, los paréntesis hacen explícita la lógica y evitan resultados inesperados.

SELECT *
FROM alumnos
WHERE ciudad = 'Santa Rosa'
AND (edad < 20 OR edad > 30);

Primero se evalúa lo que está entre paréntesis y luego se combina con la condición de ciudad.

NOT

NOT niega una condición.

SELECT *
FROM alumnos
WHERE NOT ciudad = 'Santa Rosa';

BETWEEN

Sirve para trabajar con rangos.

SELECT *
FROM alumnos
WHERE edad BETWEEN 22 AND 26;

IN

Evita escribir muchos OR cuando comparamos contra varios valores.

SELECT *
FROM alumnos
WHERE ciudad IN ('Santa Rosa', 'General Pico', 'Toay');

LIKE

LIKE busca patrones.

SELECT *
FROM alumnos
WHERE nombre LIKE 'A%';

% representa cero o más caracteres.

También existe _, que representa exactamente un carácter.

SELECT *
FROM alumnos
WHERE nombre LIKE 'A_a';

Este patrón podría coincidir con nombres como Ana, porque hay exactamente un carácter entre la A y la a.

ORDER BY

Ordena el resultado.

SELECT *
FROM alumnos
ORDER BY edad ASC;

ASC ordena ascendente y DESC descendente.

Ordenar por varias columnas

SELECT *
FROM alumnos
ORDER BY ciudad ASC, apellido ASC;

AS: poner nombres más claros al resultado

Un alias cambia el nombre mostrado de una columna sin modificar la tabla.

SELECT nombre AS alumno,
       email AS correo
FROM alumnos;

LIMIT

Limita cuántas filas devuelve la consulta.

SELECT *
FROM alumnos
LIMIT 3;

OFFSET

Permite saltar registros. Es habitual en paginación.

Por ejemplo, con 10 registros por página, la segunda página podría usar LIMIT 10 OFFSET 10. En tablas muy grandes, offsets muy altos pueden volverse costosos y existen técnicas de paginación por clave más eficientes.

SELECT *
FROM alumnos
ORDER BY id
LIMIT 2 OFFSET 2;

DISTINCT

Elimina valores repetidos del resultado.

SELECT DISTINCT ciudad
FROM alumnos;

Ejemplo combinado

SELECT nombre, apellido, edad
FROM alumnos
WHERE ciudad = 'Santa Rosa'
AND edad >= 20
ORDER BY edad DESC
LIMIT 5;

Conceptualmente, esta consulta:

  1. parte de alumnos;
  2. conserva los de Santa Rosa;
  3. descarta menores de 20;
  4. ordena por edad de mayor a menor;
  5. devuelve solo cinco filas.

Ese orden mental es muy útil para construir consultas más grandes.

Errores comunes

  • olvidar comillas en textos;
  • confundir AND y OR;
  • usar LIKE sin %;
  • suponer que una tabla tiene orden natural;
  • usar SELECT * cuando solo hacen falta dos columnas.

Ejercicios

  1. mostrar todos los alumnos;
  2. mostrar nombre y email;
  3. buscar mayores de 23;
  4. buscar alumnos de Santa Rosa;
  5. ordenar por edad;
  6. mostrar los tres mayores;
  7. listar ciudades sin repetir.

¿Qué sigue?

Funciones de agregación para resumir información.

¿Te sirvió esta guía? ☕

Si este contenido te ayudó y querés apoyar a Club Programador para seguir publicando ejercicios, proyectos y guías gratuitas, podés colaborar mediante:

☕ Apoyar con Mercado Pago

🌎 Apoyar con PayPal


Ruta SQL / MySQL

Ver todos los contenidos de SQL / MySQL

← Introducción a SQL y MySQL

Funciones de agregación →

Introducción a SQL y MySQL: bases de datos, tablas, registros y consultas desde cero

Cuando una aplicación necesita guardar información de forma permanente, ordenada y consultable, normalmente termina utilizando una base de datos. Un sistema de alumnos, una tienda, una red social, un banco o una plataforma médica tienen algo en común: necesitan almacenar datos y recuperarlos de forma confiable.

En esta primera guía vamos a construir una base conceptual sólida antes de empezar con las consultas. La idea es entender qué problema resuelven las bases de datos, qué es SQL, qué es MySQL y cómo se organiza la información.

¿Qué problema resuelve una base de datos?

Imaginemos una escuela que guarda sus alumnos en archivos de texto separados. Cada archivo podría contener nombre, documento, curso y teléfono. Al principio puede funcionar, pero enseguida aparecen problemas:

  • ¿cómo buscamos rápidamente a todos los alumnos de un curso?
  • ¿cómo evitamos cargar dos veces al mismo alumno?
  • ¿cómo actualizamos un teléfono sin modificar varios archivos?
  • ¿cómo relacionamos alumnos con cursos, profesores y materias?
  • ¿cómo permitimos que varias personas trabajen al mismo tiempo sin romper la información?

Una base de datos está diseñada precisamente para resolver ese tipo de problemas.

Dato, información y estructura

Un dato es un valor individual: por ejemplo 22, Ana o Santa Rosa. Cuando varios datos se organizan y adquieren contexto, se transforman en información útil.

Por ejemplo:

nombre: Ana
edad: 22
ciudad: Santa Rosa

En una base de datos, esos valores no se guardan de manera arbitraria: se organizan dentro de estructuras definidas.

¿Qué es una base de datos?

Una base de datos es un conjunto organizado de información que puede almacenarse, consultarse, modificarse y relacionarse de forma eficiente.

No es simplemente un archivo con datos. Una base de datos permite aplicar reglas, relaciones, permisos, restricciones y transacciones.

¿Cómo funciona una base de datos en una aplicación?

En una aplicación típica, el usuario no habla directamente con MySQL. La aplicación recibe una acción, construye una consulta SQL, la envía al servidor de base de datos y recibe un resultado.

Podemos imaginar el flujo así:

Usuario → Aplicación → SQL → MySQL → Resultado → Aplicación → Usuario

Esta separación es importante porque el motor de base de datos se especializa en almacenar, validar, buscar y relacionar información, mientras que la aplicación se ocupa de la interfaz y las reglas del sistema.

¿Qué es un SGBD?

Un Sistema Gestor de Bases de Datos o SGBD es el software encargado de administrar una base de datos.

Entre otras tareas, se ocupa de:

  • guardar y recuperar información;
  • procesar consultas;
  • controlar accesos;
  • mantener integridad;
  • gestionar concurrencia;
  • manejar transacciones;
  • optimizar consultas.

MySQL es un SGBD relacional.

¿Qué es SQL?

SQL significa Structured Query Language. Es el lenguaje que usamos para comunicarnos con bases de datos relacionales.

Con SQL podemos:

  • crear tablas;
  • insertar datos;
  • consultar información;
  • modificar registros;
  • eliminar datos;
  • crear relaciones;
  • administrar permisos;
  • controlar transacciones.

SQL es un lenguaje declarativo

Esto significa que normalmente indicamos qué resultado queremos, y el motor decide cómo obtenerlo.

Por ejemplo:

SELECT nombre, apellido
FROM alumnos
WHERE edad > 18;

Nosotros pedimos “los alumnos mayores de 18”. MySQL decide cómo localizar esos registros.

SQL y MySQL no son lo mismo

SQL es el lenguaje. MySQL es uno de los motores que implementan ese lenguaje.

También existen otros motores como PostgreSQL, SQL Server, Oracle o MariaDB. Todos utilizan SQL, aunque cada uno posee extensiones y diferencias propias.

Base de datos, esquema y tabla: no son lo mismo

Conviene distinguir tres conceptos:

  • base de datos: el conjunto general de información;
  • esquema: la organización lógica de objetos y estructuras;
  • tabla: una estructura concreta formada por columnas y filas.

En MySQL, en el uso cotidiano, los términos base de datos y schema suelen tratarse prácticamente como equivalentes.

¿Qué es una base de datos relacional?

Una base de datos relacional organiza la información en tablas. Esas tablas pueden relacionarse mediante claves.

Por ejemplo, en un sistema educativo podríamos tener:

  • alumnos;
  • cursos;
  • profesores;
  • inscripciones.

En lugar de guardar toda la información repetida en una única tabla enorme, se divide de forma lógica y después se relaciona.

Entidad, atributo y registro

Una entidad representa algo del mundo real que queremos guardar. Por ejemplo, un alumno.

Sus atributos podrían ser nombre, apellido, fecha de nacimiento o email.

Un registro es una instancia concreta de esa entidad.

Tabla, fila y columna

En una tabla:

  • cada fila representa un registro;
  • cada columna representa un atributo;
  • la tabla completa representa una entidad o relación.

Clave primaria

Una clave primaria identifica de forma única cada fila.

id INT AUTO_INCREMENT PRIMARY KEY

Dos registros no pueden compartir el mismo valor de clave primaria.

Clave foránea

Una clave foránea permite relacionar una tabla con otra.

Por ejemplo, un alumno puede guardar un curso_id que apunte al id de la tabla cursos.

¿Por qué relacionar tablas en lugar de repetir datos?

Supongamos que 500 alumnos pertenecen al mismo curso. Si escribimos el nombre completo del curso en cada alumno, repetimos el mismo dato 500 veces.

En cambio, podemos guardar el curso una sola vez:

cursos
id | nombre
1  | Programación

alumnos
id | nombre | curso_id
1  | Ana    | 1
2  | Juan   | 1

El valor curso_id = 1 significa que ambos alumnos apuntan al mismo curso. Si el nombre del curso cambia, se modifica una sola fila.

Relaciones entre tablas

Las relaciones principales son:

  • uno a uno: un registro se relaciona con uno;
  • uno a muchos: un registro se relaciona con muchos;
  • muchos a muchos: varios registros de una tabla se relacionan con varios de otra.

Concurrencia e integridad

Una de las grandes diferencias frente a guardar información en archivos sueltos es que un SGBD está preparado para que varios usuarios trabajen al mismo tiempo.

Además, puede hacer cumplir reglas. Por ejemplo, puede impedir que dos usuarios tengan el mismo email si definimos una restricción UNIQUE, o evitar que un alumno apunte a un curso inexistente mediante una clave foránea.

Persistencia y transacciones

Los datos sobreviven al cierre de la aplicación porque se almacenan de forma persistente. Y cuando una operación requiere varios pasos, las transacciones permiten tratarlos como una sola unidad: o se completan todos o se deshacen.

Ventajas de una base de datos relacional

  • organización clara;
  • menos duplicación innecesaria;
  • integridad mediante claves y restricciones;
  • consultas potentes;
  • seguridad;
  • transacciones;
  • mantenimiento más sencillo;
  • capacidad para manejar grandes volúmenes de información.

Tipos de instrucciones SQL

SQL suele dividirse conceptualmente en grupos:

  • DDL: define estructuras. Ejemplo: CREATE TABLE.
  • DML: modifica datos. Ejemplo: INSERT, UPDATE, DELETE.
  • DQL: consulta datos. Principalmente SELECT.
  • TCL: controla transacciones. Ejemplo: COMMIT, ROLLBACK.
  • DCL: administra permisos y accesos.

Crear nuestra primera base

CREATE DATABASE colegio;
USE colegio;

Crear una tabla

CREATE TABLE alumnos (
    id INT AUTO_INCREMENT PRIMARY KEY,
    nombre VARCHAR(100),
    apellido VARCHAR(100),
    edad INT,
    email VARCHAR(150)
);

Insertar un registro

INSERT INTO alumnos (nombre, apellido, edad, email)
VALUES ('Ana', 'Pérez', 22, 'ana@email.com');

Consultar datos

SELECT * FROM alumnos;

El asterisco indica que queremos todas las columnas.

Modificar datos

UPDATE alumnos
SET edad = 23
WHERE id = 1;

Eliminar datos

DELETE FROM alumnos
WHERE id = 1;

CRUD

Estas cuatro operaciones fundamentales se conocen como CRUD:

  • Create: crear;
  • Read: leer;
  • Update: modificar;
  • Delete: eliminar.

Ejercicio práctico

Creá una base llamada biblioteca y una tabla libros con:

  • id;
  • título;
  • autor;
  • año;
  • disponible.

¿Qué sigue?

En la próxima guía vamos a aprender a consultar datos con SELECT, aplicar filtros con WHERE y ordenar resultados.

¿Te sirvió esta guía? ☕

Si este contenido te ayudó y querés apoyar a Club Programador para seguir publicando ejercicios, proyectos y guías gratuitas, podés colaborar mediante:

☕ Apoyar con Mercado Pago

🌎 Apoyar con PayPal


Ruta SQL / MySQL

Ver todos los contenidos de SQL / MySQL

SELECT en SQL →