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.


martes, 23 de septiembre de 2025

Limpieza de datos en Excel

La limpieza de datos es el proceso de identificar y corregir los errores e incoherencias en los conjuntos de datos sin procesar para mejorar la calidad de la información

Ventajas de la limpieza de datos
Disponer de datos limpios aumentará la productividad general y permitirá obtener información de la más alta calidad para la toma de decisiones. Algunas de las ventajas son las siguientes.

  • Eliminación de errores cuando se usan múltiples fuentes de datos. 
  • La disminución de errores da como resultado clientes más felices y empleados menos frustrados.
  • Monitoreo de errores y mejores informes que permiten ver de dónde provienen los errores, lo que facilita la reparación de datos incorrectos o corruptos para futuras aplicaciones.
A continuación se describen algunas herramientas comunes de Excel que nos van a permitir limpiar un conjunto de datos.

Corrección ortográfica
Con esta herramienta podemos encontrar errores ortográficos, además, podemos buscar valores que no se usan de forma coherente, como los nombres de productos o servicios de una columna en una base de datos. Esta herramienta se puede encontrar en la pestaña Revisar y en el grupo con el mismo nombre. A continuación se muestra un ejemplo de uso.


Eliminación de espacios y caracteres
Es posible que en nuestro conjunto de datos haya caracteres y espacios en blanco adicionales a los que deberían de estar, para resolver esto podemos usar las funciones LIMPIAR y ESPACIOS, es específico, LIMPIAR quita los caracteres que no se pueden imprimir y ESPACIOS borra todos los espacios con excepción de aquellos que ya se dejan entre palabras. En seguida se muestra un ejemplo de uso de la función que elimina espacios.

Formato condicional
Con la herramienta de formato condicional podemos visualizar tablas con registros y elementos duplicados, una vez que estos hayan sido identificados podemos usar la opción de Quitar duplicados que se encuentra en la pestaña de Datos. Como ejemplo, si tenemos una columna que debería tener valores de código únicos para cada elemento, entonces podemos usar estas herramientas para quitar tales registros repetidos, véase la siguiente imagen.


Uso adecuado de mayúsculas y minúsculas
Es posible que nuestros datos no estén bien escritos, por ejemplo, los nombres y apellidos no hacen uso de iniciales mayúsculas o bien, es necesario que haya datos con todas las letras en mayúscula o minúscula, para abordar esto podemos usar las fórmulas de MAYUSC, MINUSC y NOMPROPIO que nos ofrece Excel. A continuación se muestra un ejemplo de uso de la formula para nombres propios.

Separar texto en varias columnas
Otra herramienta que puede ser de utilidad es la separación del texto en un celda entre varias celdas, para esto, Excel tiene la herramienta llamada texto en columnas, como ejemplo de uso, podemos separar el nombre y apellido de las personas que estén en una sola columna, o bien, separar ciudad y país. En seguida se muestra un ejemplo de texto a columnas.

Formato de fechas
En distintas ocasiones los datos de fechas no van a estar en el formato adecuado, más bien estarán en un formato diferente al propio para el dato, entonces se puede hacer uso de la herramienta de convertir texto a columnas para realizar la corrección, en este caso no se va elegir ningún delimitador para realizar la separación, además, solo vamos a elegir el formato que tendrá la fecha resultante. En la siguiente imagen se muestra como se puede configurar el formato de la fecha de salida para lograr nuestro objetivo.

Celdas vacías
En el caso de registros con celdas o campos vacíos primero se tiene que buscar la manera de obtener los datos por diversos medios, ya sea con el dueño de la base de datos, la página web desde donde se descargo la información, etc. sin embargo, cuando esto no es posible entonces podemos proceder a eliminar los registros con datos faltantes para que el análisis devuelva mejores resultados, para esto podemos filtrar las columnas y una vez hecho esto podemos borrar los registros. En la siguiente imagen se muestra una tabla que contiene nombres de producto faltantes, podemos filtrar por medio de ese campo y luego elegir que hacer con tales elementos de acuerdo a las posibilidades.

Uso de macros

La limpieza de datos en Excel puede ser repetitiva, tardada y propensa a errores, para optimizar este tipo de flujos de trabajo podemos usar las herramientas de Visual Basic y así agilizar diversos procesos. En el siguiente ejemplo se presenta una subrutina que permite limpiar datos y quitar espacios innecesarios en columnas con datos erróneos.

Sub limpiar_quitar_espacios()
    Dim valorRango As String
    valorRango = "A2:A100"
    Dim celdaIndice As Range
    Dim valorCelda As String
    Sheets("Productos").Select
       
    For Each celdaIndice In Range(valorRango)
        valorCelda = celdaIndice.Value
        celdaIndice.Value = WorksheetFunction.Clean(valorCelda) 
        valorCelda = celdaIndice.Value
        celdaIndice.Value = WorksheetFunction.Trim(valorCelda)
    Next
    
    valorRango = "B2:B100"
    For Each celdaIndice In Range(valorRango)
        valorCelda = celdaIndice.Value
        celdaIndice.Value = WorksheetFunction.Clean(valorCelda) 
        valorCelda = celdaIndice.Value
        celdaIndice.Value = WorksheetFunction.Trim(valorCelda)
    Next
End Sub

Esta subrutina elimina los espacios y caracteres indeseables en dos rangos diferentes, en las columnas A y B, para esto es necesario reiniciar la variable valorRango con las celdas a recorrer, además se tienen que llamar a las funciones Clean y Trim que son las mismas que LIMPIAR Y ESPACIOS que se usan en el entorno gráfico de las hojas de un libro de Excel.

Referencias
A continuación se muestran los enlaces que describen los detalles técnicos de los métodos y objetos de Visual Basic utilizados en este artículo.

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.