Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Noções básicas de junção interna do SQL Server com exemplos

Introdução


O T-SQL nos permite combinar registros de mais de uma tabela e devolvê-los como um único conjunto de resultados. Isso é obtido por meio do conceito de junções no SQL Server.

Essa oportunidade geralmente é necessária porque os dados em bancos de dados relacionais são normalmente normalizados. Por exemplo, temos dados de funcionários espalhados por duas ou mais tabelas. A primeira tabela seria os dados básicos do cliente e chamada de funcionário. A segunda tabela seria o departamento .

A consistência dos dados exige o correto relacionamento entre o cliente e o departamento. Retornar os dados completos de um conjunto de funcionários e seus departamentos requer a junção de ambas as tabelas.

As operações de junção SQL também podem incluir mais de duas tabelas.

Outro caso de tal existência de relacionamentos de chave estrangeira entre tabelas é para resumo e detalhe mesas.

As pessoas que trabalharam com os bancos de dados de exemplo AdventureWorks ou WideWorldImporters estão familiarizadas com os Sales.Orders e tabelas Sales.OrderDetails. Neste caso, este último contém os detalhes de cada pedido registrado em Vendas.Pedidos tabela. Duas tabelas têm um relacionamento baseado na ordem. Assim, podemos recuperar dados de ambas as tabelas como um único conjunto de resultados usando JOINS.

Tipos de JOINs do SQL Server


O T-SQL permite os seguintes tipos de junções:
  1. Inner Join retorna todos os registros comuns a todas as tabelas envolvidas na consulta.
  2. União Esquerda (Externa) retorna todos os registros da esquerda tabela e todos os registros da direita tabela que também ocorrem na tabela à esquerda. Os termos esquerda e certo consulte a posição da tabela em relação à cláusula JOIN.
  3. Juntar direito (externo) retorna todos os registros da direita tabela e todos os registros da esquerda tabela que também ocorrem na tabela à esquerda. Os termos são semelhantes ao caso anterior.
  4. União externa completa retorna todos os registros comuns a ambas as tabelas, além de todos os outros registros de ambas as tabelas. Colunas que não têm linhas correspondentes na outra tabela retornam NULL
  5. Cross Join , também chamado de União Cartesiana , retorna o produto cartesiano dos dados de ambas as tabelas. Portanto, o conjunto de resultados final para cada linha da tabela A conterá um mapeamento de todas as linhas da tabela B e vice-versa.

Este artigo se concentrará em SQL INNER JOINs.

Tabelas de amostra


Para demonstrar o conceito de junções internas, usamos três tabelas relacionadas do banco de dados TSQLV4 construído por Itzik Ben-Gan.

As listagens a seguir mostram a estrutura dessas tabelas.
-- 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

Observe a relação de chave estrangeira entre a coluna custid em Sales.Orders e a coluna custid em Sales.Customers .

Para executar JOINs, devemos especificar uma coluna comum como a base JOIN.

Ele não requer estritamente um relacionamento de chave estrangeira para executar consultas JOIN, mas as colunas que determinam o conjunto de resultados devem ser comparáveis.

As chaves estrangeiras também podem ajudar a melhorar as consultas JOIN, especialmente se a coluna de chave estrangeira for 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

Exemplos de consultas com SQL INNER JOIN


Vamos executar algumas consultas de exemplo usando um SQL INNER JOIN.

Na Listagem 4, executamos uma consulta que busca TODAS as linhas comuns às tabelas Sales.Customers e Sales.Orders. Usamos a coluna custid como condição para a junção.

Observe que a cláusula ON é um filtro muito parecido com uma cláusula WHERE. Também usamos aliases para distinguir as tabelas.
-- Listing 4: Customer Orders

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

Na Listagem 5, restringimos a consulta a colunas específicas para Sales.Customers tabela e os Vendas.Pedidos tabela. Usamos o custid coluna como a condição para a junção.

Observe que a cláusula ON é um filtro muito parecido com uma cláusula WHERE. Também usamos aliases para distinguir as tabelas.
-- 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;

Na Listagem 6, estendemos o pensamento introduzindo uma cláusula WHERE que filtra dados para um único cliente. Também anexamos aliases à lista de colunas.

Embora não seja necessário neste exemplo, há casos em que você precisa projetar colunas com o mesmo nome de ambas as tabelas. Em seguida, as colunas precisarão de uma expressão como nomes de duas partes, usando os aliases ou nomes da tabela.
-- 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';

Na listagem 7, apresentamos a coluna custid. Podemos distinguir as colunas usando o alias, mas não podemos distinguir os dois custid colunas na saída (veja a Figura 4). Podemos corrigir isso usando aliases:
-- 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';

Na Listagem 9, adicionamos a tabela Sales.OrderDetails ao mix. Ao unir mais de duas tabelas, o conjunto de resultados das duas primeiras tabelas JOIN torna-se o esquerdo tabela para a próxima tabela. No entanto, a ordem das tabelas em uma consulta JOIN não afeta a saída eventual.

Observe que usamos um curinga para buscar TODAS as colunas da tabela 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';

A Listagem 10 apresenta a tabela Production.Product mostrando os detalhes do produto associados ao 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';

JoINs não Equi


Como a cláusula ON é um filtro, podemos usar outros operadores além do operador “=”. JOINs geralmente suportam o uso de desigualdades como <,>, !=, = na cláusula ON. A Listagem 11 demonstra isso.

A execução dessas consultas retornará conjuntos de resultados diferentes.
-- 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;

Conclusão


Este artigo discutiu os SQL INNER JOINs e apresentou exemplos de seu uso. Ele cobriu cenários com duas, três e quatro tabelas na mesma consulta.

Usando tabelas relacionadas, também ilustramos como podemos variar a estrutura da consulta para exibir a saída de acordo com nossos requisitos. Também adicionamos breves exemplos de Non-Equi JOINs.