sql >> Base de Datos >  >> RDS >> Sqlserver

Conceptos básicos de unión interna de SQL Server con ejemplos

Introducción

T-SQL nos permite combinar registros de más de una tabla y devolverlos como un único conjunto de resultados. Esto se logra a través del concepto de uniones en SQL Server.

Esta oportunidad suele ser necesaria porque los datos de las bases de datos relacionales suelen estar normalizados. Por ejemplo, tenemos datos de empleados distribuidos en dos o más tablas. La primera tabla serían los datos básicos del cliente y se llamaría empleado. La segunda tabla sería el departamento .

La consistencia de los datos requiere la correcta relación entre el cliente y el departamento. Devolver los datos completos de un conjunto de empleados y sus departamentos requiere unir ambas tablas.

Las operaciones de combinación SQL también pueden incluir más de dos tablas.

Otro caso de existencia de tales relaciones de clave externa entre tablas es para resumen y detalle mesas.

Las personas que trabajaron con las bases de datos de muestra AdventureWorks o WideWorldImporters están familiarizadas con Sales.Orders y las tablas Sales.OrderDetails. En este caso, este último contiene el detalle de cada pedido registrado en el Sales.Orders mesa. Dos tablas tienen una relación basada en el orden. Por lo tanto, podemos recuperar datos de ambas tablas como un solo conjunto de resultados usando JOINS.

Tipos de JOIN de SQL Server

T-SQL permite los siguientes tipos de uniones:

  1. Unión interna devuelve todos los registros comunes a todas las tablas involucradas en la consulta.
  2. Unión izquierda (exterior) devuelve todos los registros desde la izquierda tabla y todos los registros de la derecha tabla que también aparecen en la tabla de la izquierda. Los términos izquierda y correcto consulte la posición de la tabla en relación con la cláusula JOIN.
  3. Unión derecha (externa) devuelve todos los registros de la derecha tabla y todos los registros desde la izquierda tabla que también aparecen en la tabla de la izquierda. Los términos son similares al caso anterior.
  4. Unión externa completa devuelve todos los registros comunes a ambas tablas, además de todos los demás registros de ambas tablas. Las columnas que no tienen filas correspondientes en la otra tabla devuelven NULL
  5. Unión cruzada , también llamado Unión cartesiana , devuelve el producto cartesiano de los datos de ambas tablas. Por lo tanto, el conjunto de resultados final para cada fila de la tabla A contendrá una asignación de todas las filas de la tabla B y viceversa.

Este artículo se centrará en SQL INNER JOIN.

Tablas de muestra

Para demostrar el concepto de uniones internas, usamos tres tablas relacionadas de la base de datos TSQLV4 creada por Itzik Ben-Gan.

Los siguientes listados muestran la estructura de estas tablas.

-- Listing 1: Structure of the Sales.Customers Table

CREATE TABLE [Sales].[Customers](
	[custid] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
	[companyname] [nvarchar](40) NOT NULL,
	[contactname] [nvarchar](30) NOT NULL,
	[contacttitle] [nvarchar](30) NOT NULL,
	[address] [nvarchar](60) NOT NULL,
	[city] [nvarchar](15) NOT NULL,
	[region] [nvarchar](15) NULL,
	[postalcode] [nvarchar](10) NULL,
	[country] [nvarchar](15) NOT NULL,
	[phone] [nvarchar](24) NOT NULL,
	[fax] [nvarchar](24) NULL,
 CONSTRAINT [PK_Customers] PRIMARY KEY CLUSTERED 
(
	[custid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO

Tenga en cuenta la relación de clave externa entre la columna custid en Sales.Orders y la columna custid en Sales.Customers .

Para realizar JOIN, debemos especificar una columna tan común como la base JOIN.

No requiere estrictamente una relación de clave externa para ejecutar consultas JOIN, pero las columnas que determinan el conjunto de resultados deben ser comparables.

Las claves externas también pueden ayudar a mejorar las consultas JOIN, especialmente si la columna de clave externa está indexada.

-- Listing 2: Structure of the Sales.Orders Table

CREATE TABLE [Sales].[Orders](
	[orderid] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
	[custid] [int] NULL,
	[empid] [int] NOT NULL,
	[orderdate] [date] NOT NULL,
	[requireddate] [date] NOT NULL,
	[shippeddate] [date] NULL,
	[shipperid] [int] NOT NULL,
	[freight] [money] NOT NULL,
	[shipname] [nvarchar](40) NOT NULL,
	[shipaddress] [nvarchar](60) NOT NULL,
	[shipcity] [nvarchar](15) NOT NULL,
	[shipregion] [nvarchar](15) NULL,
	[shippostalcode] [nvarchar](10) NULL,
	[shipcountry] [nvarchar](15) NOT NULL,
 CONSTRAINT [PK_Orders] PRIMARY KEY CLUSTERED 
(
	[orderid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO

ALTER TABLE [Sales].[Orders] ADD  CONSTRAINT [DFT_Orders_freight]  DEFAULT ((0)) FOR [freight]
GO
ALTER TABLE [Sales].[Orders]  WITH CHECK ADD  CONSTRAINT [FK_Orders_Customers] FOREIGN KEY([custid])
REFERENCES [Sales].[Customers] ([custid])
GO
ALTER TABLE [Sales].[Orders] CHECK CONSTRAINT [FK_Orders_Customers]
GO
ALTER TABLE [Sales].[Orders]  WITH CHECK ADD  CONSTRAINT [FK_Orders_Employees] FOREIGN KEY([empid])
REFERENCES [HR].[Employees] ([empid])
GO
ALTER TABLE [Sales].[Orders] CHECK CONSTRAINT [FK_Orders_Employees]
GO
ALTER TABLE [Sales].[Orders]  WITH CHECK ADD  CONSTRAINT [FK_Orders_Shippers] FOREIGN KEY([shipperid])
REFERENCES [Sales].[Shippers] ([shipperid])
GO
ALTER TABLE [Sales].[Orders] CHECK CONSTRAINT [FK_Orders_Shippers]
GO
-- Listing 3: Structure of the Sales.OrderDetails Table

CREATE TABLE [Sales].[OrderDetails](
	[orderid] [int] NOT NULL,
	[productid] [int] NOT NULL,
	[unitprice] [money] NOT NULL,
	[qty] [smallint] NOT NULL,
	[discount] [numeric](4, 3) NOT NULL,
 CONSTRAINT [PK_OrderDetails] PRIMARY KEY CLUSTERED 
(
	[orderid] ASC,
	[productid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO

ALTER TABLE [Sales].[OrderDetails] ADD  CONSTRAINT [DFT_OrderDetails_unitprice]  DEFAULT ((0)) FOR [unitprice]
GO
ALTER TABLE [Sales].[OrderDetails] ADD  CONSTRAINT [DFT_OrderDetails_qty]  DEFAULT ((1)) FOR [qty]
GO
ALTER TABLE [Sales].[OrderDetails] ADD  CONSTRAINT [DFT_OrderDetails_discount]  DEFAULT ((0)) FOR [discount]
GO
ALTER TABLE [Sales].[OrderDetails]  WITH CHECK ADD  CONSTRAINT [FK_OrderDetails_Orders] FOREIGN KEY([orderid])
REFERENCES [Sales].[Orders] ([orderid])
GO
ALTER TABLE [Sales].[OrderDetails] CHECK CONSTRAINT [FK_OrderDetails_Orders]
GO
ALTER TABLE [Sales].[OrderDetails]  WITH CHECK ADD  CONSTRAINT [FK_OrderDetails_Products] FOREIGN KEY([productid])
REFERENCES [Production].[Products] ([productid])
GO
ALTER TABLE [Sales].[OrderDetails] CHECK CONSTRAINT [FK_OrderDetails_Products]
GO
ALTER TABLE [Sales].[OrderDetails]  WITH CHECK ADD  CONSTRAINT [CHK_discount] CHECK  (([discount]>=(0) AND [discount]<=(1)))
GO
ALTER TABLE [Sales].[OrderDetails] CHECK CONSTRAINT [CHK_discount]
GO
ALTER TABLE [Sales].[OrderDetails]  WITH CHECK ADD  CONSTRAINT [CHK_qty] CHECK  (([qty]>(0)))
GO
ALTER TABLE [Sales].[OrderDetails] CHECK CONSTRAINT [CHK_qty]
GO
ALTER TABLE [Sales].[OrderDetails]  WITH CHECK ADD  CONSTRAINT [CHK_unitprice] CHECK  (([unitprice]>=(0)))
GO
ALTER TABLE [Sales].[OrderDetails] CHECK CONSTRAINT [CHK_unitprice]
GO

Consultas de muestra con SQL INNER JOIN

Ejecutemos algunas consultas de muestra usando SQL INNER JOIN.

En el Listado 4, ejecutamos una consulta que obtiene TODAS las filas comunes a las tablas Sales.Customers y Sales.Orders. Usamos la columna custid como condición para la unión.

Observe que la cláusula ON es un filtro muy parecido a una cláusula WHERE. También hemos utilizado alias para distinguir las tablas.

-- Listing 4: Customer Orders

use TSQLV4
go
select * from Sales.Customers sc
inner join Sales.Orders so
on sc.custid=so.custid;

En el Listado 5, restringimos la consulta a columnas específicas para Sales.Customers y la tabla Sales.Orders mesa. Usamos el custid columna como condición para la unión.

Observe que la cláusula ON es un filtro muy parecido a una cláusula WHERE. También hemos utilizado alias para distinguir las tablas.

-- Listing 5: Customer Orders with specific Rows
use TSQLV4
go
select
contactname
, contacttitle
, address
, orderid
, orderdate
, shipaddress
, shipcountry
from Sales.Customers sc
inner join Sales.Orders so
on sc.custid=so.custid;

En el Listado 6, ampliamos la idea al introducir una cláusula WHERE que filtra datos para un solo cliente. También hemos añadido alias a la lista de columnas.

Si bien no es necesario en este ejemplo, hay casos en los que necesita proyectar columnas con el mismo nombre de ambas tablas. Luego, las columnas necesitarán una expresión como nombres de dos partes, usando los alias o nombres de la tabla.

-- Listing 6: Customer Orders for a Single Customer
use TSQLV4
go
select 
sc.contactname
, sc.contacttitle
, sc.address
, so.orderid
, so.orderdate
, so.shipaddress
, so.shipcountry
from Sales.Customers sc
inner join Sales.Orders so
on sc.custid=so.custid
where sc.contactname='Allen, Michael';

En el listado 7, presentamos la columna custid. Podemos distinguir las columnas usando el alias, pero no podemos distinguir los dos custid columnas en la salida (Ver Figura 4). Podemos arreglar esto usando alias:

-- Listing 7: Customer Orders for a Single Customer with Common Column
use TSQLV4
go
select 
sc.custid
, sc.contactname
, sc.contacttitle
, sc.address
, so.custid
, so.orderid
, so.orderdate
, so.shipaddress
, so.shipcountry
from Sales.Customers sc
inner join Sales.Orders so
on sc.custid=so.custid
where sc.contactname='Allen, Michael';
-- Listing 8: Customer Orders for a Single Customer with Aliased Column
use TSQLV4
go
select 
sc.custid customer_custid
, sc.contactname
, sc.contacttitle
, sc.address
, so.custid order_custid
, so.orderid
, so.orderdate
, so.shipaddress
, so.shipcountry
from Sales.Customers sc
inner join Sales.Orders so
on sc.custid=so.custid
where sc.contactname='Allen, Michael';

En el Listado 9, agregamos la tabla Sales.OrderDetails a la mezcla. Al unir más de dos tablas, el conjunto de resultados de las dos primeras tablas JOIN se convierte en el izquierdo mesa para la próxima mesa. Sin embargo, el orden de las tablas en una consulta JOIN no afecta el resultado final.

Tenga en cuenta que usamos un comodín para obtener TODAS las columnas de la tabla Sales.OrderDetails.

-- Listing 9: Inner Join with Three Tables

use TSQLV4
go
select 
sc.custid customer_custid
, sc.contactname
, sc.contacttitle
, sc.address
, so.custid order_custid
, so.orderid
, so.orderdate
, so.shipaddress
, so.shipcountry
, sod.*
from Sales.Customers sc
inner join Sales.Orders so
on sc.custid=so.custid
inner join Sales.OrderDetails sod
on so.orderid=sod.orderid
where sc.contactname='Allen, Michael';

El Listado 10 presenta la tabla Production.Product que nos muestra los detalles del producto asociados con el pedido.

-- Listing 10: Inner Join with Four Tables

use TSQLV4
go
select 
sc.custid customer_custid
, sc.contactname
, sc.contacttitle
, sc.address
, so.custid order_custid
, so.orderid
, so.orderdate
, so.shipaddress
, so.shipcountry
, sod.*
, pp.productname
, pp.unitprice
from Sales.Customers sc
inner join Sales.Orders so
on sc.custid=so.custid
inner join Sales.OrderDetails sod
on so.orderid=sod.orderid
inner join Production.Products pp
on sod.productid=pp.productid
where sc.contactname='Allen, Michael';

UNIONES no equivalentes

Dado que la cláusula ON es un filtro, podemos usar otros operadores además del operador "=". Los JOIN generalmente admiten el uso de desigualdades como <,>, !=, = en la cláusula ON. El Listado 11 demuestra esto.

Ejecutar estas consultas arrojará diferentes conjuntos de resultados.

-- Listing 11: Non-Equi JOINs, "Equal to"
use TSQLV4
go
select
contactname
, contacttitle
, address
, orderid
, orderdate
, shipaddress
, shipcountry
from Sales.Customers sc
inner join Sales.Orders so
on sc.custid=so.custid;
-- Listing 12: Non-Equi JOINs, "Not Equal to"
use TSQLV4
go
select
contactname
, contacttitle
, address
, orderid
, orderdate
, shipaddress
, shipcountry
from Sales.Customers sc
inner join Sales.Orders so
on sc.custid<>so.custid;
-- Listing 13: Non-Equi JOINs, "Less than OR Equal to"
use TSQLV4
go
select
contactname
, contacttitle
, address
, orderid
, orderdate
, shipaddress
, shipcountry
from Sales.Customers sc
inner join Sales.Orders so
on sc.custid<=so.custid;

Conclusión

Este artículo discutió SQL INNER JOIN y presentó ejemplos de su uso. Cubría escenarios con dos, tres y cuatro tablas en la misma consulta.

Usando tablas relacionadas, también ilustramos cómo podemos variar la estructura de la consulta para mostrar el resultado de acuerdo con nuestros requisitos. También hemos agregado breves ejemplos de JOIN no equivalentes.