BD CLASE 5 2024-II
En este tutorial utilizaremos LEFT JOINs para
mostrar los datos de las dependencias, tengan o no, un responsable de área y
resolveremos los problemas que se generan cuando pasamos una variable vacía por
la url
¿Qué es un LEFT JOIN y cómo se diferencia de
otros tipos de JOINs?
Filtrando Registros con la Clausula WHERE:
Creando condiciones: Utilizando la cláusula
WHERE para filtrar los resultados de una consulta según criterios específicos.
Combinando condiciones: Apilando múltiples
condiciones con operadores lógicos (AND, OR, NOT).
Considerando tus requisitos, este tutorial abordará en profundidad el LEFT JOIN
en SQL, su distinción con otros tipos de JOIN
, y cómo utilizar la cláusula WHERE
para filtrar y combinar condiciones. Además, se incluirá una sección práctica sobre cómo manejar variables vacías pasadas por URL en el contexto de una aplicación web PHP/MySQL, un problema común en el desarrollo frontend-backend.
Tutorial: LEFT JOIN
y Filtrado con WHERE
en SQL, y Manejo de Variables Vacías en PHP
En este tutorial, exploraremos una de las operaciones fundamentales en bases de datos relacionales: la combinación de tablas (JOIN
). Nos centraremos en el LEFT JOIN
, una herramienta poderosa para recuperar datos de múltiples tablas, incluso cuando no hay coincidencias en una de ellas. Además, abordaremos cómo refinar los resultados con la cláusula WHERE
y cómo gestionar un desafío común en el desarrollo web: el manejo de variables vacías recibidas a través de la URL.
Requisitos Previos:
- Conocimientos básicos de SQL (
SELECT
,FROM
). - Conocimientos básicos de PHP y HTML (para la parte de la aplicación web).
- Un entorno XAMPP con Apache y MySQL funcionando.
- La base de datos
db_vehiculos_mantenimiento
o una similar con al menos dos tablas relacionadas (ej.Dependencias
yEmpleados
).
1. Preparación de la Base de Datos (Ejemplo Práctico)
Para ilustrar el LEFT JOIN
, utilizaremos dos tablas de ejemplo: Dependencias
y Empleados
. Una dependencia puede tener un responsable (un empleado), pero no todas las dependencias tienen por qué tenerlo asignado en todo momento.
Diseño de Tablas Sugerido:
-
Dependencias
:id_dependencia
(INT, PK, AUTO_INCREMENT)nombre_dependencia
(VARCHAR)ubicacion
(VARCHAR)id_responsable
(INT, FK aEmpleados.id_empleado
, nullable) - Esto es clave para elLEFT JOIN
.
-
Empleados
:id_empleado
(INT, PK, AUTO_INCREMENT)nombre_completo
(VARCHAR)cargo
(VARCHAR)
Sentencias SQL para crear y poblar las tablas:
-- Selecciona tu base de datos si no lo has hecho
-- USE db_vehiculos_mantenimiento;
-- Tabla Empleados (necesaria para el responsable)
CREATE TABLE Empleados (
id_empleado INT AUTO_INCREMENT PRIMARY KEY,
nombre_completo VARCHAR(100) NOT NULL,
cargo VARCHAR(50)
);
-- Insertar datos de ejemplo en Empleados
INSERT INTO Empleados (nombre_completo, cargo) VALUES
('Ana García', 'Gerente de TI'),
('Carlos Ruiz', 'Jefe de Ventas'),
('Laura Morales', 'Asistente Administrativo'); -- Este empleado no será responsable de una dependencia al inicio
-- Tabla Dependencias
CREATE TABLE Dependencias (
id_dependencia INT AUTO_INCREMENT PRIMARY KEY,
nombre_dependencia VARCHAR(100) NOT NULL,
ubicacion VARCHAR(100),
id_responsable INT,
FOREIGN KEY (id_responsable) REFERENCES Empleados(id_empleado)
);
-- Insertar datos de ejemplo en Dependencias
INSERT INTO Dependencias (nombre_dependencia, ubicacion, id_responsable) VALUES
('Departamento de Contabilidad', 'Edificio Principal', NULL), -- Sin responsable
('Departamento de Marketing', 'Anexo B', 2), -- Carlos Ruiz es responsable
('Soporte Técnico', 'Piso 3', 1), -- Ana García es responsable
('Recursos Humanos', 'Edificio Principal', NULL); -- Sin responsable
2. ¿Qué es un LEFT JOIN
y cómo se diferencia de otros tipos de JOIN
s?
Un JOIN
en SQL se utiliza para combinar filas de dos o más tablas basándose en una columna relacionada entre ellas. Existen varios tipos de JOIN
s, cada uno con un comportamiento diferente:
-
INNER JOIN
(o simplementeJOIN
):- Devuelve solo las filas cuando hay coincidencias en ambas tablas. Si una fila en una tabla no tiene una coincidencia en la otra, no se incluirá en el resultado.
- Uso común: Cuando necesitas información que solo existe si las dos entidades están relacionadas (ej. solo quieres ver mantenimientos que tienen un vehículo y un empleado asignados).
-
LEFT JOIN
(oLEFT OUTER JOIN
):- Devuelve todas las filas de la tabla de la "izquierda" (la primera tabla mencionada después de
FROM
). - También devuelve las filas coincidentes de la tabla de la "derecha".
- Si no hay coincidencia en la tabla de la "derecha", los campos de esa tabla aparecerán con valores
NULL
(nulos). - Uso común: Cuando quieres ver todos los registros de una tabla, y si tienen información relacionada en otra tabla, mostrarla; de lo contrario, mostrar
NULL
. Por ejemplo, mostrar todas las dependencias, tengan o no un responsable.
- Devuelve todas las filas de la tabla de la "izquierda" (la primera tabla mencionada después de
-
RIGHT JOIN
(oRIGHT OUTER JOIN
):- Es el inverso del
LEFT JOIN
. Devuelve todas las filas de la tabla de la "derecha", y las coincidentes de la izquierda. Si no hay coincidencia en la tabla de la "izquierda", los campos de esa tabla aparecerán conNULL
. - Uso común: Menos frecuente que el
LEFT JOIN
porque su funcionalidad suele lograrse simplemente invirtiendo el orden de las tablas en unLEFT JOIN
.
- Es el inverso del
-
FULL OUTER JOIN
:- Devuelve todas las filas cuando hay una coincidencia en cualquiera de las tablas. Combina los resultados del
LEFT JOIN
yRIGHT JOIN
. Si no hay coincidencia, los campos aparecerán conNULL
. - Nota: MySQL no soporta
FULL OUTER JOIN
directamente. Se emula conLEFT JOIN UNION RIGHT JOIN
.
- Devuelve todas las filas cuando hay una coincidencia en cualquiera de las tablas. Combina los resultados del
Demostración Práctica del LEFT JOIN
Vamos a utilizar nuestras tablas Dependencias
y Empleados
.
Objetivo: Mostrar todas las dependencias, y si tienen un responsable asignado, mostrar su nombre. Si no tienen responsable, el campo del responsable debe aparecer NULL
.
SELECT
d.nombre_dependencia,
d.ubicacion,
e.nombre_completo AS nombre_responsable,
e.cargo AS cargo_responsable
FROM
Dependencias d
LEFT JOIN
Empleados e ON d.id_responsable = e.id_empleado;
Resultado esperado:
Análisis del LEFT JOIN
:
Observa que "Departamento de Contabilidad" y "Recursos Humanos" aparecen en el resultado, a pesar de no tener una id_responsable
(NULL
) que coincida con ningún id_empleado
. Los campos de Empleados
(nombre_responsable
, cargo_responsable
) para estas filas son NULL
. Esto es el comportamiento característico del LEFT JOIN
.
3. Filtrando Registros con la Cláusula WHERE
La cláusula WHERE
se utiliza para especificar una o más condiciones que deben cumplirse para que un registro sea incluido en el conjunto de resultados de una consulta.
Creando Condiciones Básicas
Una condición se compone de una columna, un operador de comparación y un valor.
Operadores de comparación comunes:
=
(Igual a)!=
o<>
(Diferente de)>
(Mayor que)<
(Menor que)>=
(Mayor o igual que)<=
(Menor o igual que)BETWEEN valor1 AND valor2
(Entre dos valores, inclusivo)LIKE 'patron'
(Para búsqueda de patrones, usa%
como comodín para cero o más caracteres,_
para un solo carácter)IN (valor1, valor2, ...)
(Valor está en una lista de valores)IS NULL
(Valor es nulo)IS NOT NULL
(Valor no es nulo)
Ejemplos de WHERE
con LEFT JOIN
:
a) Mostrar solo las dependencias que sí tienen un responsable asignado:
SELECT
d.nombre_dependencia,
d.ubicacion,
e.nombre_completo AS nombre_responsable,
e.cargo AS cargo_responsable
FROM
Dependencias d
LEFT JOIN
Empleados e ON d.id_responsable = e.id_empleado
WHERE
e.id_empleado IS NOT NULL; -- O directamente d.id_responsable IS NOT NULL
Este WHERE
filtra los resultados del LEFT JOIN
para incluir solo las filas donde la tabla de la derecha (Empleados
) sí encontró una coincidencia (es decir, id_empleado
no es nulo).
b) Mostrar dependencias en una ubicación específica:
SELECT
d.nombre_dependencia,
d.ubicacion,
e.nombre_completo AS nombre_responsable
FROM
Dependencias d
LEFT JOIN
Empleados e ON d.id_responsable = e.id_empleado
WHERE
d.ubicacion = 'Edificio Principal';
Combinando Condiciones: AND
, OR
, NOT
Puedes apilar múltiples condiciones usando operadores lógicos:
AND
: Ambas condiciones deben ser verdaderas.OR
: Al menos una de las condiciones debe ser verdadera.NOT
: Niega la condición.
Ejemplos:
a) Dependencias en "Edificio Principal" Y con responsable asignado:
SELECT
d.nombre_dependencia,
d.ubicacion,
e.nombre_completo AS nombre_responsable
FROM
Dependencias d
LEFT JOIN
Empleados e ON d.id_responsable = e.id_empleado
WHERE
d.ubicacion = 'Edificio Principal' AND e.id_empleado IS NOT NULL;
b) Dependencias con responsable 'Ana García' O sin responsable asignado:
SELECT
d.nombre_dependencia,
d.ubicacion,
e.nombre_completo AS nombre_responsable
FROM
Dependencias d
LEFT JOIN
Empleados e ON d.id_responsable = e.id_empleado
WHERE
e.nombre_completo = 'Ana García' OR e.id_empleado IS NULL;
c) Todas las dependencias EXCEPTO las del 'Piso 3':
SELECT
d.nombre_dependencia,
d.ubicacion,
e.nombre_completo AS nombre_responsable
FROM
Dependencias d
LEFT JOIN
Empleados e ON d.id_responsable = e.id_empleado
WHERE
NOT d.ubicacion = 'Piso 3'; -- Equivalente a d.ubicacion != 'Piso 3'
Uso de paréntesis para agrupar condiciones:
Es crucial usar paréntesis para controlar el orden de evaluación de las operaciones lógicas, especialmente con AND
y OR
.
WHERE (condicion1 AND condicion2) OR condicion3
WHERE condicion1 AND (condicion2 OR condicion3)
4. Resolución de Problemas: Pasar una Variable Vacía por la URL en PHP
Un escenario común es tener un filtro en tu aplicación web que toma un valor de la URL (usando el método GET) para usarlo en una consulta SQL. El problema surge cuando ese valor está vacío (ej. el usuario no introduce nada en un campo de búsqueda o el parámetro no está presente en la URL).
Escenario: Queremos mostrar las dependencias, opcionalmente filtradas por un responsable específico que se pasa por la URL (?responsable=ID_EMPLEADO
). Si responsable
está vacío, queremos mostrar todas las dependencias.
Archivos Necesarios:
includes/conexion.php
(reutilizado del tutorial anterior).listado_dependencias.php
(nuestro script principal).
listado_dependencias.php
:
<?php
session_start();
include 'includes/conexion.php'; // Asegúrate de que esta ruta sea correcta para tu proyecto
include 'includes/funciones.php'; // Si tienes funciones útiles como sanear_dato
// 1. Obtener la variable de la URL
$id_responsable_filtro = $_GET['responsable'] ?? ''; // Usa el operador de fusión de null (PHP 7+)
// Si usas PHP < 7, usa: $id_responsable_filtro = isset($_GET['responsable']) ? $_GET['responsable'] : '';
// 2. Sanear y validar la variable
// Es CRUCIAL sanear cualquier entrada de usuario para prevenir inyecciones SQL
if (!empty($id_responsable_filtro)) {
// Asegurarse de que sea un número entero si esperamos un ID
$id_responsable_filtro = (int) sanear_dato($conn, $id_responsable_filtro);
} else {
// Si está vacío, le asignamos NULL o un valor que indique "no filtrar por responsable"
$id_responsable_filtro = null; // O un valor que no coincida con ningún ID, como -1
}
// 3. Construir la consulta SQL dinámicamente
$sql = "SELECT
d.id_dependencia,
d.nombre_dependencia,
d.ubicacion,
e.nombre_completo AS nombre_responsable,
e.cargo AS cargo_responsable
FROM
Dependencias d
LEFT JOIN
Empleados e ON d.id_responsable = e.id_empleado";
$params = []; // Para los parámetros de la sentencia preparada
$types = ""; // Para los tipos de los parámetros
if ($id_responsable_filtro !== null) {
// Si hay un filtro, agregamos la cláusula WHERE
$sql .= " WHERE d.id_responsable = ?";
$params[] = $id_responsable_filtro;
$types .= "i"; // 'i' para entero
}
// 4. Preparar y ejecutar la consulta (¡fundamental para seguridad!)
$stmt = $conn->prepare($sql);
if ($stmt === false) {
die("Error al preparar la consulta: " . $conn->error);
}
if ($id_responsable_filtro !== null) {
$stmt->bind_param($types, ...$params);
}
$stmt->execute();
$result = $stmt->get_result();
?>
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Listado de Dependencias</title>
<link rel="stylesheet" href="css/style.css"> </head>
<body>
<div class="container">
<h1>Listado de Dependencias</h1>
<p>Filtrar por Responsable (introduce ID de empleado):</p>
<form action="listado_dependencias.php" method="GET">
<input type="text" name="responsable" placeholder="Ej. 1, 2, etc." value="<?php echo htmlspecialchars($_GET['responsable'] ?? ''); ?>">
<input type="submit" value="Filtrar">
<a href="listado_dependencias.php">Mostrar Todos</a>
</form>
<?php if ($result->num_rows > 0): ?>
<table>
<thead>
<tr>
<th>ID Dependencia</th>
<th>Nombre Dependencia</th>
<th>Ubicación</th>
<th>Nombre Responsable</th>
<th>Cargo Responsable</th>
</tr>
</thead>
<tbody>
<?php while ($row = $result->fetch_assoc()): ?>
<tr>
<td><?php echo htmlspecialchars($row['id_dependencia']); ?></td>
<td><?php echo htmlspecialchars($row['nombre_dependencia']); ?></td>
<td><?php echo htmlspecialchars($row['ubicacion']); ?></td>
<td><?php echo htmlspecialchars($row['nombre_responsable'] ?? 'Sin Asignar'); ?></td>
<td><?php echo htmlspecialchars($row['cargo_responsable'] ?? 'N/A'); ?></td>
</tr>
<?php endwhile; ?>
</tbody>
</table>
<?php else: ?>
<p>No se encontraron dependencias con los criterios seleccionados.</p>
<?php endif; ?>
<?php
$stmt->close();
$conn->close();
?>
</div>
</body>
</html>
Explicación del Manejo de la Variable Vacía:
-
Captura de la Variable:
$id_responsable_filtro = $_GET['responsable'] ?? '';
- Aquí usamos el operador de fusión de null (
??
) (disponible desde PHP 7) que asigna''
(cadena vacía) a$id_responsable_filtro
si$_GET['responsable']
no está definido o esNULL
. Esto es más conciso queisset()
. - Si el usuario ingresa un valor pero lo deja vacío en el formulario de búsqueda, también llegará como cadena vacía.
-
Sanear y Validar (
if (!empty($id_responsable_filtro))
):empty()
: Verifica si la variable está vacía (cadena vacía,NULL
,0
,false
,array()
vacío).- Si
$id_responsable_filtro
no está vacío, significa que el usuario ha proporcionado un valor para el filtro. En ese caso, se sanea el dato y se convierte a entero para asegurar que es un ID válido. - Si
$id_responsable_filtro
sí está vacío, lo convertimos anull
. Estenull
será nuestra señal para que la consulta no incluya la cláusulaWHERE
de filtrado por responsable.
-
Construcción Dinámica de la Consulta SQL:
- La sentencia
SELECT ... LEFT JOIN ...
se define inicialmente sin la cláusulaWHERE
. - Solo si
$id_responsable_filtro
no esnull
(es decir, el usuario sí especificó un filtro), se concatenaWHERE d.id_responsable = ?
a la consulta SQL. Esto asegura que la cláusulaWHERE
solo se añada si es necesaria.
- La sentencia
-
Sentencias Preparadas (
$stmt->bind_param(...)
):- Es fundamental usar sentencias preparadas para todas las variables que provienen de la entrada del usuario. Esto previene ataques de inyección SQL.
$stmt->bind_param($types, ...$params);
solo se ejecuta si$id_responsable_filtro
no esnull
, ya que solo en ese caso$params
y$types
tendrán valores.
Cómo probarlo:
- Guarda los archivos PHP en
C:\xampp\htdocs\tu_proyecto\
. - Abre tu navegador y ve a
http://localhost/tu_proyecto/listado_dependencias.php
. Deberías ver todas las dependencias. - Prueba a filtrar:
http://localhost/tu_proyecto/listado_dependencias.php?responsable=1
(mostrará solo dependencias con responsable ID 1). - Prueba con un ID que no existe:
http://localhost/tu_proyecto/listado_dependencias.php?responsable=999
. No mostrará resultados. - Prueba con un valor vacío o ningún parámetro:
http://localhost/tu_proyecto/listado_dependencias.php?responsable=
o simplementehttp://localhost/tu_proyecto/listado_dependencias.php
. Debería mostrar todas las dependencias, incluyendo las que tienenNULL
como responsable.
Este tutorial te ha proporcionado una comprensión profunda del LEFT JOIN
para combinar tablas, cómo utilizar WHERE
para filtrar datos de manera precisa, y una solución práctica y segura para manejar variables vacías en consultas SQL en aplicaciones web PHP.
Referencias
American Psychological Association. (2020). Publication Manual of the American Psychological Association (7th ed.). American Psychological
MySQL. (s.f.). MySQL Reference Manual. Recuperado de
PHP. (s.f.). PHP Manual. Recuperado de
Comentarios
Publicar un comentario