关于"按某列分组,求最新一条记录"两种sql写法的测试

nzperfect 2008-07-29 09:56:30

-----------------------------------------------------------------------
--组织测试数据,首先我们来组织category分类比较少的情况,分了体现这个差别,所以只分了两个分类
drop table tb
create table tb(id int identity primary key,category nvarchar(50),remark char(5000))


declare @i int set @i=1
while @i<=5000
begin
insert into tb select 'a','remark'+rtrim(@i)
set @i=@i+1
end

declare @i int set @i=1
while @i<=5000
begin
insert into tb select 'b','remark'+rtrim(@i)
set @i=@i+1
end



checkpoint
dbcc dropcleanbuffers
dbcc freeproccache
dbcc freesystemcache ('all')
set statistics io on
set showplan_all on
set showplan_all off

--在不给category加索引的情况下:
select * from tb t where not exists (select 1 from tb where category = t.category and id>t.id)
/*
(2 行受影响)
表 'tb'。扫描计数 6,逻辑读取 22654 次,物理读取 5 次,预读 3900 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'Worktable'。扫描计数 0,逻辑读取 0 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。 */
select * from tb t
inner join
(
select max(id) as id from tb group by category
) as t2 on t.id=t2.id
/*
(2 行受影响)
表 'tb'。扫描计数 1,逻辑读取 10044 次,物理读取 3 次,预读 10016 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'Worktable'。扫描计数 0,逻辑读取 0 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
*/


--在给category加索引的情况下:
create index ix_01 on tb (category) update statistics tb with fullscan
select * from tb t where not exists (select 1 from tb where category = t.category and id>t.id)
/*
(2 行受影响)
表 'tb'。扫描计数 10001,逻辑读取 31377 次,物理读取 7 次,预读 10031 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。 */
select t.* from tb t
inner join
(
select max(id) as id from tb group by category
) as t2 on t.id=t2.id
/*
(2 行受影响)
表 'tb'。扫描计数 1,逻辑读取 28 次,物理读取 6 次,预读 20 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
*/


-----------------------------------------------------------------------
--组织测试数据,首先我们来组织category分类比较多的情况,2001类
drop table tb
create table tb(id int identity primary key,category nvarchar(50),remark char(5000))


declare @i int set @i=1
declare @ii int set @ii=1
while @i<=10000
begin
if @i%5=0 set @ii=@ii+1
insert into tb select 'a'+rtrim(@ii),'remark'+rtrim(@i)
set @i=@i+1
end

checkpoint
dbcc dropcleanbuffers
dbcc freeproccache
dbcc freesystemcache ('all')
set statistics io on
set showplan_all on
set showplan_all off

--在不给category加索引的情况下:
select * from tb t where not exists (select 1 from tb where category = t.category and id>t.id)
/*
(2001 行受影响)
表 'Worktable'。扫描计数 0,逻辑读取 0 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'tb'。扫描计数 2,逻辑读取 20076 次,物理读取 1 次,预读 4147 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
*/
select * from tb t
inner join
(
select max(id) as id from tb group by category
) as t2 on t.id=t2.id
/*
(2001 行受影响)
表 'tb'。扫描计数 1,逻辑读取 16175 次,物理读取 3 次,预读 10025 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'Worktable'。扫描计数 0,逻辑读取 0 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
*/


--在给category加索引的情况下:
create index ix_01 on tb (category) update statistics tb with fullscan
select * from tb t where not exists (select 1 from tb where category = t.category and id>t.id)
/*
(2001 行受影响)
表 'Worktable'。扫描计数 0,逻辑读取 0 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'tb'。扫描计数 2,逻辑读取 10069 次,物理读取 4 次,预读 10045 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
*/
select t.* from tb t
inner join
(
select max(id) as id from tb group by category
) as t2 on t.id=t2.id
/*
(2001 行受影响)
表 'tb'。扫描计数 1,逻辑读取 10694 次,物理读取 2 次,预读 10053 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
*/


-----
总结:
在用来分组的字段可分组比较少的情况,用gorup by 分组后再inner join原表的效率会明显高于not exists写法,
因为group by 后再inner join 原表是采用 hash join ,这要在有分组列有索引的情况下,已经排序,数据又少,速度极快。
但not exits是nested loop join,在这种有大量外部数据输入再比较的情况下,效率是很慢的。

反之,两者效率相当,都会很慢。
...全文
230 13 打赏 收藏 转发到动态 举报
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
M1CR0S0FT 2008-07-29
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 hery2002 的回复:]
呵呵,
今天早上在一个帖子里面才回复了,
没有时间测试,
没想到P.A居然就测试出来了。
谢谢完美MM.:)
[/Quote]
楼主是女人?
hery2002 2008-07-29
  • 打赏
  • 举报
回复
呵呵,
今天早上在一个帖子里面才回复了,
没有时间测试,
没想到P.A居然就测试出来了。
谢谢完美MM.:)
nzperfect 2008-07-29
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 M1CR0S0FT 的回复:]
引用 8 楼 perfectaction 的回复:
引用 7 楼 M1CR0S0FT 的回复:
引用 2 楼 perfectaction 的回复:
晕,更正下,上面打错了,是 merge join .


SQL code-----
总结: 在用来分组的字段可分组比较少的情况,用gorup by 分组后再inner join原表的效率会明显高于not exists写法, 因为group by 后再inner join 原表是采用 merge join ,这要在有分组列有索引的情况下,已经排序,数据又少,速度极快。 但not exits是nested lo…
[/Quote]

噢。果然
M1CR0S0FT 2008-07-29
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 perfectaction 的回复:]
引用 7 楼 M1CR0S0FT 的回复:
引用 2 楼 perfectaction 的回复:
晕,更正下,上面打错了,是 merge join .


SQL code-----
总结: 在用来分组的字段可分组比较少的情况,用gorup by 分组后再inner join原表的效率会明显高于not exists写法, 因为group by 后再inner join 原表是采用 merge join ,这要在有分组列有索引的情况下,已经排序,数据又少,速度极快。 但not exits是nested loop join,在这种有大量外部数据输…
[/Quote]
gorup by---group by
nzperfect 2008-07-29
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 M1CR0S0FT 的回复:]
引用 2 楼 perfectaction 的回复:
晕,更正下,上面打错了,是 merge join .


SQL code-----
总结: 在用来分组的字段可分组比较少的情况,用gorup by 分组后再inner join原表的效率会明显高于not exists写法, 因为group by 后再inner join 原表是采用 merge join ,这要在有分组列有索引的情况下,已经排序,数据又少,速度极快。 但not exits是nested loop join,在这种有大量外部数据输入再比较的情况下,效率是很慢的。…
[/Quote]

什么问题?
M1CR0S0FT 2008-07-29
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 perfectaction 的回复:]
晕,更正下,上面打错了,是 merge join .


SQL code-----
总结: 在用来分组的字段可分组比较少的情况,用gorup by 分组后再inner join原表的效率会明显高于not exists写法, 因为group by 后再inner join 原表是采用 merge join ,这要在有分组列有索引的情况下,已经排序,数据又少,速度极快。 但not exits是nested loop join,在这种有大量外部数据输入再比较的情况下,效率是很慢的。 反之,两者效率相当,都会很慢。
[/Quote]
还是有问题.
nzperfect 2008-07-29
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 Garnett_KG 的回复:]
20太小气了.
[/Quote]
哈哈.. 赚点分不容易啊 ~
Garnett_KG 2008-07-29
  • 打赏
  • 举报
回复
20太小气了.
-狙击手- 2008-07-29
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 yrwx001 的回复:]

[/Quote]
yrwx001 2008-07-29
  • 打赏
  • 举报
回复
nzperfect 2008-07-29
  • 打赏
  • 举报
回复
晕,更正下,上面打错了,是 merge join .

-----  
总结: 在用来分组的字段可分组比较少的情况,用gorup by 分组后再inner join原表的效率会明显高于not exists写法, 因为group by 后再inner join 原表是采用 merge join ,这要在有分组列有索引的情况下,已经排序,数据又少,速度极快。 但not exits是nested loop join,在这种有大量外部数据输入再比较的情况下,效率是很慢的。 反之,两者效率相当,都会很慢。
水族杰纶 2008-07-29
  • 打赏
  • 举报
回复
收藏学习之~~~
一品梅 2008-07-29
  • 打赏
  • 举报
回复
学习PA
通过慢sql分析的学习,了解什么是慢sql,以及慢SQL会引起那些性能问题。清楚慢sql日志的设置,然后再通过慢sql分析工具的学习,清楚慢sql分析的步骤和流程。慢sql分析工具:mysqldumpslow工具、explain工具、profile工具、Optimizer Trace工具。 提供课程中所使用的sql语句。 课程内容:第一章:课程简介1、课程介绍2、课程大纲 第二章:慢sql简介1、慢sql简介2、慢sql会引起的问题 第三章:慢日志的设置1、慢sql的分析流程2、慢日志参数理解3、慢日志参数设置:第1种方式:my.ini文件设置4、慢日志参数设置:第2种方式:sql脚本设置5、慢日志参数设置-效果验证 第四章:如何发现慢sql1、如何发现慢sql:第1种方式:慢日志文件2、如何发现慢sql:第2种方式:mysql库的slow_log表 第五章:慢sql分析工具1、慢sql提取-mysqldumpslow工具-使用方法2、慢sql提取-mysqldumpslow工具-操作实战3、慢sql的执行计划分析-explain分析-执行计划结果说明4、慢sql的执行计划分析-explain分析-索引介绍+type类型举例5、慢sql的资源开销分析-profile分析-分析步骤6、慢sql的资源开销分析-profile分析-show profile执行阶段说明7、慢sql的资源开销分析-profile分析-完整列表说明+操作实战8、慢sql的跟踪分析-Optimizer Trace分析-分析步骤9、慢sql的跟踪分析-Optimizer Trace表的介绍10、索引失效场景举例 第六章:慢日志清理1、慢日志清理

22,210

社区成员

发帖
与我相关
我的任务
社区描述
MS-SQL Server 疑难问题
社区管理员
  • 疑难问题社区
  • 尘觉
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧