Mostrando las entradas con la etiqueta sql. Mostrar todas las entradas
Mostrando las entradas con la etiqueta sql. Mostrar todas las entradas

miércoles, 2 de septiembre de 2026

Consultas entre dos o más tablas en SQL Server

SQL Server utiliza uniones para recuperar datos de varias tablas basándose en las relaciones lógicas entre ellas. Las combinaciones son fundamentales para las operaciones de bases de datos relacionales y permiten combinar datos de dos o más tablas en un único conjunto de resultados.

SQL Server implementa tanto operaciones de unión lógica (definidas por la sintaxis de Transact-SQL) como operaciones de unión física (los algoritmos que se utilizan para ejecutar las uniones). Comprender ambos aspectos ayuda a escribir consultas eficientes y a optimizar el rendimiento de la base de datos.

Las uniones indican la forma en la que SQL Server debe usar los datos de una tabla para seleccionar las filas de otra. 

Una condición de unión define cómo se relacionan dos tablas en una consulta mediante:
  • La especificación de la columna de cada tabla que se utilizará para la unión. Una condición de unión típica especifica una clave foránea de una tabla y su clave asociada en la otra.
  • La especificación de un operador lógico (por ejemplo, = o <>,) que se utilizará para comparar los valores de las columnas.
Las uniones se expresan lógicamente mediante la siguiente sintaxis de Transact-SQL:
  • INNER JOIN 
  • LEFT JOIN 
  • RIGHT JOIN 
  • FULL  JOIN 
En la unión de tipo Inner Join se obtienen los datos que son comunes a 2 o más tablas en las que se realiza la consulta. Left Join devuelve todos los registros de la tabla izquierda y también los que coinciden con los registros de la tabla de la derecha (La tabla de la izquierda es la primer tabla que se define en la consulta de SQL, la de la derecha es la segunda tabla que se define en la consulta), además, si no hay coincidencia entre ambas tablas no se devuelven resultados de registros. Right join es el caso contrario, devuelve todos los registros de la tabla derecha y también los que coinciden con los registros de la tabla de la izquierda. Full Join devuelve todos los registros de las tablas, los de la tabla izquierda, los de la derecha y los que coinciden en ambas tablas.

De manera visual la uniones se muestran en la siguiente imagen.




Ejemplos
Se puede usar el siguiente código para ejemplificar el uso de INNER JOIN.

CREATE TABLE clientes ( 
idcliente int NOT NULL primary key,
nombre varchar(20) NOT NULL,
apellido varchar(30) NOT NULL,
direccion varchar(100) NOT NULL,
ciudad varchar(50) NOT NULL,
telefono numeric(10) NULL,
);

CREATE TABLE ordenes(
id_orden int NOT NULL primary key,
idcliente int foreign key references clientes(idcliente),
fecha_orden date default getdate(),
id_vendedor int NOT NULL
);

SELECT ordenes.id_orden, Clientes.nombre, Clientes.apellido, ordenes.fecha_orden -- De
la tabla ordenes elige el id de la orden, de la tabla de clientes el nombre y el
apellido y de la tabla de ordenes la fecha
FROM ordenes -- Para esto, usa la tabla de ordenes
INNER JOIN Clientes on ordenes.idcliente = Clientes.idCliente; -- y por medio un inner
JOIN con la tabla Clientes trae esos campos CUANDO el id del cliente de la tabla de
ordenes sea el mismo id del cliente en la tabla de Clientes

SELECT ordenes.id_orden, Clientes.nombre, Clientes.apellido, ordenes.fecha_orden,
ordenes.id_vendedor -- También podemos traer el id del vendedor para saber quien le ha
vendido productos al cliente Juan
FROM ordenes
INNER JOIN Clientes ON ordenes.idcliente = Clientes.idCliente
WHERE nombre = 'Juan' ORDER BY fecha_orden; -- Realizamos la misma consulta anterior
pero filtrando para el cliente con nombre Juan y ordenamos por fecha

Este código se puede ver de otra manera: Se toma cada registro de la tabla de ordenes y se checa con qué cliente está asociado esa orden por medio del id del cliente, después de esta verificación se van a devolver el nombre y el apellido en la tabla de clientes pero también el id de la orden y la fecha de la orden en la tabla de ordenes, estos campos se devuelven para cada uno de los registros de la tabla de ordenes.

Si se desea saber cuáles clientes tienen ordenes de servicio y cuáles no las tienen, se puede hacer uso de left join porque de esta manera podemos obtener aquellos registros que están relacionados con la tabla de ordenes pero también aquellos que no están relacionados, esto cumple con la definición de left join que devuelve los propios registros de la tabla izquierda y también sus coincidencias con respecto a otra tabla con la que está relacionada.

El siguiente ejemplo de código muestra cómo se puede usar left join para solucionar el problema previamente explicado.

SELECT Clientes.nombre, Clientes.apellido, ordenes.id_orden, ordenes.fecha_orden -- Devuelve el nombre y el apellido de la tabla Clientes y el id de orden y la fecha de
la tabla de ordenes
FROM Clientes --(Clientes es la tabla de la izquierda o primer tabla) Para esto, usa la tabla de ordenes
LEFT JOIN ordenes on Clientes.idcliente = ordenes.idcliente -- y por medio un left join con la tabla ordenes trae esos campos CUANDO el id del cliente de la tabla de ordenes sea el mismo id del cliente en la tabla de Clientes
order by id_orden; -- ordena la consulta por el id de la orden

SELECT cli.nombre, cli.apellido, ord.id_orden, ord.fecha_orden
FROM Clientes cli -- La tabla Clientes usa 'cli' como alias
LEFT JOIN ordenes ord on cli.idcliente = ord.idcliente -- La tabla ordenes usa 'ord' como alias
order by id_orden;

Referencias
Para obtener más información sobre las uniones, consultar el siguiente enlace.


miércoles, 3 de septiembre de 2025

Modularización de consultas por medio de procedimientos almacenados en SQL Server

En SQL Server un procedimiento almacenado es un grupo de una o más instrucciones de Transact-SQL (Lenguaje de programación). Estos procedimientos aceptan parámetros de entrada y devuelven múltiples valores en forma de parámetros de salida al programa que llama al procedimiento, además contienen instrucciones de programación que realizan operaciones en la base de datos. También, estos procedimientos devuelven un valor de estado al programa que llamo al procedimiento para indicar éxito o fracaso (y el motivo del fracaso).

Gracias al control de flujo que ofrece SQL Server es que podemos usar los procedimientos almacenados. Como se describió, Transact-SQL tiene diversas instrucciones que nos permiten emplear estructuras de control tal como en los lenguajes de programación, es decir, podemos usar construcciones del lenguaje para modificar el flujo de ejecución de las instrucciones de un programa.

Los procedimientos almacenados tienen la ventaja de que permiten modularizar soluciones de software al encapsular el código en unidades manejables y con ello facilitar su reutilización. Además, con el uso de procedimientos almacenados los comandos de estos se ejecutan como un único lote de código, esto puede reducir significativamente el tráfico de red entre el servidor y el cliente, ya que solo se envía la llamada a través de la red para ejecutar un procedimiento determinado. Sin la encapsulación de código que proporciona un procedimiento, cada línea de código tendría que atravesar la red.

Creación de procedimientos almacenados
La sentencia CREATE PROCEDURE es la que nos permite la creación y persistencia de bloques de código, además, cuando se usa esta sentencia, el procedimiento se guarda en la carpeta Databases > NombreBaseDeDatos > Programmability > Stored procedures, aquí es donde podemos encontrar todos los bloques de código que se han guardado en la propia base de datos. La carpeta de procedimientos almacenados se muestra en la siguiente imagen.


Como ejemplo de código, vamos a suponer que tenemos una tabla de productos que tiene un campo cantidad y que deseamos actualizar los valores de este campo, para esto, podemos crear un procedimiento almacenado que nos va permitir llamarlo cada vez que lo necesitemos sin tener que volver a escribir el código en su interior. El código es el que se muestra en seguida.

CREATE PROCEDURE p_actualiza_stock
AS
BEGIN
    IF EXISTS(SELECT * FROM productos
            WHERE cantidad = 0)
        UPDATE 
            productos SET cantidad = 20
        WHERE 
            cantidad <= 5;
END

Con el código anterior se guarda el procedimiento y ahora solo falta ejecutarlo por medio de la sentencia EXEC, esto se muestra a continuación.

EXECUTE p_actualiza_stock;

Es importante mencionar que cada vez que un procedimiento se guarda y modifica en la base de datos, será necesario ejecutarlo o mandarlo a llamar por medio de la sentencia EXEC.

Actualización de procedimientos almacenados
Para actualizar o modificar un procedimiento que ya guardamos previamente, tenemos que usar la sentencia ALTER PROCEDURE. Como ejemplo, podemos modificar el procedimiento que describimos previamente, en este caso podemos agregar 10 unidades más a cada artículo que tenga 20 elementos, entonces el código es el siguiente.

ALTER PROCEDURE p_actualiza_stock
AS
BEGIN
    IF EXISTS(SELECT * FROM productos
            WHERE cantidad = 10)
        UPDATE 
            productos SET cantidad = 30
        WHERE 
            cantidad = 20;
END

De igual manera, la instrucción ALTER modifica y guarda el procedimiento en la base de datos en cuestión.

Borrar procedimientos almacenados
Para borrar procedimientos almacenados tenemos que usar la instrucción DROP PROCEDURE, entonces para borrar el procedimiento con el que hemos estado trabajando tenemos que usar el código que muestra a continuación.

DROP PROCEDURE p_actualiza_stock;

Con esto, el módulo de actualización de stock ya no estará disponible en la carpeta Stored Procedures de la base de datos.

Parámetros de entrada en procedimientos almacenados
Para usar parámetros o variables al interior de una procedimiento almacenado tenemos que declarar las variables antes del propio código de ejecución, es decir, antes de la clausula AS, recordemos que la declaración de variables tiene la sintaxis @nombreVar TipoDeDato = valor, además, si se usan varias variables tenemos que separarlas por comas. En el siguiente ejemplo se muestra un procedimiento almacenado que nos permite buscar un producto con determinados valores en cada campo. En este caso la tabla de productos tiene los campos nombre, precio y cantidad (existencia) en su estructura.

CREATE OR ALTER PROCEDURE p_buscar_producto
@producto VARCHAR(40) = 'Microcontrolador',
@precioProducto SMALLMONEY = 29.99,
@existencia INT = 35
AS
SELECT * FROM productos
WHERE
    nombre = @producto AND
    precio = @precioProducto AND
    cantidad = @existencia;

En esta ocasión se uso la palabra reservada ALTER en el encabezado del procedimiento almacenado, con esto podemos crear y/o modificar un procedimiento en el mismo lugar sin la necesidad de realizar estas operaciones por separado.

Referencias
En los siguientes enlaces se puede obtener más detalles técnicos sobre las sentencias de código usadas en este artículo.

viernes, 29 de agosto de 2025

Proyecto de clínica de especialidades usando Microsoft Access

Este proyecto tiene como objetivo implementar una base de datos en Microsoft Access que será para una clínica de especialidades y tiene como finalidad llevar el control de citas de los diferentes médicos. La base de datos tiene tres tablas, Citas, Médicos y Pacientes, a continuación se describen los campos que contendrá cada tabla.
  • Médicos: idMedico, nombre, apellidoPaterno, especialidad
  • Pacientes: idPaciente, nombre, apellidoPaterno, apellidoMaterno, género, numeroDeExpediente, calleYNumero, colonia, codigoPostal, entidadFederativa, telefono, correoElectronico
  • Citas: idCita, idMedico, idPaciente, fechaCita, horaCita, costoCita, observaciones
El desarrollo de esta base de datos se describe a continuación.

Creación de tablas
La creación de las tablas se realizó por medio de lenguaje SQL. Es importante recordar que la ejecución de sentencias SQL se realiza por medio de la pestaña Crear, grupo Consultas, opción Diseño de consulta y después en la ventana de consulta tenemos que elegir Vista SQL por medio del uso de clic derecho. A continuación se muestran los códigos utilizados para crear cada una de estas tablas.

CREATE TABLE Medicos(
idMedico INTEGER,
nombre CHAR(40) NOT NULL,
apellidoPaterno CHAR(40) NOT NULL,
especialidad CHAR(40) NOT NULL,
CONSTRAINT PK_enlace_medico PRIMARY KEY (idMedico)
);

CREATE TABLE Pacientes(
idPaciente INTEGER,
nombre CHAR(40) NOT NULL,
apellidoPaterno CHAR(40) NOT NULL,
apellidoMaterno CHAR(40) NOT NULL,
sexo CHAR(20) NOT NULL,
numeroDeExpediente NOT NULL,
calleYNumero CHAR(40) NOT NULL,
colonia CHAR(40) NOT NULL,
codigoPostal CHAR(5) NOT NULL,
entidadFederativa CHAR(40) NOT NULL,
telefono CHAR(20),
correoElectronico CHAR(40),
CONSTRAINT PK_enlace_pacientes PRIMARY KEY (idPaciente)
);

CREATE TABLE Citas(
idCita INTEGER NOT NULL,
idMedico INTEGER NOT NULL,
idPaciente INTEGER NOT NULL,
fechaCita DATETIME NOT NULL,
horaCita CHAR(10) NOT NULL,
costoCita MONEY NOT NULL,
observaciones CHAR(80),
CONSTRAINT PK_enlace_citas PRIMARY KEY (idCita)
);

Configuración de relaciones entre tablas
El siguiente paso es establecer la relación entre estas tablas, para esto tenemos que hacer uso de las restricciones, esto se puede lograr por medio de las sentencia ALTER TABLE y CONSTRAINT. Las instrucciones usadas para lograr esto se muestran en seguida.

ALTER TABLE Citas ADD CONSTRAINT FK_citas_pacientes FOREIGN KEY (idPaciente) 
REFERENCES Pacientes(idPaciente);

ALTER TABLE Citas ADD CONSTRAINT FK_citas_medicos FOREIGN KEY (idMedico)
REFERENCES Medicos(idMedico);

Access genera un diagrama entidad-relación que resulta de la ejecución de las instrucciones anteriores, este se muestra en la siguiente imagen.


Desde luego, la creación de tablas, relaciones y el diagrama entidad-relación también se puede generar por medio del uso de la interfaz gráfica de usuario de Access.

Inserción de registros
Ya que se tiene la estructura de la base de datos entonces podemos proceder a realizar la inserción de registros en las tablas correspondientes, para ellos tenemos que hacer uso de la instrucción INSERT INTO pero se hará una excepción, es decir, se usarán las macros de VBA en Access para realizar la inserción de varios registros debido a que la ventana de consultas de este software solo permite insertar un registro a la vez pero por medio de las macros podemos introducir gran cantidad de registros usando una sola subrutina. Esta subrutina se muestra a continuación.

Sub conectarConDbActual()
    Dim conexionDB As New ADODB.Connection
    Set conexionDB = CurrentProject.Connection
    
    'Inserción de registros en tabla Medicos
    conexionDB.Execute ("INSERT INTO Medicos VALUES(1, 'Julio',         'Rodriguez', 'Cardiología')")
    conexionDB.Execute ("INSERT INTO Medicos VALUES(2, 'Pedro',         'Estrada', 'Neurología')")
    conexionDB.Execute ("INSERT INTO Medicos VALUES(3, 'Mariana',         'Hurtado', 'Dermatología')")
    conexionDB.Execute ("INSERT INTO Medicos VALUES(4, 'Luis',             'Fernández', 'Oncología')")
    conexionDB.Execute ("INSERT INTO Medicos VALUES(5, 'Sonia',         'Martínez', 'Radiología')")

    'Inserción de registros en tabla Pacientes
    conexionDB.Execute ("INSERT INTO Pacientes VALUES(1, 'Alberto',     'Ramírez', 'Camarena', 'Masculino', 10, 'Calle 3, No 9', 'Valle     de Bravo', '99502', 'CDMX', '0987640123',                            'alberto@micorreo.com')")
    conexionDB.Execute ("INSERT INTO Pacientes VALUES(2, 'Maria',         'Juarez', 'López', 'Femenino', 30, 'Calle 9, No 8', 'Valle de         Anáhuac', '88412', 'Estado de México', '8904321456',                 'maria@micorreo.com')")
    conexionDB.Execute ("INSERT INTO Pacientes VALUES(3, 'Adriana',     'Domínguez', 'Antulio', 'Femenino', 36, 'Calle 13, No 90',             'Héreoes', '89022', 'CDMX', '5589098901',                             'adriana@micorreo.com')")
    conexionDB.Execute ("INSERT INTO Pacientes VALUES(4, 'Fernando',     'Jiménez', 'Fernández', 'Masculino', 45, 'Calle 23, No 4',             'Laureles', '09789', 'CDMX', '550000000021',                         'fernando@micorreo.com')")
    conexionDB.Execute ("INSERT INTO Pacientes VALUES(5, 'Antonio',     'Perales', 'Ochoa', 'Masculino', 48, 'Calle 33, No 2', 'Cedros',     '09034', 'Estado de México', '0923567891',                             'kevin@micorreo.com')")
    
    'Inserción de registros en tabla Citas
    conexionDB.Execute ("INSERT INTO Citas VALUES(1, 1, 1, '2025-05-    25', '10:00', 70, 'Ninguna')")
    conexionDB.Execute ("INSERT INTO Citas VALUES(2, 2, 1, '2025-06-    25', '13:00', 70, 'Ninguna')")
    conexionDB.Execute ("INSERT INTO Citas VALUES(3, 1, 2, '2025-01-    25', '16:00', 80, 'Ninguna')")
    conexionDB.Execute ("INSERT INTO Citas VALUES(4, 2, 2, '2025-02-    25', '09:00', 80, 'Ninguna')")
    conexionDB.Execute ("INSERT INTO Citas VALUES(5, 1, 3, '2025-03-    25', '14:00', 90, 'Ninguna')")
    conexionDB.Execute ("INSERT INTO Citas VALUES(6, 2, 3, '2025-04-    25', '15:00', 90, 'Ninguna')")
    conexionDB.Execute ("INSERT INTO Citas VALUES(7, 1, 4, '2025-11-    25', '16:00', 100, 'Ninguna')")
    conexionDB.Execute ("INSERT INTO Citas VALUES(8, 2, 4, '2025-12-    25', '15:00', 100, 'Ninguna')")
    conexionDB.Execute ("INSERT INTO Citas VALUES(9, 1, 5, '2025-10-    25', '15:00', 50, 'Ninguna')")
    conexionDB.Execute ("INSERT INTO Citas VALUES(10, 2, 5, '2025-11-    25', '17:00', 50, 'Ninguna')")
End Sub

La siguiente imagen muestra los registros colocados en las tablas de este proyecto de Access (Clic en cada imagen para ampliarla).




Pruebas y resultados
Para probar la correcta estructuración de la base de datos podemos ejecutar algunas consultas multitabla que nos permitan observar las relaciones que fueron creadas, para esto se tiene la opción de usar uniones, específicamente se va usar INNER JOIN para obtener los datos comunes a dos o más tablas. Por ejemplo, vamos a realizar una consulta para obtener la fecha de la cita, la hora y el costa de la misma, también vamos a extraer el nombre del paciente de esa cita, su entidad federativa y el nombre del médico que va atender a ese paciente. El código para llevar a cabo esto es el siguiente.

SELECT 
    Citas.fechaCita, Citas.horaCita, Citas.costoCita, Pacientes.nombre, Pacientes.entidadFederativa, Medicos.nombre
FROM 
    (Citas
INNER JOIN 
    Pacientes ON Citas.idPaciente = Pacientes.idPaciente) 
INNER JOIN 
    Medicos ON Citas.idMedico = Medicos.idMedico;

El resultado (Vista de hoja de datos) que devuelve esta consulta se muestra en la siguiente imagen.


Otra consulta que puede ser muy útil es extraer todas las citas que tiene un médico junto con datos del paciente asociado, para esto podemos usar la clausula INNER JOIN y la clausula WHERE para definir la condición de extraer solo los datos de columnas de un id de médico específico. El código de esta consulta se muestra en seguida.

SELECT 
    Citas.fechaCita, Citas.horaCita, Medicos.nombre, Medicos.idMedico
FROM 
    Citas 
INNER JOIN 
    Medicos ON Citas.idMedico =  Medicos.idMedico
WHERE 
    Medicos.idMedico = 1;

Esta consulta devuelve la tabla de datos que se muestra en la siguiente imagen.


También podemos hacer uso de uniones de tipo LEFT JOIN para extraer todos los médicos o pacientes que no tienen ninguna cita pero que están presentes en la base de datos, esto puede ser útil para casos como contactar a clientes con los que no se ha tenido comunicación o que no han tenido la oportunidad de asistir a la clínica  y por tanto no tienen citas recientes en el sistema, o bien, puede se útil para asignar citas y pacientes a médicos que por el momento están más libres de tiempo. En seguida se proporcionan dos consultas del tipo descrito.

SELECT Pacientes.nombre, Pacientes.apellidoPaterno, Pacientes.telefono, Citas.idCita
FROM
    Pacientes
LEFT JOIN
    Citas ON Pacientes.idPaciente = Citas.idPaciente;

SELECT Medicos.nombre, Medicos.apellidoPaterno, Citas.idCita
FROM
    Medicos
LEFT JOIN
    Citas ON Medicos.idMedico = Citas.idMedico;

Los resultados de estas consultas se muestran en las siguientes imágenes.




En estas imágenes se puede ver que aquellos pacientes sin citas y médicos sin citas muestran una casilla vacía de id de citas, esto se debe a que LEFT JOIN devuelve los resultados propios de cada tabla (registros sin cita) y los comunes a ambas tablas (registros con cita).

Además, de las consultas multitabla que se usaron previamente, podemos usar RIGHT JOIN para traer todos los registros de la tabla de citas (definida como tabla derecha) junto con los registros coincidentes de la tabla de médicos o pacientes, en este caso, en el sistema todas las citas están asignadas, por tanto no se van a devolver resultados vacíos de parte de la tabla de citas. La consulta RIGHT JOIN se describe a continuación y contiene los id de cada cita pero también el nombre y apellido de cada paciente relacionado con esa cita.

SELECT citas.idCita, Pacientes.nombre, Pacientes.apellidoPaterno
FROM 
    Pacientes
RIGHT JOIN 
    Citas ON Pacientes.idPaciente = Citas.idPaciente;

El resultado devuelto por esta consulta se muestra a continuación.


Finalmente, la estructura del proyecto creada con los procesos anteriores se puede observar en la siguiente imagen, los objetos que están presentes son las tablas,  consultas y los módulos que contienen los códigos de VBA.