Einführung
T-SQL ermöglicht es uns, Datensätze aus mehr als einer Tabelle zu kombinieren und sie als eine einzige Ergebnismenge zurückzugeben. Dies wird durch das Join-Konzept in SQL Server erreicht.
Diese Möglichkeit ist häufig erforderlich, da Daten in relationalen Datenbanken normalerweise normalisiert werden. Beispielsweise haben wir Mitarbeiterdaten, die auf zwei oder mehr Tabellen verteilt sind. Die erste Tabelle würde die grundlegenden Kundendaten enthalten und employee. heißen Die zweite Tabelle wäre die Abteilung .
Die Datenkonsistenz setzt die richtige Beziehung zwischen Kunde und Fachbereich voraus. Um die vollständigen Daten für eine Reihe von Mitarbeitern und deren Abteilungen zurückzugeben, müssen beide Tabellen verknüpft werden.
Die SQL-Join-Operationen können auch mehr als zwei Tabellen umfassen.
Ein weiterer Fall, in dem solche Fremdschlüsselbeziehungen zwischen Tabellen existieren, ist die Zusammenfassung und Detail Tabellen.
Personen, die mit den Beispieldatenbanken AdventureWorks oder WideWorldImporters gearbeitet haben, sind mit Sales.Orders vertraut und Sales.OrderDetails-Tabellen. In diesem Fall enthält letzteres die Details jeder Bestellung, die in Sales.Orders erfasst sind Tisch. Zwei Tabellen haben eine Beziehung basierend auf der Reihenfolge. Somit können wir mit JOINS.
Daten aus beiden Tabellen als eine einzelne Ergebnismenge abrufenTypen von SQL Server-JOINs
T-SQL erlaubt die folgenden Join-Typen:
- Innere Verbindung gibt alle Datensätze zurück, die allen an der Abfrage beteiligten Tabellen gemeinsam sind.
- Linker (äußerer) Join gibt alle Datensätze von links zurück Tabelle und alle Datensätze von rechts Tabelle, die auch in der linken Tabelle vorkommen. Die Begriffe links und richtig beziehen sich auf die Position der Tabelle relativ zur JOIN-Klausel.
- Right (Outer) Join gibt alle Datensätze von rechts zurück Tabelle und alle Datensätze von links Tabelle, die auch in der linken Tabelle vorkommen. Die Bedingungen sind ähnlich wie im vorherigen Fall.
- Vollständiger äußerer Join gibt alle Datensätze zurück, die beiden Tabellen gemeinsam sind, sowie alle anderen Datensätze aus beiden Tabellen. Spalten, die keine entsprechenden Zeilen in der anderen Tabelle haben, geben NULL zurück
- Cross Join , auch Kartesischer Join genannt , gibt das kartesische Produkt der Daten aus beiden Tabellen zurück. Daher enthält die endgültige Ergebnismenge für jede Zeile in Tabelle A eine Zuordnung aller Zeilen in Tabelle B und umgekehrt.
Dieser Artikel konzentriert sich auf SQL INNER JOINs.
Beispieltabellen
Um das Konzept innerer Verknüpfungen zu demonstrieren, verwenden wir drei verwandte Tabellen aus der von Itzik Ben-Gan erstellten TSQLV4-Datenbank.
Die folgenden Auflistungen zeigen die Struktur dieser 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
Beachten Sie die Fremdschlüsselbeziehung zwischen der custid-Spalte in Sales.Orders und die Spalte custid in Sales.Customers .
Um JOINs auszuführen, müssen wir eine solche gemeinsame Spalte als JOIN-Basis angeben.
Es ist nicht unbedingt eine Fremdschlüsselbeziehung erforderlich, um JOIN-Abfragen auszuführen, aber die Spalten, die die Ergebnismenge bestimmen, müssen vergleichbar sein.
Fremdschlüssel können auch dazu beitragen, JOIN-Abfragen zu verbessern, insbesondere wenn die Fremdschlüsselspalte indiziert ist.
-- 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
Beispielabfragen mit SQL INNER JOIN
Lassen Sie uns einige Beispielabfragen mit einem SQL INNER JOIN ausführen.
In Listing 4 führen wir eine Abfrage aus, die ALLE Zeilen abruft, die in den Tabellen „Sales.Customers“ und „Sales.Orders“ vorkommen. Wir verwenden die custid-Spalte als Bedingung für den Join.
Beachten Sie, dass die ON-Klausel ein Filter ist, der einer WHERE-Klausel sehr ähnlich ist. Wir haben auch Aliase verwendet, um die Tabellen zu unterscheiden.
-- 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 grenzen wir die Abfrage auf bestimmte Spalten zu Sales.Customers ein Tabelle und die Sales.Orders Tisch. Wir verwenden die custid Spalte als Bedingung für den Join.
Beachten Sie, dass die ON-Klausel ein Filter ist, der einer WHERE-Klausel sehr ähnlich ist. Wir haben auch Aliase verwendet, um die Tabellen zu unterscheiden.
-- 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 erweitern wir den Gedanken, indem wir eine WHERE-Klausel einführen, die Daten für einen einzelnen Kunden filtert. Wir haben auch Aliase an die Spaltenliste angehängt.
Obwohl dies in diesem Beispiel nicht erforderlich ist, gibt es Fälle, in denen Sie Spalten mit demselben Namen aus beiden Tabellen projizieren müssen. Dann benötigen die Spalten einen Ausdruck als zweiteilige Namen unter Verwendung der Tabellenaliasnamen oder -namen.
-- 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 führen wir die Spalte custid ein. Wir können die Spalten anhand des Alias unterscheiden, aber wir können die beiden custid nicht unterscheiden Spalten in der Ausgabe (siehe Abbildung 4). Wir können dies beheben, indem wir Aliase verwenden:
-- 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 fügen wir der Mischung die Tabelle Sales.OrderDetails hinzu. Wenn mehr als zwei Tabellen verknüpft werden, wird die Ergebnismenge der ersten beiden Tabellen JOIN zur linken Tabelle für die nächste Tabelle. Die Reihenfolge der Tabellen in einer JOIN-Abfrage wirkt sich jedoch nicht auf die endgültige Ausgabe aus.
Beachten Sie, dass wir einen Platzhalter verwenden, um ALLE Spalten aus der Sales.OrderDetails-Tabelle abzurufen.
-- 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 stellt die Production.Product-Tabelle vor, die uns die mit der Bestellung verknüpften Produktdetails zeigt.
-- 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';
Nicht-Equi-JOINs
Da die ON-Klausel ein Filter ist, können wir andere Operatoren als den „=“-Operator verwenden. JOINs unterstützen im Allgemeinen die Verwendung von Ungleichheiten wie <,>, !=, =
Das Ausführen dieser Abfragen gibt unterschiedliche Ergebnissätze zurück.
-- 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;
Schlussfolgerung
In diesem Artikel wurden die SQL INNER JOINs erörtert und Beispiele für ihre Verwendung vorgestellt. Es wurden Szenarien mit zwei, drei und vier Tabellen in derselben Abfrage behandelt.
Anhand verwandter Tabellen haben wir auch veranschaulicht, wie wir die Abfragestruktur variieren können, um die Ausgabe gemäß unseren Anforderungen anzuzeigen. Wir haben auch kurze Beispiele für Non-Equi JOINs hinzugefügt.