sql >> Database >  >> RDS >> Sqlserver

Basisprincipes van SQL Server Inner Join met voorbeelden

Inleiding

Met T-SQL kunnen we records uit meer dan één tabel combineren en deze als een enkele resultaatset retourneren. Dit wordt bereikt door het concept van joins in SQL Server.

Deze mogelijkheid is vaak nodig omdat gegevens in relationele databases doorgaans worden genormaliseerd. We hebben bijvoorbeeld werknemersgegevens verspreid over twee of meer tabellen. De eerste tabel zou de basis klantgegevens zijn en genaamd werknemer. De tweede tabel is de afdeling .

De dataconsistentie vereist de juiste relatie tussen de klant en de afdeling. Om de volledige gegevens voor een groep medewerkers en hun afdelingen te retourneren, moeten beide tabellen worden samengevoegd.

De SQL-joinbewerkingen kunnen ook meer dan twee tabellen bevatten.

Een ander geval van het bestaan ​​van dergelijke externe sleutelrelaties tussen tabellen is voor samenvatting en detail tabellen.

Mensen die met de voorbeelddatabases AdventureWorks of WideWorldImporters hebben gewerkt, zijn bekend met de Sales.Orders en Sales.OrderDetails tabellen. In dit geval bevat de laatste de details van elke bestelling die is vastgelegd in de Sales.Orders tafel. Twee tabellen hebben een relatie op basis van de volgorde. We kunnen dus gegevens uit beide tabellen ophalen als een enkele resultaatset met JOINS.

Soorten SQL Server JOIN's

T-SQL staat de volgende typen joins toe:

  1. Innerlijke deelname geeft alle records terug die gemeenschappelijk zijn voor alle tabellen die bij de query betrokken zijn.
  2. Linker (buitenste) deelname retourneert alle records van links tabel en alle records van rechts tabel die ook in de linkertabel voorkomen. De termen links en rechts verwijzen naar de positie van de tabel ten opzichte van de JOIN-component.
  3. Rechts (buitenste) deelname retourneert alle records van rechts tabel en alle records van links tabel die ook in de linkertabel voorkomen. De termen zijn vergelijkbaar met het vorige geval.
  4. Volledige uiterlijke deelname retourneert alle records die in beide tabellen voorkomen, plus alle andere records uit beide tabellen. Kolommen die geen overeenkomstige rijen in de andere tabel hebben, retourneren NULL
  5. Kruis deelnemen , ook wel Cartesian Join . genoemd , retourneert het cartesiaanse product van de gegevens uit beide tabellen. Daarom bevat de uiteindelijke resultaatset voor elke rij in tabel A een toewijzing van alle rijen in tabel B en vice versa.

Dit artikel gaat over SQL INNER JOINs.

Voorbeeldtabellen

Om het concept van inner joins te demonstreren, gebruiken we drie gerelateerde tabellen uit de TSQLV4-database die is gebouwd door Itzik Ben-Gan.

De volgende lijsten tonen de structuur van deze tabellen.

-- 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

Let op de externe-sleutelrelatie tussen de custid-kolom in Sales.Orders en de custid-kolom in Sales.Customers .

Om JOIN's uit te voeren, moeten we een dergelijke gemeenschappelijke kolom specificeren als de JOIN-basis.

Er is niet strikt een externe-sleutelrelatie vereist om JOIN-query's uit te voeren, maar de kolommen die de resultatenset bepalen, moeten vergelijkbaar zijn.

Buitenlandse sleutels kunnen ook helpen bij het verbeteren van JOIN-query's, vooral als de kolom met externe sleutels is geïndexeerd.

-- 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

Voorbeeldquery's met SQL INNER JOIN

Laten we enkele voorbeeldquery's uitvoeren met een SQL INNER JOIN.

In Listing 4 voeren we een query uit die ALLE rijen ophaalt die gemeenschappelijk zijn voor de Sales.Customers-tabel en Sales.Orders-tabel. We gebruiken de custid-kolom als voorwaarde voor de join.

Merk op dat de ON-component een filter is dat erg lijkt op een WHERE-component. We hebben ook aliassen gebruikt om de tabellen te onderscheiden.

-- Listing 4: Customer Orders

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

In listing 5 beperken we de zoekopdracht tot specifieke kolommen tot de Sales.Customers tabel en de Sales.Orders tafel. We gebruiken de custid kolom als de voorwaarde voor de deelname.

Merk op dat de ON-component een filter is dat veel lijkt op een WHERE-component. We hebben ook aliassen gebruikt om de tabellen te onderscheiden.

-- 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;

In Listing 6 breiden we de gedachte uit door een WHERE-clausule te introduceren die gegevens voor een enkele klant filtert. We hebben ook aliassen toegevoegd aan de kolomlijst.

Hoewel dit in dit voorbeeld niet nodig is, zijn er gevallen waarin u kolommen met dezelfde naam uit beide tabellen moet projecteren. Vervolgens hebben de kolommen een uitdrukking nodig als tweedelige namen, waarbij de tabelaliassen of namen worden gebruikt.

-- 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';

In listing 7 introduceren we de voogdijkolom. We kunnen de kolommen onderscheiden met behulp van de alias, maar we kunnen de twee custid . niet onderscheiden kolommen in de uitvoer (zie afbeelding 4). We kunnen dit oplossen door aliassen te gebruiken:

-- 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';

In Listing 9 voegen we de tabel Sales.OrderDetails toe aan de mix. Bij het samenvoegen van meer dan twee tabellen, wordt de resultaatset van de eerste twee tabellen JOIN de linker tafel voor de volgende tafel. De volgorde van tabellen in een JOIN-query heeft echter geen invloed op de uiteindelijke uitvoer.

Merk op dat we een jokerteken gebruiken om ALLE kolommen op te halen uit de tabel 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';

Listing 10 introduceert de Production.Product-tabel die ons de productdetails toont die aan de bestelling zijn gekoppeld.

-- 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';

Niet-Equi JOINs

Aangezien de ON-component een filter is, kunnen we andere operatoren gebruiken dan de operator "=". JOIN's ondersteunen over het algemeen het gebruik van ongelijkheden zoals <,>, !=, = in de ON-clausule. Lijst 11 toont dit aan.

Als u deze zoekopdrachten uitvoert, worden verschillende resultatensets geretourneerd.

-- 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;

Conclusie

Dit artikel besprak de SQL INNER JOIN's en presenteerde voorbeelden van het gebruik ervan. Het omvatte scenario's met twee, drie en vier tabellen in dezelfde query.

Met behulp van gerelateerde tabellen hebben we ook geïllustreerd hoe we de querystructuur kunnen variëren om de uitvoer weer te geven volgens onze vereisten. We hebben ook korte voorbeelden van Non-Equi JOIN's toegevoegd.


  1. bereken een som van typetijd met behulp van sql

  2. Een login.sql-bestand maken voor SQLcl

  3. Tijdzoneconversie in SQL-query

  4. Query verwijderen om rijen in MySQL te verwijderen