我有一个架构,涉及客户,每个客户的设施,每个设施的项目以及每个项目的项目。
我想知道是否有关于每个项目列出客户的表现的一般经验法则。我认为常见的一种方式是:
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 :(得分:3)
在关于SQL的问题中,为样本数据包含DDL + DML总是好的。我将在这篇文章的末尾添加一个脚本,它将使用CTE生成一堆测试数据。
经过几次测试后,我发现连接性能更高。
如果针对单个项目ID(其中item.itemid = 500,例如)对彼此运行2个查询,则每个查询的成本为50%。
如果使用范围 - 在这种情况下,itemid介于200和8000之间,则查询开销真正有利于连接(19%到81%),并且连接执行速度更快。
我检查这样的速度:
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用于替代查询。
你的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