Qué es una baja
Una baja elimina una fila. Es la D del CRUD, la última operación y la única de las cuatro que destruye información. Las otras tres se pueden corregir: un alta equivocada se borra, una modificación mal hecha se vuelve a modificar. Una baja no: lo que desaparece, desapareció.
Esa asimetría es la que define el tono de toda esta página. No se trata de tener miedo de la sentencia, sino de entender que es la única operación sin marcha atrás y que, por lo tanto, es la única que exige pensar antes de apretar el botón. Todo lo demás —las confirmaciones, las advertencias, la decisión de no borrar— sale de esa idea.
Y hay una segunda particularidad que la distingue de las otras tres: casi nunca se puede hacer sola. Un socio que tiene préstamos no se puede eliminar sin arrastrar sus préstamos, y una factura emitida no se puede eliminar nunca. La baja es la operación que choca de frente con las claves foráneas, y ahí aparece la pregunta de fondo de esta página: ¿qué significa de verdad «dar de baja»?
La sentencia DELETE FROM
La sentencia que elimina registros es DELETE FROM, y está desarrollada en la Unidad 3: su forma, la diferencia con TRUNCATE y DROP TABLE, y el hábito de verificar con un SELECT antes de ejecutar.
DELETE FROM socios WHERE id = 47;
No hace falta repetir acá el desarrollo de la sentencia. Lo que sí es propio de esta unidad es que la baja cambia por completo de carácter: deja de ser algo que se escribe y pasa a ser un botón que aprieta cualquiera.
DELETE sabe lo que está haciendo y ve cuántos registros se vieron afectados. En la aplicación, quien aprieta el botón no ve ninguna de las dos cosas: no escribió la sentencia y no recibe ningún informe. Por eso, en las páginas que siguen, la baja tiene dos protecciones que en el gestor no existen: una pantalla de confirmación antes de ejecutar y una decisión de diseño sobre qué significa borrar.
Borrar de verdad o marcar como borrado
Acá está la decisión más importante de toda la unidad sobre las bajas, y conviene plantearla antes de escribir una línea de código, porque cambia el diseño. Hay dos formas de dar de baja algo:
| Baja física | Baja lógica | |
|---|---|---|
| Qué se hace | DELETE FROM: la fila desaparece. |
Se marca con activo = 0: la fila queda. |
| La ve el usuario | No. | No, si el sistema filtra bien. |
| El historial | Se pierde. | Se conserva. |
| Se puede deshacer | No, salvo que haya respaldo. | Sí: se vuelve a poner el 1. |
| Qué exige del resto del código | Nada. | Que toda consulta filtre WHERE activo = 1. |
Para la mayoría de los datos de un sistema real, la baja lógica es la respuesta correcta, y las razones son concretas:
- El historial suele valer más que el estado actual. Que un socio ya no esté activo no significa que sus préstamos del año pasado no hayan existido. Si se borra el socio, esos préstamos quedan apuntando a un fantasma.
- Hay información que no se puede borrar. Facturas, historias clínicas, actas de examen, registros de accesos. Muchas veces la ley o la propia institución exige conservar esos datos durante años.
- Un error se puede corregir. Con la baja física, una fila borrada por equivocación ya no existe; con la lógica, se revierte con un clic.
- Es la única que muchas veces se puede hacer. Si el socio tiene préstamos, la clave foránea va a bloquear el
DELETE. Con la baja lógica, el problema no se plantea.
Pero tiene un costo, y hay que decirlo: hay que acordarse de filtrar. Si una consulta del sistema se olvida del WHERE activo = 1, los «borrados» reaparecen como por arte de magia en esa pantalla. Es un precio real —una línea olvidada en cualquier consulta futura arruina la lógica— y la forma de pagarlo es que la columna de estado esté desde el diseño inicial, que todas las consultas se escriban con el filtro por costumbre, y que el listado de inactivos exista como pantalla propia, para que se puedan consultar cuando haga falta. Por eso activo ya estaba en el CREATE TABLE de socios: no es una columna que se agrega cuando aparece el problema, es una decisión de diseño.
El código PHP
Con la baja lógica, el código se parece más al de una modificación que al de un borrado, y eso es esperable: marcar como borrado es modificar una columna.
<?php
$bd = new PDO('mysql:host=localhost;dbname=biblioteca;charset=utf8mb4', 'usuario', 'clave');
$bd->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$id = (int) ($_POST['id'] ?? 0);
// Sin un id válido no se hace nada
if ($id <= 0) {
header('Location: listado.php?error=id');
exit;
}
// Baja lógica: la fila sigue ahí, pero deja de aparecer
$sentencia = $bd->prepare('UPDATE socios SET activo = 0 WHERE id = :id');
$sentencia->execute([':id' => $id]);
header('Location: listado.php?baja=ok');
exit;
Y esta es la versión con baja física, para el caso en que corresponda borrar de verdad:
<?php
$id = (int) ($_POST['id'] ?? 0);
if ($id > 0) {
// Antes de borrar: ¿tiene datos que dependen de él?
$consulta = $bd->prepare('SELECT COUNT(*) FROM prestamos WHERE socio_id = :id');
$consulta->execute([':id' => $id]);
if ($consulta->fetchColumn() > 0) {
// Tiene préstamos: no se puede borrar sin destruir el historial
header('Location: listado.php?error=con-prestamos');
exit;
}
$sentencia = $bd->prepare('DELETE FROM socios WHERE id = :id');
$sentencia->execute([':id' => $id]);
}
Dos cosas de ese segundo ejemplo. La primera: la comprobación se hace antes, no después de que la base rechace el borrado. Es cierto que la clave foránea lo va a bloquear igual, pero si el sistema averigua por su cuenta puede dar un mensaje claro —«no se puede dar de baja: tiene 3 préstamos sin devolver»— en lugar de un error técnico. La segunda: fetchColumn() es el método que corresponde cuando la consulta devuelve un solo valor, como un COUNT(*). Trae ese valor y nada más, sin armar toda una fila para después descartarla.
Confirmar antes de borrar
En la interfaz, una baja nunca se dispara con un solo clic. El recorrido tiene tres pasos, y el del medio es el que evita los accidentes:
- El listado, con el botón de eliminar en la fila.
- Una pantalla de confirmación, que muestra los datos de la fila que se va a dar de baja y pregunta si es eso lo que se quiere.
- La acción, que se ejecuta recién cuando el usuario confirma.
La pantalla de confirmación tiene que mostrar el dato, no solo la pregunta: «¿Confirmás dar de baja a Ana Pérez, documento 30123456?». Cuando se pregunta en abstracto —«¿Estás seguro?»— el usuario aprieta que sí sin leer, porque no tiene nada que chequear. Mostrando de qué se trata, en cambio, hay una oportunidad real de que note que era otro Pérez.
Y hay un detalle de interfaz que vale la pena: en la pantalla de confirmación, el botón que cancela conviene que sea el destacado o el que recibe el foco, no el que borra. Es una regla no escrita de los sistemas bien hechos: la acción destructiva tiene que requerir un acto deliberado, no ser el camino por defecto para quien aprieta Enter sin pensar.
Qué pasa con las relaciones
Cuando una tabla referencia a otra con una clave foránea, la base de datos tiene que decidir qué hacer si se intenta borrar la fila referenciada. Esa decisión se declara en el CREATE TABLE, con la cláusula ON DELETE:
| Cláusula | Qué hace si se borra el socio | Cuándo corresponde |
|---|---|---|
ON DELETE RESTRICTes la opción por defecto |
Impide el borrado y devuelve un error. | Cuando el historial importa y hay que decidir a mano. Es la opción segura. |
ON DELETE CASCADE |
Borra también todos los préstamos de ese socio. | Casi nunca. Solo cuando los datos dependientes no tienen valor por sí solos. |
ON DELETE SET NULL |
Les vacía la referencia, pero las filas quedan. | Cuando el dato tiene valor sin la referencia. Exige que la columna admita vacíos. |
De las tres, conviene desconfiar de CASCADE, que es la más cómoda y la más peligrosa. Un DELETE aparentemente inofensivo puede arrastrar cientos de filas en otras tablas, en cadena, sin mostrar nada antes de hacerlo. Y como encadena de tabla en tabla, el alcance real del borrado es difícil de ver a simple vista. La opción por defecto —impedir el borrado— es la que obliga al sistema a decidir con información: si esos préstamos importan, no se borra; si no importan, se borran primero, explícitamente. Es más trabajo y es lo correcto.
Resumen
- Una baja elimina una fila, y es la única operación sin marcha atrás.
DELETE FROM tabla WHERE condición: elWHEREdecide qué filas se eliminan.- No lleva lista de columnas porque borra la fila completa, no valores sueltos.
- Sin
WHEREla tabla queda vacía, sin aviso y sin recuperación. - Antes de borrar:
SELECTpara ver qué se pierde, y respaldo si es masivo. DELETE FROMvacía la tabla pero la deja;DROP TABLEelimina la tabla misma.- Hay dos formas de dar de baja: física (
DELETE) y lógica (activo = 0). - La lógica conserva el historial, se puede deshacer y no choca con las claves foráneas; su costo es que toda consulta tiene que filtrar.
- En la web, la baja nunca se dispara con un enlace: siempre un formulario con
POST. - Antes de ejecutar hay una pantalla de confirmación que muestra el dato.
ON DELETE RESTRICTimpide el borrado y es lo más seguro;CASCADEarrastra filas en cadena y hay que evitarlo.