SQL Server性能:一对一连接,还是在select中选择?

时间:2017-08-15 02:13:26

标签: sql sql-server

我有一个架构,涉及客户,每个客户的设施,每个设施的项目以及每个项目的项目。

我想知道是否有关于每个项目列出客户的表现的一般经验法则。我认为常见的一种方式是:

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)

有这种差异的原因吗?如果是这样,我如何利用它来使其他查询同样更高效?

谢谢!

1 个答案:

答案 0 :(得分: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在我的场景中vs~420 MS用于替代查询。

itemid between 200 and 80000

你的where子句有什么独特之处吗?也许有一个sargability问题或其他什么。

这是DDL / DML,它创建了100个客户,~1000个设施,~10000个项目,~100000个项目:

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