Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

Основи за вътрешно присъединяване на SQL Server с примери

Въведение

T-SQL ни позволява да комбинираме записи от повече от една таблица и да ги връщаме като единичен набор от резултати. Това се постига чрез концепцията за присъединяване в SQL Server.

Тази възможност често е необходима, тъй като данните в релационни бази данни обикновено се нормализират. Например, имаме данни за служителите, разпределени в две или повече таблици. Първата таблица ще бъде основните данни за клиентите и ще се нарича служител. Втората таблица ще бъде отдел .

Съгласуваността на данните изисква правилна връзка между клиента и отдела. Връщането на пълните данни за набор от служители и техните отдели изисква присъединяване на двете таблици.

SQL операциите за присъединяване могат също да включват повече от две таблици.

Друг случай на съществуване на такива връзки с външни ключове между таблиците е за обобщение и подробности таблици.

Хората, които са работили с примерните бази данни на AdventureWorks или WideWorldImporters, са запознати с Sales.Orders и таблици Sales.OrderDetails. В този случай последният съдържа подробностите за всяка поръчка, записана в Sales.Orders маса. Две таблици имат връзка въз основа на порядъка. По този начин можем да извлечем данни от двете таблици като единичен набор от резултати, използвайки JOINS.

Типове JOIN на SQL Server

T-SQL позволява следните типове обединения:

  1. Вътрешно присъединяване връща всички записи, общи за всички таблици, включени в заявката.
  2. Ляво (външно) присъединяване връща всички записи от ляво таблица и всички записи от дясно таблица, които също се срещат в лявата таблица. Термините вляво и вдясно отнасят се до позицията на таблицата спрямо клаузата JOIN.
  3. Дясно (външно) присъединяване връща всички записи от дясно таблица и всички записи от ляво таблица, които също се срещат в лявата таблица. Условията са подобни на предишния случай.
  4. Пълно външно присъединяване връща всички записи, общи за двете таблици, плюс всички други записи от двете таблици. Колоните, които нямат съответни редове в другата таблица, връщат NULL
  5. Кръстосано присъединяване , наричан още декартово присъединяване , връща декартовото произведение на данните от двете таблици. Следователно крайният набор от резултати за всеки ред в таблица A ще съдържа съпоставяне на всички редове в таблица B и обратно.

Тази статия ще се фокусира върху SQL INNER JOIN.

Примерни таблици

За да демонстрираме концепцията за вътрешни съединения, използваме три свързани таблици от базата данни TSQLV4, изградена от Ицик Бен-Ган.

Следните списъци показват структурата на тези таблици.

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

Обърнете внимание на връзката с външния ключ между колоната custid в Sales.Orders и колоната клиент в Sales.Customers .

За да изпълняваме 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

Примерни заявки с SQL INNER JOIN

Нека изпълним някои примерни заявки с помощта на SQL INNER JOIN.

В листинг 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 таблицата и Продажби.Поръчки маса. Ние използваме 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, която филтрира данни за един клиент. Ние също така добавихме псевдоними към списъка с колони.

Въпреки че не е необходимо в този пример, има случаи, когато трябва да проектирате колони с едно и също име от двете таблици. След това колоните ще се нуждаят от израз като имена от две части, като се използват псевдоними или имена на таблицата.

-- 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. Можем да различим колоните с помощта на псевдонима, но не можем да различим двата 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 към микса. При присъединяване на повече от две таблици, наборът от резултати от първите две таблици 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';

СЪЕДИНЯВАНИЯ, които не са Equi

Тъй като клаузата 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 и представи примери за тяхното използване. Той обхваща сценарии с две, три и четири таблици в една и съща заявка.

Използвайки свързани таблици, ние също така илюстрирахме как можем да променим структурата на заявката, за да покажем изхода според нашите изисквания. Добавихме и кратки примери за неравноправни JOIN.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Методът SqlDataAdapter.Fill бавен

  2. Автоматизирано извличане на номера на версията от .Dtsx файлове

  3. Възможно ли е да се зададе схема по подразбиране от низ за връзка?

  4. Как да вмъкнете стойност в колона за идентичност ръчно в таблица на SQL Server - SQL Server / T-SQL урок, част 41

  5. Как да добавите потребителска група на Active Directory като вход в SQL Server