2017-08-15 51 views
2

我有一個模式,涉及客戶,每個客戶的設施,每個設施的項目和每個項目的項目。SQL Server性能:一對一連接,還是在select中選擇?

我想知道是否有關於列出每個項目的客戶的性能的一般經驗法則。我想一個辦法是常見的是:

SELECT 
    item.iteminfo 
    customer.customerinfo 
FROM 
    item 
INNER JOIN 
    project ON item.projectid = project.projectid 
INNER JOIN 
    facility ON project.facilityid = facility.facilityid 
INNER JOIN 
    customer ON facility.customerid = customer.customerid 
WHERE 
    (item filtering criteria) 

每個表都在他們的主鍵和外鍵索引。

然而,在標杆,我發現這是稍微更高性能:

SELECT 
    item.iteminfo 
    (SELECT TOP 1 customerinfo 
    FROM customer 
    WHERE customerid = (SELECT TOP 1 customerid 
         FROM facility 
         WHERE facilityid = (SELECT TOP 1 facilityid 
              FROM project 
              WHERE project.projectid = item.projectid) 
         ) 
    ) 
FROM 
    item 
WHERE 
    (item filtering criteria) 

是否有這種差異的一個原因?如果是這樣,我該如何利用它來使其他查詢具有更高的性能?

謝謝!

+0

沒有辦法確切知道W/O看到實際的執行計劃這兩個查詢。 –

+0

個人喜好,你可能通過第二種方式獲得的最小收益被它的可讀性/可維護性遠遠抵消。 –

+0

加入肯定應該有更好的表現。你的桌子有多少行?嘗試執行具有數百萬行的查詢。 – Fabio

回答

3

在關於SQL的問題中,爲示例數據包含DDL + DML總是很好。我將在本文末尾添加一個腳本,它將使用CTE生成一堆測試數據。

經過一些測試,我發現連接更高性能。

如果針對單個項目ID(其中item.itemid = 500,例如)針對彼此運行2個查詢,則每個查詢的成本爲50%。

itemid = 500

如果使用範圍 - 在這種情況下,爲itemid 200和8000之間,查詢成本開始真正有利於連接(19%至81%),以及加入執行得更快一致。

itemid between 200 and 8000

我檢查的速度是這樣的:

declare @start datetime 
set @start = getdate() 

Query 1 

select getdate() - @start 
set @start = getdate() 

Query 2 

select getdate() - @start 

如果增加範圍200和80000之間爲itemid,你看到更多的分離 - 查詢費用就像是5%至95 %贊成連接,並且連接在我的方案中執行〜330 MS,對於替代查詢,執行〜420 MS。

itemid between 200 and 80000

有什麼真正的獨特之處的where子句?也許有一個可控性問題或其他問題。

這裏的DDL/DML,創建100個客戶,〜1000個設施,〜10000個項目,〜10萬級的物品:

create table customer (customerid int primary key,customerinfo varchar(25)) 
create table facility (facilityid int primary key, customerid int foreign key references customer(customerid)) 
create table project (projectid int primary key, facilityid int foreign key references facility(facilityid)) 
create table item (itemid int primary key, iteminfo varchar(25), projectid int foreign key references project(projectid)) 
GO 

;with cte as 
(select 1 as id, 'customer' + cast(1 as varchar(5)) as info 
union all 
select cte.id + 1 as id, 'customer' + cast(cte.id + 1 as varchar(5)) 
from cte 
where cte.id < 100) 
insert into customer select id, info from cte 
option(maxrecursion 100) 
GO 

;with cte as 
(select 1 as id, 1 as customerid 
union all 
select cte.id + 1, ((cte.id + 1)/10) + 1 
from cte 
where cte.id < 999) 
insert into facility select id, customerid from cte 
option(maxrecursion 1000) 
GO 

;with cte as 
(select 1 as id, 1 as facilityid 
union all 
select cte.id + 1, ((cte.id + 1)/10) + 1 
from cte 
where cte.id < 9989) 
insert into project select id, facilityid from cte 
option(maxrecursion 10000) 
GO 

;with cte as 
(select 1 as id, 1 as projectid, 'item' + cast(1 as varchar(5)) as iteminfo 
union all 
select cte.id + 1, ((cte.id + 1)/10) + 1, 'item' + cast(cte.id + 1 as varchar(5)) 
from cte 
where cte.id < 99889) 
insert into item select id, iteminfo, projectid from cte 
option(maxrecursion 0) 
GO 
+0

非常好的答案。我只會改變第一句話 - 在關於sql的問題中,包含DDL + DML的示例數據總是很好。 –

+1

@ZoharPeled偉大的反饋 - 謝謝 - 相應地調整了我的答案。 –