はじめに
T-SQLを使用すると、複数のテーブルのレコードを組み合わせて、単一の結果セットとして返すことができます。これは、SQLServerの結合の概念によって実現されます。
リレーショナルデータベースのデータは通常正規化されているため、この機会が必要になることがよくあります。たとえば、従業員データが2つ以上のテーブルに分散しているとします。最初のテーブルは基本的な顧客データであり、従業員と呼ばれます。 2番目のテーブルは部門になります 。
データの一貫性には、顧客と部門の間の正しい関係が必要です。一連の従業員とその部門の完全なデータを返すには、両方のテーブルを結合する必要があります。
SQL結合操作には、3つ以上のテーブルを含めることもできます。
テーブル間にこのような外部キー関係が存在するもう1つのケースは、要約です。 および詳細em> テーブル。
AdventureWorksまたはWideWorldImportersサンプルデータベースを使用したことがある人は、 Sales.Ordersに精通しています。 およびSales.OrderDetailsテーブル。この場合、後者には Sales.Ordersに記録された各注文の詳細が含まれます テーブル。 2つのテーブルには、順序に基づいた関係があります。 したがって、JOINSを使用して、両方のテーブルから単一の結果セットとしてデータを取得できます。
SQLServer結合の種類
T-SQLでは、次の種類の結合が可能です。
- 内部参加 クエリに関係するすべてのテーブルに共通するすべてのレコードを返します。
- 左(外側)に参加 左からすべてのレコードを返します テーブルと右のすべてのレコード 左側のテーブルにもあるテーブル。 左という用語 および正しい JOIN句に対するテーブルの位置を参照してください。
- 右(外部)参加 右からすべてのレコードを返します テーブルと左のすべてのレコード 左側のテーブルにもあるテーブル。用語は前のケースと同様です。
- 完全な外部参加 両方のテーブルに共通するすべてのレコードに加えて、両方のテーブルからの他のすべてのレコードを返します。他のテーブルに対応する行がない列は、 NULLを返します
- クロスジョイン 、 デカルト結合とも呼ばれます 、両方のテーブルからのデータのデカルト積を返します。したがって、テーブルAの各行の最終結果セットには、テーブルBのすべての行のマッピングが含まれ、その逆も同様です。
この記事では、SQL内部結合に焦点を当てます。
サンプルテーブル
内部結合の概念を示すために、ItzikBen-Ganによって構築されたTSQLV4データベースの3つの関連テーブルを使用します。
次のリストは、これらのテーブルの構造を示しています。
-- 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
Sales.Ordersのcustid列間の外部キーの関係に注意してください。 およびSales.Customersのcustid列 。
JOINを実行するには、JOINベースなどの共通列を指定する必要があります。
JOINクエリを実行するために外部キーの関係を厳密に要求する必要はありませんが、結果セットを決定する列は比較可能である必要があります。
外部キーは、特に外部キー列にインデックスが付けられている場合に、JOINクエリの改善にも役立ちます。
-- 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
サンプルクエリ SQLINNERJOINを使用
SQLINNERJOINを使用していくつかのサンプルクエリを実行してみましょう。
リスト4では、Sales.CustomersテーブルとSales.Ordersテーブルに共通するすべての行をフェッチするクエリを実行します。結合の条件としてcustid列を使用します。
ON句はWHERE句に非常によく似たフィルタであることに注意してください。また、テーブルを区別するためにエイリアスを使用しました。
-- Listing 4: Customer Orders
use TSQLV4
go
select * from Sales.Customers sc
inner join Sales.Orders so
on sc.custid=so.custid;
リスト5では、クエリを特定の列に絞り込み、 Sales.Customers テーブルとSales.Orders テーブル。 custidを使用します 結合の条件としての列。
ON句がWHERE句と非常によく似たフィルタであることに注意してください。また、テーブルを区別するためにエイリアスを使用しました。
-- 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;
リスト6では、単一の顧客のデータをフィルター処理するWHERE句を導入することで考え方を拡張しています。また、列リストにエイリアスを追加しました。
この例では必要ありませんが、両方のテーブルから同じ名前の列を投影する必要がある場合があります。次に、テーブルのエイリアスまたは名前を使用して、列に2つの部分からなる名前としての式が必要になります。
-- 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';
リスト7では、custid列を紹介します。エイリアスを使用して列を区別することはできますが、2つの custid を区別することはできません。 出力の列(図4を参照)。エイリアスを使用してこれを修正できます:
-- 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';
リスト9では、Sales.OrderDetailsテーブルをミックスに追加しています。 3つ以上のテーブルを結合すると、最初の2つのテーブルJOINの結果セットが左になります。 次のテーブルのテーブル。ただし、JOINクエリ内のテーブルの順序は、最終的な出力には影響しません。
ワイルドカードを使用して、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';
リスト10は、注文に関連付けられた製品の詳細を示すProduction.Productテーブルを示しています。
-- 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';
非等価結合
ON句はフィルターであるため、「=」演算子以外の演算子を使用できます。 JOINは通常、ON句での<、>、!=、=<、=>などの不等式の使用をサポートします。リスト11はこれを示しています。
これらのクエリを実行すると、異なる結果セットが返されます。
-- 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;
結論
この記事では、SQL INNER JOINについて説明し、その使用例を示しました。同じクエリに2つ、3つ、および4つのテーブルがあるシナリオについて説明しました。
関連するテーブルを使用して、要件に応じて出力を表示するためにクエリ構造を変更する方法も示しました。非等価JOINの簡単な例も追加しました。