Introduzione
T-SQL ci consente di combinare record da più tabelle e restituirli come un unico set di risultati. Ciò si ottiene attraverso il concetto di join in SQL Server.
Questa opportunità è spesso necessaria perché i dati nei database relazionali sono generalmente normalizzati. Ad esempio, abbiamo i dati dei dipendenti distribuiti su due o più tabelle. La prima tabella sarebbe costituita dai dati di base del cliente e denominata dipendente. La seconda tabella sarebbe il dipartimento .
La coerenza dei dati richiede il corretto rapporto tra il cliente e il reparto. La restituzione dei dati completi per un insieme di dipendenti e dei relativi reparti richiede l'unione di entrambe le tabelle.
Le operazioni di join SQL possono includere anche più di due tabelle.
Un altro caso di esistenza di tali relazioni chiave esterne tra le tabelle è per il riepilogo e dettaglio tabelle.
Le persone che hanno lavorato con i database di esempio AdventureWorks o WideWorldImporters hanno familiarità con Sales.Orders e tabelle Sales.OrderDetails. In questo caso, quest'ultimo contiene i dettagli di ogni ordine registrato nel Sales.Orders tavolo. Due tabelle hanno una relazione basata sull'ordine. Pertanto, possiamo recuperare i dati da entrambe le tabelle come un unico set di risultati utilizzando JOINS.
Tipi di SQL Server JOIN
T-SQL consente i seguenti tipi di join:
- Unità interiore restituisce tutti i record comuni a tutte le tabelle coinvolte nella query.
- Unisciti a sinistra (esterno) restituisce tutti i record da sinistra tabella e tutti i record da destra tabella che si trova anche nella tabella di sinistra. I termini sinistra e destra fare riferimento alla posizione della tabella relativa alla clausola JOIN.
- Partecipa a destra (esterno) restituisce tutti i record da destra tabella e tutti i record da sinistra tabella che si trova anche nella tabella di sinistra. I termini sono simili al caso precedente.
- Partecipazione esterna completa restituisce tutti i record comuni a entrambe le tabelle, più tutti gli altri record di entrambe le tabelle. Le colonne che non hanno righe corrispondenti nell'altra tabella restituiscono NULL
- Partecipa incrociata , chiamato anche Unisci cartesiani , restituisce il prodotto cartesiano dei dati di entrambe le tabelle. Pertanto, il set di risultati finale per ciascuna riga della tabella A conterrà una mappatura di tutte le righe della tabella B e viceversa.
Questo articolo si concentrerà sugli INNER JOIN di SQL.
Tabelle di esempio
Per dimostrare il concetto di inner join, utilizziamo tre tabelle correlate dal database TSQLV4 creato da Itzik Ben-Gan.
I seguenti elenchi mostrano la struttura di queste tabelle.
-- 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
Nota la relazione della chiave esterna tra la colonna custid in Sales.Orders e la colonna custid in Sales.Customers .
Per eseguire JOIN, dobbiamo specificare una colonna così comune come base JOIN.
Non richiede strettamente una relazione di chiave esterna per eseguire query JOIN, ma le colonne che determinano il set di risultati devono essere confrontabili.
Le chiavi esterne possono anche aiutare a migliorare le query JOIN, soprattutto se la colonna della chiave esterna è indicizzata.
-- 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
Query di esempio con SQL INNER JOIN
Eseguiamo alcune query di esempio utilizzando SQL INNER JOIN.
Nel Listato 4, eseguiamo una query che recupera TUTTE le righe comuni alla tabella Sales.Customers e alla tabella Sales.Orders. Usiamo la colonna custid come condizione per l'unione.
Si noti che la clausola ON è un filtro molto simile a una clausola WHERE. Abbiamo anche usato alias per distinguere le tabelle.
-- Listing 4: Customer Orders
use TSQLV4
go
select * from Sales.Customers sc
inner join Sales.Orders so
on sc.custid=so.custid;
Nel Listato 5, restringiamo la query a colonne specifiche per Sales.Customers tabella e Vendite.Ordini tavolo. Usiamo il custid colonna come condizione per l'unione.
Osservare che la clausola ON è un filtro molto simile a una clausola WHERE. Abbiamo anche usato alias per distinguere le tabelle.
-- 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;
Nel Listato 6 estendiamo il pensiero introducendo una clausola WHERE che filtra i dati per un singolo cliente. Abbiamo anche aggiunto alias all'elenco delle colonne.
Sebbene non sia necessario in questo esempio, ci sono casi in cui è necessario proiettare colonne con lo stesso nome da entrambe le tabelle. Quindi, le colonne avranno bisogno di un'espressione come nomi in due parti, utilizzando gli alias oi nomi delle tabelle.
-- 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';
Nel listato 7, introduciamo la colonna custid. Possiamo distinguere le colonne usando l'alias, ma non possiamo distinguere i due custid colonne nell'output (vedere la figura 4). Possiamo risolvere questo problema utilizzando 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';
Nel Listato 9, aggiungiamo la tabella Sales.OrderDetails al mix. Quando si uniscono più di due tavoli, il set di risultati dei primi due tavoli JOIN diventa il sinistra tavolo per il tavolo successivo. Tuttavia, l'ordine delle tabelle in una query JOIN non influisce sull'eventuale output.
Tieni presente che utilizziamo un carattere jolly per recuperare TUTTE le colonne dalla tabella 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';
Il Listato 10 introduce la tabella Production.Product che ci mostra i dettagli del prodotto associati all'ordine.
-- 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';
JoIN non Equi
Poiché la clausola ON è un filtro, possiamo utilizzare operatori diversi dall'operatore "=". I JOIN generalmente supportano l'uso di disuguaglianze come <,>, !=, =
L'esecuzione di queste query restituirà set di risultati diversi.
-- 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;
Conclusione
Questo articolo ha discusso degli SQL INNER JOIN e ha presentato esempi del suo utilizzo. Ha coperto scenari con due, tre e quattro tabelle nella stessa query.
Utilizzando le tabelle correlate, abbiamo anche illustrato come possiamo variare la struttura della query per visualizzare l'output in base alle nostre esigenze. Abbiamo anche aggiunto brevi esempi di JOIN non Equi.