惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
Stack Overflow Blog
Stack Overflow Blog
Hugging Face - Blog
Hugging Face - Blog
博客园_首页
T
The Blog of Author Tim Ferriss
博客园 - 叶小钗
N
Netflix TechBlog - Medium
腾讯CDC
C
Check Point Blog
P
Proofpoint News Feed
Engineering at Meta
Engineering at Meta
GbyAI
GbyAI
S
SegmentFault 最新的问题
F
Fortinet All Blogs
美团技术团队
U
Unit 42
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
博客园 - 司徒正美
F
Full Disclosure
Recorded Future
Recorded Future
D
DataBreaches.Net
博客园 - 【当耐特】
Martin Fowler
Martin Fowler
J
Java Code Geeks
I
InfoQ
Y
Y Combinator Blog
A
About on SuperTechFans
AI
AI
爱范儿
爱范儿
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Forbes - Security
Forbes - Security
W
WeLiveSecurity
M
MIT News - Artificial intelligence
雷峰网
雷峰网
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Simon Willison's Weblog
Simon Willison's Weblog
Schneier on Security
Schneier on Security
The GitHub Blog
The GitHub Blog
Security Archives - TechRepublic
Security Archives - TechRepublic
aimingoo的专栏
aimingoo的专栏
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
G
GRAHAM CLULEY
Know Your Adversary
Know Your Adversary
Latest news
Latest news
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
D
Docker
Recent Commits to openclaw:main
Recent Commits to openclaw:main
量子位
V2EX - 技术
V2EX - 技术
Project Zero
Project Zero

博客园 - 家中慢步

AKShare 高频请求东财数据接口的异常问题及解决方案 quartz 2.2.1 jdbc 连接池参数配置 httpclient发送request请求时设置header和timeout redmine 安装roadmap 插件 centos 下自动备份redmine 数据 centos 5.6 安装redmine 步骤 解决mysql 写入中文读出乱码的问题 SVN的Redmine集成插件 Quartz.net Tutorial Lesson 2 Redmine 导入AD用户 RedMine 邮件通知配置 teamlab与redmine试用对比报告 Redmine集成LDAP认证 Redmine 初体验 jqgrid 属性说明 为sql server客户端连接添加别名 [转载]sql server T-SQL 区分字符串大小写 的两种方法 [转载]sql server 常用存储过程 Quartz.net Tutorial Lesson 1
[原创]sql server inner join 效率测试
家中慢步 · 2011-08-23 · via 博客园 - 家中慢步

 今天有兄弟提到inner join,大表与小表的前后顺序会产生查询开销的差异,对此进行了如下测试:

一, 创建测试表, t1, t2, t3

-- create t1.
CREATETABLE[dbo].[T1](
[UID][uniqueidentifier]NOTNULL,
[Name][varchar](20) NULL,
[Memo][varchar](20) NULL,
CONSTRAINT[PK_T1]PRIMARYKEYCLUSTERED
(
[UID]ASC
)
WITH (PAD_INDEX =OFF, STATISTICS_NORECOMPUTE =OFF, IGNORE_DUP_KEY =OFF, ALLOW_ROW_LOCKS =ON, ALLOW_PAGE_LOCKS =ON) ON[PRIMARY]
)
ON[PRIMARY]GO
-- create t2.
CREATETABLE[dbo].[T2](
[UID][uniqueidentifier]NOTNULL,
[Name][varchar](20) NULL,
[Memo][varchar](20) NULL,
CONSTRAINT[PK_T2]PRIMARYKEYCLUSTERED
(
[UID]ASC
)
WITH (PAD_INDEX =OFF, STATISTICS_NORECOMPUTE =OFF, IGNORE_DUP_KEY =OFF, ALLOW_ROW_LOCKS =ON, ALLOW_PAGE_LOCKS =ON) ON[PRIMARY]
)
ON[PRIMARY]GO-- create t3.
CREATETABLE[dbo].[T3](
[UID][uniqueidentifier]NOTNULL,
[Name][varchar](20) NULL,
[Memo][varchar](20) NULL,
CONSTRAINT[PK_T3]PRIMARYKEYCLUSTERED
(
[UID]ASC
)
WITH (PAD_INDEX =OFF, STATISTICS_NORECOMPUTE =OFF, IGNORE_DUP_KEY =OFF, ALLOW_ROW_LOCKS =ON, ALLOW_PAGE_LOCKS =ON) ON[PRIMARY]
)
ON[PRIMARY]GO

二, 写入测试数据, t1 20w, t2, 2000, t3 30w.

-- insert t1.
declare@IDXint
set@IDX=0
while@IDX<200000
begin
insert T1 selectNEWID(),cast(@IDXasvarchar),'aaaa';
set@IDX=@IDX+1
end-- insert t2.
declare@IDXint
set@IDX=0
while@IDX<2000
begin
insert T2 selectNEWID(),cast(@IDXasvarchar),'aaaa';
set@IDX=@IDX+1
end-- insert t3.
declare@IDXint
set@IDX=0
while@IDX<300000
begin
insert T3 selectNEWID(),cast(@IDXasvarchar),'aaaa';
set@IDX=@IDX+1
end

在Name字段创建索引

/****** Object:  Index [IX_T1_Name]    Script Date: 08/23/2011 18:13:51 ******/
CREATENONCLUSTEREDINDEX[IX_T1_Name]ON[dbo].[T1]
(
[Name]ASC
)
WITH (PAD_INDEX =OFF, STATISTICS_NORECOMPUTE =OFF, SORT_IN_TEMPDB =OFF, IGNORE_DUP_KEY =OFF, DROP_EXISTING =OFF, ONLINE =OFF, ALLOW_ROW_LOCKS =ON, ALLOW_PAGE_LOCKS =ON) ON[PRIMARY]
GO/****** Object: Index [IX_T2_Name] Script Date: 08/23/2011 18:14:10 ******/
CREATENONCLUSTEREDINDEX[IX_T2_Name]ON[dbo].[T2]
(
[Name]ASC
)
WITH (PAD_INDEX =OFF, STATISTICS_NORECOMPUTE =OFF, SORT_IN_TEMPDB =OFF, IGNORE_DUP_KEY =OFF, DROP_EXISTING =OFF, ONLINE =OFF, ALLOW_ROW_LOCKS =ON, ALLOW_PAGE_LOCKS =ON) ON[PRIMARY]
GO/****** Object: Index [IX_T3_Name] Script Date: 08/23/2011 18:14:20 ******/
CREATENONCLUSTEREDINDEX[IX_T3_Name]ON[dbo].[T3]
(
[Name]ASC
)
WITH (PAD_INDEX =OFF, STATISTICS_NORECOMPUTE =OFF, SORT_IN_TEMPDB =OFF, IGNORE_DUP_KEY =OFF, DROP_EXISTING =OFF, ONLINE =OFF, ALLOW_ROW_LOCKS =ON, ALLOW_PAGE_LOCKS =ON) ON[PRIMARY]
GO

三, 测试inner join三个表的各种排列的执行效率.

-- 20w inner join 2000
select*from T1
innerjoin T2
on T1.Name=T2.Name
-- 2000 inner join 20w
select*from T2
innerjoin T1
on T2.Name=T1.Name
-- 20w inner join 30w
select*from T1
innerjoin t3
on T1.Name=t3.Name
-- 30w inner join 20w
select*from t3
innerjoin T1
on t3.name=T1.Name
-- 2000 inner join 30w
select*from T2
innerjoin t3
on T2.Name=t3.Name
-- 30w inner join 2000
select*from t3
innerjoin T2
on t3.Name=T2.Name

测试结果:

对比下面的结果,我们会发现,如果在连接的关系字段建立索引,不加任何条件的情况下,查询的开销没有任何差别。

SQL Server 分析和编译时间: 
CPU 时间 =
31 毫秒,占用时间 = 36 毫秒。

(

2502 行受影响)
'Worktable'。扫描计数 0,逻辑读取 0 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
'T1'。扫描计数 1,逻辑读取 1452 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
'T2'。扫描计数 1,逻辑读取 15 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

SQL Server

执行时间:
CPU 时间 =
141 毫秒,占用时间 = 135 毫秒。

(

2502 行受影响)
'Worktable'。扫描计数 0,逻辑读取 0 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
'T1'。扫描计数 1,逻辑读取 1452 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
'T2'。扫描计数 1,逻辑读取 15 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

SQL Server

执行时间:
CPU 时间 =
125 毫秒,占用时间 = 129 毫秒。

(

220128 行受影响)
'Worktable'。扫描计数 0,逻辑读取 0 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
'T3'。扫描计数 1,逻辑读取 2168 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
'T1'。扫描计数 1,逻辑读取 1452 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

SQL Server

执行时间:
CPU 时间 =
937 毫秒,占用时间 = 2793 毫秒。

(

220128 行受影响)
'Worktable'。扫描计数 0,逻辑读取 0 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
'T3'。扫描计数 1,逻辑读取 2168 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
'T1'。扫描计数 1,逻辑读取 1452 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

SQL Server

执行时间:
CPU 时间 =
844 毫秒,占用时间 = 2788 毫秒。

(

3485 行受影响)
'T3'。扫描计数 2000,逻辑读取 17105 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
'T2'。扫描计数 1,逻辑读取 15 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

SQL Server

执行时间:
CPU 时间 =
47 毫秒,占用时间 = 69 毫秒。

(

3485 行受影响)
'T3'。扫描计数 2000,逻辑读取 17105 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
'T2'。扫描计数 1,逻辑读取 15 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

SQL Server

执行时间:
CPU 时间 =
47 毫秒,占用时间 = 51 毫秒。
SQL Server
分析和编译时间:
CPU 时间 =
0 毫秒,占用时间 = 0 毫秒。

SQL Server

执行时间:
CPU 时间 =
0 毫秒,占用时间 = 0 毫秒。

四, 加入where 条件进行inner join查询, 并查看不同排列的执行效率。

select*from T1 
innerjoin T2
on T1.Name=T2.Name
where T1.Name='23'select*from T2
innerjoin T1
on T1.Name=T2.Name
where T1.Name='23'

测试结果:

对比下面的结果,我们会发现,如果在连接的关系字段建立索引,在where条件1:1匹配的情况下,查询的开销没有任何差别。

SQL Server 分析和编译时间: 
CPU 时间
=0 毫秒,占用时间 =6 毫秒。

(

1 行受影响)
'T1'。扫描计数 1,逻辑读取 6 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
'T2'。扫描计数 1,逻辑读取 4 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

SQL Server 执行时间:
CPU 时间

=0 毫秒,占用时间 =0 毫秒。

(

1 行受影响)
'T2'。扫描计数 1,逻辑读取 4 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
'T1'。扫描计数 1,逻辑读取 6 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

SQL Server 执行时间:
CPU 时间

=0 毫秒,占用时间 =0 毫秒。
SQL Server 分析和编译时间:
CPU 时间
=0 毫秒,占用时间 =0 毫秒。

SQL Server 执行时间:
CPU 时间

=0 毫秒,占用时间 =0 毫秒。

五, 制造不同数量的索引筛选数据进行测试,并查看测试结果。

对三张表分别生成不同数量的'rrrr'数据,并作为查询条件。

-- t2 1条 rrrr
update T2 set Name='rrrr'
where uid in (
selecttop1 uid from t2)
-- t1 10条 rrrr
update T1 set Name='rrrr'
where uid in (
selecttop10 uid from t1)
-- t3 2000条 rrrr
update t3 set name='rrrr'
where uid in
(
selecttop2000 uid from t3
)
selectCOUNT(1) from T1
selectCOUNT(1) from T2
selectCOUNT(1) from T3 selectCOUNT(1) from T1 where Name='rrrr'
selectCOUNT(1) from T2 where Name='rrrr'
selectCOUNT(1) from T3 where Name='rrrr'

将nam='rrrr'作为连接查询的条件,再次进行不同排列的测试。

select*from T1 
innerjoin T2
on T1.Name=T2.Name
where T1.Name='rrrr'select*from T2
innerjoin T1
on T1.Name=T2.Name
where T1.Name='rrrr'select*from T3
innerjoin T2
on T3.Name=T2.Name
where T2.Name='rrrr'select*from T2
innerjoin T3
on T3.Name=T2.Name
where T3.Name='rrrr'select*from T1
innerjoin t3
on T3.Name=t1.name
where T3.Name='rrrr'select*from T3
innerjoin t1
on T1.Name=t3.name
where T3.Name='rrrr'

我们再来看看测试结果,差异产生了!

SQL Server 分析和编译时间: 
CPU 时间
=16 毫秒,占用时间 =23 毫秒。

(

10 行受影响)
'T1'。扫描计数 1,逻辑读取 33 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
'T2'。扫描计数 1,逻辑读取 4 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

SQL Server 执行时间:
CPU 时间

=0 毫秒,占用时间 =0 毫秒。

(

10 行受影响)
'T2'。扫描计数 10,逻辑读取 40 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
'T1'。扫描计数 1,逻辑读取 33 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

SQL Server 执行时间:
CPU 时间

=0 毫秒,占用时间 =0 毫秒。

(

2000 行受影响)
'T3'。扫描计数 1,逻辑读取 6012 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
'T2'。扫描计数 1,逻辑读取 4 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

SQL Server 执行时间:
CPU 时间

=16 毫秒,占用时间 =12 毫秒。

(

2000 行受影响)
'T2'。扫描计数 2000,逻辑读取 8000 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
'T3'。扫描计数 1,逻辑读取 6012 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

SQL Server 执行时间:
CPU 时间

=31 毫秒,占用时间 =36 毫秒。

(

20000 行受影响)
'T1'。扫描计数 2000,逻辑读取 66000 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
'T3'。扫描计数 1,逻辑读取 6012 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

SQL Server 执行时间:
CPU 时间

=94 毫秒,占用时间 =445 毫秒。

(

20000 行受影响)
'T3'。扫描计数 10,逻辑读取 60120 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
'T1'。扫描计数 1,逻辑读取 33 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

SQL Server 执行时间:
CPU 时间

=46 毫秒,占用时间 =264 毫秒。
SQL Server 分析和编译时间:
CPU 时间
=0 毫秒,占用时间 =0 毫秒。

SQL Server 执行时间:
CPU 时间

=0 毫秒,占用时间 =0 毫秒。

根据执行结果推断,sql server inner查询的效率差异的起因是索引的利用率,我们可以从测试结果,比较直观地得出结果,索引利用率越高的表,放在inner join前面,连接查询的开销越小。

再进行其他一些诸如like、> 、<作为条件的测试,发现如何排列inner join前后的顺序,查询的开销都是相同。

以上仅为测试结果和记录,关于sql server查询机制的细节还要继续恶补。