20 个解决方案
#1
你数据量为什么要这么大呢?你的用户可以一口气看完几十万行记录吗?你没有必要要这么做,要不你用分页技术每页1000行,用户能看完了不起了。
declare @SQLStr varchar(8000)
set @SQLStr='SELECT Top '+cast(@每页大小 as varchar)+' * FROM 表 WHERE 主键列 NOT IN (SELECT TOP '+cast(@每页大小*@第几页 as varchar)+' 主键列 from 表 )'
exec(@SQLStr)
declare @SQLStr varchar(8000)
set @SQLStr='SELECT Top '+cast(@每页大小 as varchar)+' * FROM 表 WHERE 主键列 NOT IN (SELECT TOP '+cast(@每页大小*@第几页 as varchar)+' 主键列 from 表 )'
exec(@SQLStr)
#2
select IDENTITY(int, 1,1) AS ID_Num,* into #temp from 表
select * from #temp where ID_Num between 10 and 20
select * from #temp where ID_Num between 10 and 20
#3
这个问题不应该从设置超时上去解决.
应该对你的数据库进行优化.创建必要的主键,索引
查询数据时避免一次性取大量的数据到客户端等.
应该对你的数据库进行优化.创建必要的主键,索引
查询数据时避免一次性取大量的数据到客户端等.
#4
推荐你看一篇优化SQL的摘文,希望对你有所帮助
如何让你的SQL运行得更快
---- 人们在使用SQL时往往会陷入一个误区,即太关注于所得的结果是否正确,而忽略
了不同的实现方法之间可能存在的性能差异,这种性能差异在大型的或是复杂的数据库
环境中(如联机事务处理OLTP或决策支持系统DSS)中表现得尤为明显。笔者在工作实践
中发现,不良的SQL往往来自于不恰当的索引设计、不充份的连接条件和不可优化的whe
re子句。在对它们进行适当的优化后,其运行速度有了明显地提高!下面我将从这三个
方面分别进行总结:
---- 为了更直观地说明问题,所有实例中的SQL运行时间均经过测试,不超过1秒的均
表示为(< 1秒)。
---- 测试环境--
---- 主机:HP LH II
---- 主频:330MHZ
---- 内存:128兆
---- 操作系统:Operserver5.0.4
----数据库:Sybase11.0.3
一、不合理的索引设计
----例:表record有620000行,试看在不同的索引下,下面几个 SQL的运行情况:
---- 1.在date上建有一非个群集索引
select count(*) from record where date >
'19991201' and date < '19991214'and amount >
2000 (25秒)
select date,sum(amount) from record group by date
(55秒)
select count(*) from record where date >
'19990901' and place in ('BJ','SH') (27秒)
---- 分析:
----date上有大量的重复值,在非群集索引下,数据在物理上随机存放在数据页上,在
范围查找时,必须执行一次表扫描才能找到这一范围内的全部行。
---- 2.在date上的一个群集索引
select count(*) from record where date >
'19991201' and date < '19991214' and amount >
2000 (14秒)
select date,sum(amount) from record group by date
(28秒)
select count(*) from record where date >
'19990901' and place in ('BJ','SH')(14秒)
---- 分析:
---- 在群集索引下,数据在物理上按顺序在数据页上,重复值也排列在一起,因而在范
围查找时,可以先找到这个范围的起末点,且只在这个范围内扫描数据页,避免了大范
围扫描,提高了查询速度。
---- 3.在place,date,amount上的组合索引
select count(*) from record where date >
'19991201' and date < '19991214' and amount >
2000 (26秒)
select date,sum(amount) from record group by date
(27秒)
select count(*) from record where date >
'19990901' and place in ('BJ', 'SH')(< 1秒)
---- 分析:
---- 这是一个不很合理的组合索引,因为它的前导列是place,第一和第二条SQL没有引
用place,因此也没有利用上索引;第三个SQL使用了place,且引用的所有列都包含在组
合索引中,形成了索引覆盖,所以它的速度是非常快的。
---- 4.在date,place,amount上的组合索引
select count(*) from record where date >
'19991201' and date < '19991214' and amount >
2000(< 1秒)
select date,sum(amount) from record group by date
(11秒)
select count(*) from record where date >
'19990901' and place in ('BJ','SH')(< 1秒)
---- 分析:
---- 这是一个合理的组合索引。它将date作为前导列,使每个SQL都可以利用索引,并
且在第一和第三个SQL中形成了索引覆盖,因而性能达到了最优。
---- 5.总结:
---- 缺省情况下建立的索引是非群集索引,但有时它并不是最佳的;合理的索引设计要
建立在对各种查询的分析和预测上。一般来说:
---- ①.有大量重复值、且经常有范围查询
(between, >,< ,>=,< =)和order by
、group by发生的列,可考虑建立群集索引;
---- ②.经常同时存取多列,且每列都含有重复值可考虑建立组合索引;
---- ③.组合索引要尽量使关键查询形成索引覆盖,其前导列一定是使用最频繁的列。
二、不充份的连接条件:
---- 例:表card有7896行,在card_no上有一个非聚集索引,表account有191122行,在
account_no上有一个非聚集索引,试看在不同的表连接条件下,两个SQL的执行情况:
select sum(a.amount) from account a,
card b where a.card_no = b.card_no(20秒)
---- 将SQL改为:
select sum(a.amount) from account a,
card b where a.card_no = b.card_no and a.
account_no=b.account_no(< 1秒)
---- 分析:
---- 在第一个连接条件下,最佳查询方案是将account作外层表,card作内层表,利用
card上的索引,其I/O次数可由以下公式估算为:
---- 外层表account上的22541页+(外层表account的191122行*内层表card上对应外层
表第一行所要查找的3页)=595907次I/O
---- 在第二个连接条件下,最佳查询方案是将card作外层表,account作内层表,利用
account上的索引,其I/O次数可由以下公式估算为:
---- 外层表card上的1944页+(外层表card的7896行*内层表account上对应外层表每一
行所要查找的4页)= 33528次I/O
---- 可见,只有充份的连接条件,真正的最佳方案才会被执行。
---- 总结:
---- 1.多表操作在被实际执行前,查询优化器会根据连接条件,列出几组可能的连接方
案并从中找出系统开销最小的最佳方案。连接条件要充份考虑带有索引的表、行数多的
表;内外表的选择可由公式:外层表中的匹配行数*内层表中每一次查找的次数确定,乘
积最小为最佳方案。
---- 2.查看执行方案的方法-- 用set showplanon,打开showplan选项,就可以看到连
接顺序、使用何种索引的信息;想看更详细的信息,需用sa角色执行dbcc(3604,310,30
2)。
三、不可优化的where子句
---- 1.例:下列SQL条件语句中的列都建有恰当的索引,但执行速度却非常慢:
select * from record where
substring(card_no,1,4)='5378'(13秒)
select * from record where
amount/30< 1000(11秒)
select * from record where
convert(char(10),date,112)='19991201'(10秒)
---- 分析:
---- where子句中对列的任何操作结果都是在SQL运行时逐列计算得到的,因此它不得不
进行表搜索,而没有使用该列上面的索引;如果这些结果在查询编译时就能得到,那么
就可以被SQL优化器优化,使用索引,避免表搜索,因此将SQL重写成下面这样:
select * from record where card_no like
'5378%'(< 1秒)
select * from record where amount
< 1000*30(< 1秒)
select * from record where date= '1999/12/01'
(< 1秒)
---- 你会发现SQL明显快起来!
---- 2.例:表stuff有200000行,id_no上有非群集索引,请看下面这个SQL:
select count(*) from stuff where id_no in('0','1')
(23秒)
---- 分析:
---- where条件中的'in'在逻辑上相当于'or',所以语法分析器会将in ('0','1')转化
为id_no ='0' or id_no='1'来执行。我们期望它会根据每个or子句分别查找,再将结果
相加,这样可以利用id_no上的索引;但实际上(根据showplan),它却采用了"OR策略"
,即先取出满足每个or子句的行,存入临时数据库的工作表中,再建立唯一索引以去掉
重复行,最后从这个临时表中计算结果。因此,实际过程没有利用id_no上索引,并且完
成时间还要受tempdb数据库性能的影响。
---- 实践证明,表的行数越多,工作表的性能就越差,当stuff有620000行时,执行时
间竟达到220秒!还不如将or子句分开:
select count(*) from stuff where id_no='0'
select count(*) from stuff where id_no='1'
---- 得到两个结果,再作一次加法合算。因为每句都使用了索引,执行时间只有3秒,
在620000行下,时间也只有4秒。或者,用更好的方法,写一个简单的存储过程:
create proc count_stuff as
declare @a int
declare @b int
declare @c int
declare @d char(10)
begin
select @a=count(*) from stuff where id_no='0'
select @b=count(*) from stuff where id_no='1'
end
select @c=@a+@b
select @d=convert(char(10),@c)
print @d
---- 直接算出结果,执行时间同上面一样快!
---- 总结:
---- 可见,所谓优化即where子句利用了索引,不可优化即发生了表扫描或额外开销。
---- 1.任何对列的操作都将导致表扫描,它包括数据库函数、计算表达式等等,查询时
要尽可能将操作移至等号右边。
---- 2.in、or子句常会使用工作表,使索引失效;如果不产生大量重复值,可以考虑把
子句拆开;拆开的子句中应该包含索引。
---- 3.要善于使用存储过程,它使SQL变得更加灵活和高效。
---- 从以上这些例子可以看出,SQL优化的实质就是在结果正确的前提下,用优化器可
以识别的语句,充份利用索引,减少表扫描的I/O次数,尽量避免表搜索的发生。其实S
QL的性能优化是一个复杂的过程,上述这些只是在应用层次的一种体现,深入研究还会
涉及数据库层的资源配置、网络层的流量控制以及操作系统层的总体设计。
如何让你的SQL运行得更快
---- 人们在使用SQL时往往会陷入一个误区,即太关注于所得的结果是否正确,而忽略
了不同的实现方法之间可能存在的性能差异,这种性能差异在大型的或是复杂的数据库
环境中(如联机事务处理OLTP或决策支持系统DSS)中表现得尤为明显。笔者在工作实践
中发现,不良的SQL往往来自于不恰当的索引设计、不充份的连接条件和不可优化的whe
re子句。在对它们进行适当的优化后,其运行速度有了明显地提高!下面我将从这三个
方面分别进行总结:
---- 为了更直观地说明问题,所有实例中的SQL运行时间均经过测试,不超过1秒的均
表示为(< 1秒)。
---- 测试环境--
---- 主机:HP LH II
---- 主频:330MHZ
---- 内存:128兆
---- 操作系统:Operserver5.0.4
----数据库:Sybase11.0.3
一、不合理的索引设计
----例:表record有620000行,试看在不同的索引下,下面几个 SQL的运行情况:
---- 1.在date上建有一非个群集索引
select count(*) from record where date >
'19991201' and date < '19991214'and amount >
2000 (25秒)
select date,sum(amount) from record group by date
(55秒)
select count(*) from record where date >
'19990901' and place in ('BJ','SH') (27秒)
---- 分析:
----date上有大量的重复值,在非群集索引下,数据在物理上随机存放在数据页上,在
范围查找时,必须执行一次表扫描才能找到这一范围内的全部行。
---- 2.在date上的一个群集索引
select count(*) from record where date >
'19991201' and date < '19991214' and amount >
2000 (14秒)
select date,sum(amount) from record group by date
(28秒)
select count(*) from record where date >
'19990901' and place in ('BJ','SH')(14秒)
---- 分析:
---- 在群集索引下,数据在物理上按顺序在数据页上,重复值也排列在一起,因而在范
围查找时,可以先找到这个范围的起末点,且只在这个范围内扫描数据页,避免了大范
围扫描,提高了查询速度。
---- 3.在place,date,amount上的组合索引
select count(*) from record where date >
'19991201' and date < '19991214' and amount >
2000 (26秒)
select date,sum(amount) from record group by date
(27秒)
select count(*) from record where date >
'19990901' and place in ('BJ', 'SH')(< 1秒)
---- 分析:
---- 这是一个不很合理的组合索引,因为它的前导列是place,第一和第二条SQL没有引
用place,因此也没有利用上索引;第三个SQL使用了place,且引用的所有列都包含在组
合索引中,形成了索引覆盖,所以它的速度是非常快的。
---- 4.在date,place,amount上的组合索引
select count(*) from record where date >
'19991201' and date < '19991214' and amount >
2000(< 1秒)
select date,sum(amount) from record group by date
(11秒)
select count(*) from record where date >
'19990901' and place in ('BJ','SH')(< 1秒)
---- 分析:
---- 这是一个合理的组合索引。它将date作为前导列,使每个SQL都可以利用索引,并
且在第一和第三个SQL中形成了索引覆盖,因而性能达到了最优。
---- 5.总结:
---- 缺省情况下建立的索引是非群集索引,但有时它并不是最佳的;合理的索引设计要
建立在对各种查询的分析和预测上。一般来说:
---- ①.有大量重复值、且经常有范围查询
(between, >,< ,>=,< =)和order by
、group by发生的列,可考虑建立群集索引;
---- ②.经常同时存取多列,且每列都含有重复值可考虑建立组合索引;
---- ③.组合索引要尽量使关键查询形成索引覆盖,其前导列一定是使用最频繁的列。
二、不充份的连接条件:
---- 例:表card有7896行,在card_no上有一个非聚集索引,表account有191122行,在
account_no上有一个非聚集索引,试看在不同的表连接条件下,两个SQL的执行情况:
select sum(a.amount) from account a,
card b where a.card_no = b.card_no(20秒)
---- 将SQL改为:
select sum(a.amount) from account a,
card b where a.card_no = b.card_no and a.
account_no=b.account_no(< 1秒)
---- 分析:
---- 在第一个连接条件下,最佳查询方案是将account作外层表,card作内层表,利用
card上的索引,其I/O次数可由以下公式估算为:
---- 外层表account上的22541页+(外层表account的191122行*内层表card上对应外层
表第一行所要查找的3页)=595907次I/O
---- 在第二个连接条件下,最佳查询方案是将card作外层表,account作内层表,利用
account上的索引,其I/O次数可由以下公式估算为:
---- 外层表card上的1944页+(外层表card的7896行*内层表account上对应外层表每一
行所要查找的4页)= 33528次I/O
---- 可见,只有充份的连接条件,真正的最佳方案才会被执行。
---- 总结:
---- 1.多表操作在被实际执行前,查询优化器会根据连接条件,列出几组可能的连接方
案并从中找出系统开销最小的最佳方案。连接条件要充份考虑带有索引的表、行数多的
表;内外表的选择可由公式:外层表中的匹配行数*内层表中每一次查找的次数确定,乘
积最小为最佳方案。
---- 2.查看执行方案的方法-- 用set showplanon,打开showplan选项,就可以看到连
接顺序、使用何种索引的信息;想看更详细的信息,需用sa角色执行dbcc(3604,310,30
2)。
三、不可优化的where子句
---- 1.例:下列SQL条件语句中的列都建有恰当的索引,但执行速度却非常慢:
select * from record where
substring(card_no,1,4)='5378'(13秒)
select * from record where
amount/30< 1000(11秒)
select * from record where
convert(char(10),date,112)='19991201'(10秒)
---- 分析:
---- where子句中对列的任何操作结果都是在SQL运行时逐列计算得到的,因此它不得不
进行表搜索,而没有使用该列上面的索引;如果这些结果在查询编译时就能得到,那么
就可以被SQL优化器优化,使用索引,避免表搜索,因此将SQL重写成下面这样:
select * from record where card_no like
'5378%'(< 1秒)
select * from record where amount
< 1000*30(< 1秒)
select * from record where date= '1999/12/01'
(< 1秒)
---- 你会发现SQL明显快起来!
---- 2.例:表stuff有200000行,id_no上有非群集索引,请看下面这个SQL:
select count(*) from stuff where id_no in('0','1')
(23秒)
---- 分析:
---- where条件中的'in'在逻辑上相当于'or',所以语法分析器会将in ('0','1')转化
为id_no ='0' or id_no='1'来执行。我们期望它会根据每个or子句分别查找,再将结果
相加,这样可以利用id_no上的索引;但实际上(根据showplan),它却采用了"OR策略"
,即先取出满足每个or子句的行,存入临时数据库的工作表中,再建立唯一索引以去掉
重复行,最后从这个临时表中计算结果。因此,实际过程没有利用id_no上索引,并且完
成时间还要受tempdb数据库性能的影响。
---- 实践证明,表的行数越多,工作表的性能就越差,当stuff有620000行时,执行时
间竟达到220秒!还不如将or子句分开:
select count(*) from stuff where id_no='0'
select count(*) from stuff where id_no='1'
---- 得到两个结果,再作一次加法合算。因为每句都使用了索引,执行时间只有3秒,
在620000行下,时间也只有4秒。或者,用更好的方法,写一个简单的存储过程:
create proc count_stuff as
declare @a int
declare @b int
declare @c int
declare @d char(10)
begin
select @a=count(*) from stuff where id_no='0'
select @b=count(*) from stuff where id_no='1'
end
select @c=@a+@b
select @d=convert(char(10),@c)
print @d
---- 直接算出结果,执行时间同上面一样快!
---- 总结:
---- 可见,所谓优化即where子句利用了索引,不可优化即发生了表扫描或额外开销。
---- 1.任何对列的操作都将导致表扫描,它包括数据库函数、计算表达式等等,查询时
要尽可能将操作移至等号右边。
---- 2.in、or子句常会使用工作表,使索引失效;如果不产生大量重复值,可以考虑把
子句拆开;拆开的子句中应该包含索引。
---- 3.要善于使用存储过程,它使SQL变得更加灵活和高效。
---- 从以上这些例子可以看出,SQL优化的实质就是在结果正确的前提下,用优化器可
以识别的语句,充份利用索引,减少表扫描的I/O次数,尽量避免表搜索的发生。其实S
QL的性能优化是一个复杂的过程,上述这些只是在应用层次的一种体现,深入研究还会
涉及数据库层的资源配置、网络层的流量控制以及操作系统层的总体设计。
#5
好长,都看得眼花了,决定不看了
#6
超过半分钟的语句就算不超时用户也受不了了,看能否优化一下你的语句吧。
#7
谢谢各位朋友!
我测试了,主要是在执行存储过程时,出现超时!
我测试了,主要是在执行存储过程时,出现超时!
#8
SP???
那是SP的运算有问题吧,或者用游标没释放什么的
那是SP的运算有问题吧,或者用游标没释放什么的
#9
在报表列印之前,需要执行一个存储过程,里面也没有用到游标,只是建了一个临时表,然后分别从试图和表向其中插入数据而已,语句都很简单,好像也没有什么可优化的!
#10
必须列印那么多
因为存在需要显示期初和期末的问题,比如:你列印1-5月和5-9月 和1-9月时,那么列印5-9月就会存在一个期初的问题,所以必须一次性把1-9月的数据全部列印出来
因为存在需要显示期初和期末的问题,比如:你列印1-5月和5-9月 和1-9月时,那么列印5-9月就会存在一个期初的问题,所以必须一次性把1-9月的数据全部列印出来
#11
把你的SP贴出来看看!
#12
优化索引,优化语句.
引与系统性能
索引可以加快数据检索的速度但它会使数据的插入删除和更新变慢尤其是簇索
引数据是按照逻辑顺序存放在一定的物理位置当变更数据时根据新的数据顺序需
要将许多数据进行物理位置的移动这将增加系统的负担对非簇索引数据更新时也需
要更新索引页这也需要占用系统时间因此在一个表中使用太多的索引会影响数据库
的性能对于一个经常会改变的表应该尽量限制表只使用一个簇索引和不超过3~4 个
非簇索引对事务处理特别繁重的表其索引应尽量不超过3 个
索引调整向导Index Tuning Wizard
索引调整向导可以帮助选择并创建一个最优化的索引集合以提高数据库的性能
要使用索引调整向导需要一个工作负荷记录Workload 工作负荷记录由SQL 脚
本或SQL Server Profiler 创建的存储在文件或表中的跟踪组成如果没有现存的针对要进
行索引调整的数据库或表的工作负荷记录可以通过SQL Server Profiler 来创建一个其
具体方法请参见第19 章中SQL Server Profiler 的用法可以用Sample 1 – TSQL 跟踪定
义来创建或新建一个跟踪索引调整向导可以使用查询优化器根据工作负荷记录分析索引
的性能并提出相应的调整建议可以立即让系统根据建议修改索引也可以将任务列入
计划以后再创建
引与系统性能
索引可以加快数据检索的速度但它会使数据的插入删除和更新变慢尤其是簇索
引数据是按照逻辑顺序存放在一定的物理位置当变更数据时根据新的数据顺序需
要将许多数据进行物理位置的移动这将增加系统的负担对非簇索引数据更新时也需
要更新索引页这也需要占用系统时间因此在一个表中使用太多的索引会影响数据库
的性能对于一个经常会改变的表应该尽量限制表只使用一个簇索引和不超过3~4 个
非簇索引对事务处理特别繁重的表其索引应尽量不超过3 个
索引调整向导Index Tuning Wizard
索引调整向导可以帮助选择并创建一个最优化的索引集合以提高数据库的性能
要使用索引调整向导需要一个工作负荷记录Workload 工作负荷记录由SQL 脚
本或SQL Server Profiler 创建的存储在文件或表中的跟踪组成如果没有现存的针对要进
行索引调整的数据库或表的工作负荷记录可以通过SQL Server Profiler 来创建一个其
具体方法请参见第19 章中SQL Server Profiler 的用法可以用Sample 1 – TSQL 跟踪定
义来创建或新建一个跟踪索引调整向导可以使用查询优化器根据工作负荷记录分析索引
的性能并提出相应的调整建议可以立即让系统根据建议修改索引也可以将任务列入
计划以后再创建
#13
收藏
#14
CREATE PROCEDURE [REP_CPBCRKMX001]
@STHTPH varchar(20)='',
@EDHTPH varchar(20)='z',
@STDATE datetime,
@EDDATE datetime,
@QCDATE datetime,
@FIRST int=1
AS
CREATE TABLE #cpbtempdata (HTPH varchar(20) NOT NULL, ZDBH varchar(20) NOT NULL, JLR datetime NOT NULL, PZH varchar(26) NOT NULL, KHBH VARCHAR(20) NULL,KHJC VARCHAR(20) NULL,
XXX varchar(3) NOT NULL, HTPM varchar(40) NULL, HTDWMC varchar(10) NULL, PZR datetime NULL,
PZMC varchar(20) NOT NULL,LBPH varchar(20) NULL,LBPM varchar(40) NULL,LBDWMC varchar(10) NULL,
LBRKZ float NULL DEFAULT 0, HTRKZ float NULL DEFAULT 0, LBCKZ1 float NULL DEFAULT 0,
HTCKZ1 float NULL DEFAULT 0, LBCKZ2 float NULL DEFAULT 0, HTCKZ2 float NULL DEFAULT 0,
LBCKZ3 float NULL DEFAULT 0, HTCKZ3 float NULL DEFAULT 0, HTCKZ float NULL DEFAULT 0,JY FLOAT DEFAULT 0,paixu varchar(2)
PRIMARY KEY(HTPH,ZDBH,JLR,PZH,XXX)) --//总表
--清空库
TRUNCATE TABLE #cpbtempdata
--本期-------------------------------------------------------------------------------------------------------------
--J 成品入库--进---------------------------------
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,paixu)
SELECT 'J1',ZDBH, RKDH,'成品入库',HTPH,RKDR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,'a'
FROM CINORD_V
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and htrkz>=0
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,paixu)
SELECT 'J1',ZDBH, RKDH,'成品入库',HTPH,RKDR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,'b1'
FROM CINORD_V
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and htrkz<0
--J 补做接单入库--进---------------------------------
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,paixu)
SELECT 'J2',ZDBH, RKDH,'成品入库',HTPH,RKDR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,'a'
FROM CINORD_V
WHERE JLR<@QCDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
--X 销退入库--直接---------------------------------
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBCKZ1,HTCKZ1,KHBH,paixu)
SELECT 'X1',ZDBH,THDH,'销退入库',HTPH,THDR,JLR,LBPH,LBDWMC,-LBthZ,-HTthZ,KHBH,'b'
FROM RECINORD_V01
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and JZZL='直接出口'
--X 销退入库--间接---------------------------------
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBCKZ2,HTCKZ2,KHBH,paixu)
SELECT 'X2',ZDBH,THDH,'销退入库',HTPH,THDR,JLR,LBPH,LBDWMC,-LBthZ,-HTthZ,KHBH,'b'
FROM RECINORD_V01
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and JZZL='间接出口'
--X 销退入库--内销---------------------------------
INSERT INTO #cpbtempdata (XXX,ZDBH, PZH, PZMC, HTPH, PZR, JLR, LBPH, LBDWMC, LBCKZ3, HTCKZ3,KHBH,paixu)
SELECT 'X3',ZDBH, THDH, '销退入库', HTPH, THDR, JLR, LBPH,LBDWMC,-LBthZ,-HTthZ,KHBH,'b'
FROM RECINORD_V01
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and JZZL='内销'
@STHTPH varchar(20)='',
@EDHTPH varchar(20)='z',
@STDATE datetime,
@EDDATE datetime,
@QCDATE datetime,
@FIRST int=1
AS
CREATE TABLE #cpbtempdata (HTPH varchar(20) NOT NULL, ZDBH varchar(20) NOT NULL, JLR datetime NOT NULL, PZH varchar(26) NOT NULL, KHBH VARCHAR(20) NULL,KHJC VARCHAR(20) NULL,
XXX varchar(3) NOT NULL, HTPM varchar(40) NULL, HTDWMC varchar(10) NULL, PZR datetime NULL,
PZMC varchar(20) NOT NULL,LBPH varchar(20) NULL,LBPM varchar(40) NULL,LBDWMC varchar(10) NULL,
LBRKZ float NULL DEFAULT 0, HTRKZ float NULL DEFAULT 0, LBCKZ1 float NULL DEFAULT 0,
HTCKZ1 float NULL DEFAULT 0, LBCKZ2 float NULL DEFAULT 0, HTCKZ2 float NULL DEFAULT 0,
LBCKZ3 float NULL DEFAULT 0, HTCKZ3 float NULL DEFAULT 0, HTCKZ float NULL DEFAULT 0,JY FLOAT DEFAULT 0,paixu varchar(2)
PRIMARY KEY(HTPH,ZDBH,JLR,PZH,XXX)) --//总表
--清空库
TRUNCATE TABLE #cpbtempdata
--本期-------------------------------------------------------------------------------------------------------------
--J 成品入库--进---------------------------------
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,paixu)
SELECT 'J1',ZDBH, RKDH,'成品入库',HTPH,RKDR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,'a'
FROM CINORD_V
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and htrkz>=0
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,paixu)
SELECT 'J1',ZDBH, RKDH,'成品入库',HTPH,RKDR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,'b1'
FROM CINORD_V
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and htrkz<0
--J 补做接单入库--进---------------------------------
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,paixu)
SELECT 'J2',ZDBH, RKDH,'成品入库',HTPH,RKDR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,'a'
FROM CINORD_V
WHERE JLR<@QCDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
--X 销退入库--直接---------------------------------
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBCKZ1,HTCKZ1,KHBH,paixu)
SELECT 'X1',ZDBH,THDH,'销退入库',HTPH,THDR,JLR,LBPH,LBDWMC,-LBthZ,-HTthZ,KHBH,'b'
FROM RECINORD_V01
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and JZZL='直接出口'
--X 销退入库--间接---------------------------------
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBCKZ2,HTCKZ2,KHBH,paixu)
SELECT 'X2',ZDBH,THDH,'销退入库',HTPH,THDR,JLR,LBPH,LBDWMC,-LBthZ,-HTthZ,KHBH,'b'
FROM RECINORD_V01
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and JZZL='间接出口'
--X 销退入库--内销---------------------------------
INSERT INTO #cpbtempdata (XXX,ZDBH, PZH, PZMC, HTPH, PZR, JLR, LBPH, LBDWMC, LBCKZ3, HTCKZ3,KHBH,paixu)
SELECT 'X3',ZDBH, THDH, '销退入库', HTPH, THDR, JLR, LBPH,LBDWMC,-LBthZ,-HTthZ,KHBH,'b'
FROM RECINORD_V01
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and JZZL='内销'
#15
--X 成品出货--直接---------------------------------
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBCKZ1,HTCKZ1,KHBH,paixu)
SELECT 'X4',ZDBH, CHDH,'成品出货', HTPH, CHDR, JLR, LBPH, LBDWMC, LBCHZ, HTCHZ,KHBH,'c'
FROM COURD_V
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and JZZL='直接出口'
--X 成品出货--间接---------------------------------
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBCKZ2,HTCKZ2,KHBH,paixu)
SELECT 'X5',ZDBH, CHDH,'成品出货', HTPH, CHDR, JLR, LBPH, LBDWMC, LBCHZ, HTCHZ,KHBH,'c'
FROM COURD_V
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and JZZL='间接出口'
--X 成品出货--内销---------------------------------
INSERT INTO #cpbtempdata (XXX,ZDBH, PZH, PZMC, HTPH, KHBH, PZR, JLR,LBPH,LBDWMC,LBCKZ3,HTCKZ3,PAIXU)
SELECT 'X9',ZDBH,CHDH,'成品出货', HTPH,KHBH, CHDR, JLR,LBPH, LBDWMC, LBCHZ, HTCHZ,'c'
FROM courd_v
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and JZZL='内销'
and LXMC<>'海关批准内销'
and LXMC<>'其它内销'
INSERT INTO #cpbtempdata (XXX,ZDBH, PZH, PZMC, HTPH, KHBH, PZR, JLR,LBPH,LBDWMC,LBCKZ3,HTCKZ3,PAIXU)
SELECT 'X9',ZDBH,CHDH,'海关批准内销', HTPH,KHBH, CHDR, JLR,LBPH, LBDWMC, LBCHZ, HTCHZ,'c'
FROM courd_v
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and JZZL='内销' and LXMC='海关批准内销'
INSERT INTO #cpbtempdata (XXX,ZDBH, PZH, PZMC, HTPH, KHBH, PZR, JLR,LBPH,LBDWMC,LBCKZ3,HTCKZ3,PAIXU)
SELECT 'X8',ZDBH,CHDH,'其它内销', HTPH,KHBH, CHDR, JLR,LBPH, LBDWMC, LBCHZ, HTCHZ,'c'
FROM courd_v
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and JZZL='内销' and LXMC='其它内销'
--/////////////////////////////////////////////////////
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,paixu)
SELECT 'J11',ZDBH, RKDH,'成品入库',HTPH,RKDR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,'a'
FROM CINORD_V
WHERE JLR>=@QCDATE and JLR<@STDATE and htrkz>=0
and zdbh in (select zdbh from #cpbtempdata )
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,paixu)
SELECT 'J11',ZDBH, RKDH,'成品入库',HTPH,RKDR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,'b1'
FROM CINORD_V
WHERE JLR>=@QCDATE and JLR<@STDATE and htrkz<0
and zdbh in (select zdbh from #cpbtempdata )
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBCKZ1,HTCKZ1,KHBH,paixu)
SELECT 'X11',ZDBH,THDH,'销退入库',HTPH,THDR,JLR,LBPH,LBDWMC,-LBthZ,-HTthZ,KHBH,'b'
FROM RECINORD_V01
WHERE JLR>=@QCDATE and JLR<@STDATE
and zdbh in (select zdbh from #cpbtempdata )
and JZZL='直接出口'
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBCKZ2,HTCKZ2,KHBH,paixu)
SELECT 'X22',ZDBH,THDH,'销退入库',HTPH,THDR,JLR,LBPH,LBDWMC,-LBthZ,-HTthZ,KHBH,'b'
FROM RECINORD_V01
WHERE JLR>=@QCDATE and JLR<@STDATE
and zdbh in (select zdbh from #cpbtempdata )
and JZZL='间接出口'
--X 销退入库--内销---------------------------------
INSERT INTO #cpbtempdata (XXX,ZDBH, PZH, PZMC, HTPH, PZR, JLR, LBPH, LBDWMC, LBCKZ3, HTCKZ3,KHBH,paixu)
SELECT 'X33',ZDBH, THDH, '销退入库', HTPH, THDR, JLR, LBPH,LBDWMC,-LBthZ,-HTthZ,KHBH,'b'
FROM RECINORD_V01
WHERE JLR>=@QCDATE and JLR<@STDATE
and zdbh in (select zdbh from #cpbtempdata )
and JZZL='内销'
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBCKZ1,HTCKZ1,KHBH,paixu)
SELECT 'X44',ZDBH, CHDH,'成品出货', HTPH, CHDR, JLR, LBPH, LBDWMC, LBCHZ, HTCHZ,KHBH,'c'
FROM COURD_V
WHERE JLR>=@QCDATE and JLR<@STDATE
and zdbh in (select zdbh from #cpbtempdata )
and JZZL='直接出口'
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBCKZ2,HTCKZ2,KHBH,paixu)
SELECT 'X55',ZDBH, CHDH,'成品出货', HTPH, CHDR, JLR, LBPH, LBDWMC, LBCHZ, HTCHZ,KHBH,'c'
FROM COURD_V
WHERE JLR>=@QCDATE and JLR<@STDATE
and zdbh in (select zdbh from #cpbtempdata )
and JZZL='间接出口'
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBCKZ3,HTCKZ3,KHBH,paixu)
SELECT 'X66',ZDBH, CHDH,'成品出货', HTPH, CHDR, JLR, LBPH, LBDWMC, LBCHZ, HTCHZ,KHBH,'c'
FROM COURD_V
WHERE JLR>=@QCDATE and JLR<@STDATE
and zdbh in (select zdbh from #cpbtempdata )
and JZZL='内销'
--更新合同料件名称/单位名称
UPDATE #cpbtempdata SET HTPM=(SELECT TOP 1 HTPM FROM CMCBOM_V WHERE CMCBOM_V.HTPH=#cpbtempdata.HTPH),
HTDWMC=(SELECT TOP 1 DWMC FROM CMCBOM_V WHERE CMCBOM_V.HTPH=#cpbtempdata.HTPH)
UPDATE #cpbtempdata SET LBPM=(SELECT TOP 1 LBPM FROM MATER WHERE MATER.LBPH=#cpbtempdata.LBPH)
UPDATE #cpbtempdata SET KHJC=(SELECT KHJC FROM CUST WHERE CUST.KHBH=#cpbtempdata.KHBH)
IF(@FIRST=0)
SELECT * FROM #cpbtempdata order by htph,zdbh,jlr,paixu
ELSE IF(@FIRST=1)
SELECT * FROM #cpbtempdata WHERE ZDBH IN (SELECT ZDBH FROM JDRKCH_V WHERE HTJDZ=SUMHTRKZ AND HTJDZ=SUMHTCHZ AND LBJDZ=SUMLBRKZ AND LBJDZ=SUMLBCHZ) order by htph,zdbh,jlr,paixu
ELSE
SELECT * FROM #cpbtempdata WHERE ZDBH NOT IN(SELECT ZDBH FROM JDRKCH_V WHERE HTJDZ=SUMHTRKZ AND HTJDZ=SUMHTCHZ AND LBJDZ=SUMLBRKZ AND LBJDZ=SUMLBCHZ) order by htph,zdbh,jlr,paixu
GO
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBCKZ1,HTCKZ1,KHBH,paixu)
SELECT 'X4',ZDBH, CHDH,'成品出货', HTPH, CHDR, JLR, LBPH, LBDWMC, LBCHZ, HTCHZ,KHBH,'c'
FROM COURD_V
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and JZZL='直接出口'
--X 成品出货--间接---------------------------------
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBCKZ2,HTCKZ2,KHBH,paixu)
SELECT 'X5',ZDBH, CHDH,'成品出货', HTPH, CHDR, JLR, LBPH, LBDWMC, LBCHZ, HTCHZ,KHBH,'c'
FROM COURD_V
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and JZZL='间接出口'
--X 成品出货--内销---------------------------------
INSERT INTO #cpbtempdata (XXX,ZDBH, PZH, PZMC, HTPH, KHBH, PZR, JLR,LBPH,LBDWMC,LBCKZ3,HTCKZ3,PAIXU)
SELECT 'X9',ZDBH,CHDH,'成品出货', HTPH,KHBH, CHDR, JLR,LBPH, LBDWMC, LBCHZ, HTCHZ,'c'
FROM courd_v
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and JZZL='内销'
and LXMC<>'海关批准内销'
and LXMC<>'其它内销'
INSERT INTO #cpbtempdata (XXX,ZDBH, PZH, PZMC, HTPH, KHBH, PZR, JLR,LBPH,LBDWMC,LBCKZ3,HTCKZ3,PAIXU)
SELECT 'X9',ZDBH,CHDH,'海关批准内销', HTPH,KHBH, CHDR, JLR,LBPH, LBDWMC, LBCHZ, HTCHZ,'c'
FROM courd_v
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and JZZL='内销' and LXMC='海关批准内销'
INSERT INTO #cpbtempdata (XXX,ZDBH, PZH, PZMC, HTPH, KHBH, PZR, JLR,LBPH,LBDWMC,LBCKZ3,HTCKZ3,PAIXU)
SELECT 'X8',ZDBH,CHDH,'其它内销', HTPH,KHBH, CHDR, JLR,LBPH, LBDWMC, LBCHZ, HTCHZ,'c'
FROM courd_v
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and JZZL='内销' and LXMC='其它内销'
--/////////////////////////////////////////////////////
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,paixu)
SELECT 'J11',ZDBH, RKDH,'成品入库',HTPH,RKDR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,'a'
FROM CINORD_V
WHERE JLR>=@QCDATE and JLR<@STDATE and htrkz>=0
and zdbh in (select zdbh from #cpbtempdata )
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,paixu)
SELECT 'J11',ZDBH, RKDH,'成品入库',HTPH,RKDR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,'b1'
FROM CINORD_V
WHERE JLR>=@QCDATE and JLR<@STDATE and htrkz<0
and zdbh in (select zdbh from #cpbtempdata )
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBCKZ1,HTCKZ1,KHBH,paixu)
SELECT 'X11',ZDBH,THDH,'销退入库',HTPH,THDR,JLR,LBPH,LBDWMC,-LBthZ,-HTthZ,KHBH,'b'
FROM RECINORD_V01
WHERE JLR>=@QCDATE and JLR<@STDATE
and zdbh in (select zdbh from #cpbtempdata )
and JZZL='直接出口'
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBCKZ2,HTCKZ2,KHBH,paixu)
SELECT 'X22',ZDBH,THDH,'销退入库',HTPH,THDR,JLR,LBPH,LBDWMC,-LBthZ,-HTthZ,KHBH,'b'
FROM RECINORD_V01
WHERE JLR>=@QCDATE and JLR<@STDATE
and zdbh in (select zdbh from #cpbtempdata )
and JZZL='间接出口'
--X 销退入库--内销---------------------------------
INSERT INTO #cpbtempdata (XXX,ZDBH, PZH, PZMC, HTPH, PZR, JLR, LBPH, LBDWMC, LBCKZ3, HTCKZ3,KHBH,paixu)
SELECT 'X33',ZDBH, THDH, '销退入库', HTPH, THDR, JLR, LBPH,LBDWMC,-LBthZ,-HTthZ,KHBH,'b'
FROM RECINORD_V01
WHERE JLR>=@QCDATE and JLR<@STDATE
and zdbh in (select zdbh from #cpbtempdata )
and JZZL='内销'
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBCKZ1,HTCKZ1,KHBH,paixu)
SELECT 'X44',ZDBH, CHDH,'成品出货', HTPH, CHDR, JLR, LBPH, LBDWMC, LBCHZ, HTCHZ,KHBH,'c'
FROM COURD_V
WHERE JLR>=@QCDATE and JLR<@STDATE
and zdbh in (select zdbh from #cpbtempdata )
and JZZL='直接出口'
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBCKZ2,HTCKZ2,KHBH,paixu)
SELECT 'X55',ZDBH, CHDH,'成品出货', HTPH, CHDR, JLR, LBPH, LBDWMC, LBCHZ, HTCHZ,KHBH,'c'
FROM COURD_V
WHERE JLR>=@QCDATE and JLR<@STDATE
and zdbh in (select zdbh from #cpbtempdata )
and JZZL='间接出口'
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBCKZ3,HTCKZ3,KHBH,paixu)
SELECT 'X66',ZDBH, CHDH,'成品出货', HTPH, CHDR, JLR, LBPH, LBDWMC, LBCHZ, HTCHZ,KHBH,'c'
FROM COURD_V
WHERE JLR>=@QCDATE and JLR<@STDATE
and zdbh in (select zdbh from #cpbtempdata )
and JZZL='内销'
--更新合同料件名称/单位名称
UPDATE #cpbtempdata SET HTPM=(SELECT TOP 1 HTPM FROM CMCBOM_V WHERE CMCBOM_V.HTPH=#cpbtempdata.HTPH),
HTDWMC=(SELECT TOP 1 DWMC FROM CMCBOM_V WHERE CMCBOM_V.HTPH=#cpbtempdata.HTPH)
UPDATE #cpbtempdata SET LBPM=(SELECT TOP 1 LBPM FROM MATER WHERE MATER.LBPH=#cpbtempdata.LBPH)
UPDATE #cpbtempdata SET KHJC=(SELECT KHJC FROM CUST WHERE CUST.KHBH=#cpbtempdata.KHBH)
IF(@FIRST=0)
SELECT * FROM #cpbtempdata order by htph,zdbh,jlr,paixu
ELSE IF(@FIRST=1)
SELECT * FROM #cpbtempdata WHERE ZDBH IN (SELECT ZDBH FROM JDRKCH_V WHERE HTJDZ=SUMHTRKZ AND HTJDZ=SUMHTCHZ AND LBJDZ=SUMLBRKZ AND LBJDZ=SUMLBCHZ) order by htph,zdbh,jlr,paixu
ELSE
SELECT * FROM #cpbtempdata WHERE ZDBH NOT IN(SELECT ZDBH FROM JDRKCH_V WHERE HTJDZ=SUMHTRKZ AND HTJDZ=SUMHTCHZ AND LBJDZ=SUMLBRKZ AND LBJDZ=SUMLBCHZ) order by htph,zdbh,jlr,paixu
GO
#16
以上就是那个存储过程
我还没有建索引呢,只建了主键
我还没有建索引呢,只建了主键
#17
数据库的查询优化技术
数据库系统是管理信息系统的核心,基于数据库的联机事务处理(OLTP)以及联机分析处理(OLAP)是银行、企业、*等部门最为重要的计算机应用之一。从大多数系统的应用实例来看,查询操作在各种数据库操作中所占据的比重最大,而查询操作所基于的SELECT语句在SQL语句中又是代价最大的语句。举例来说,如果数据的量积累到一定的程度,比如一个银行的账户数据库表信息积累到上百万甚至上千万条记录,全表扫描一次往往需要数十分钟,甚至数小时。如果采用比全表扫描更好的查询策略,往往可以使查询时间降为几分钟,由此可见查询优化技术的重要性。
笔者在应用项目的实施中发现,许多程序员在利用一些前端数据库开发工具(如PowerBuilder、Delphi等)开发数据库应用程序时,只注重用户界面的华丽,并不重视查询语句的效率问题,导致所开发出来的应用系统效率低下,资源浪费严重。因此,如何设计高效合理的查询语句就显得非常重要。本文以应用实例为基础,结合数据库理论,介绍查询优化技术在现实系统中的运用。
分析问题
许多程序员认为查询优化是DBMS(数据库管理系统)的任务,与程序员所编写的SQL语句关系不大,这是错误的。一个好的查询计划往往可以使程序性能提高数十倍。查询计划是用户所提交的SQL语句的集合,查询规划是经过优化处理之后所产生的语句集合。DBMS处理查询计划的过程是这样的:在做完查询语句的词法、语法检查之后,将语句提交给DBMS的查询优化器,优化器做完代数优化和存取路径的优化之后,由预编译模块对语句进行处理并生成查询规划,然后在合适的时间提交给系统处理执行,最后将执行结果返回给用户。在实际的数据库产品(如Oracle、Sybase等)的高版本中都是采用基于代价的优化方法,这种优化能根据从系统字典表所得到的信息来估计不同的查询规划的代价,然后选择一个较优的规划。虽然现在的数据库产品在查询优化方面已经做得越来越好,但由用户提交的SQL语句是系统优化的基础,很难设想一个原本糟糕的查询计划经过系统的优化之后会变得高效,因此用户所写语句的优劣至关重要。系统所做查询优化我们暂不讨论,下面重点说明改善用户查询计划的解决方案。
解决问题
下面以关系数据库系统Informix为例,介绍改善用户查询计划的方法。
1.合理使用索引
索引是数据库中重要的数据结构,它的根本目的就是为了提高查询效率。现在大多数的数据库产品都采用IBM最先提出的ISAM索引结构。索引的使用要恰到好处,其使用原则如下:
●在经常进行连接,但是没有指定为外键的列上建立索引,而不经常连接的字段则由优化器自动生成索引。
●在频繁进行排序或分组(即进行group by或order by操作)的列上建立索引。
●在条件表达式中经常用到的不同值较多的列上建立检索,在不同值少的列上不要建立索引。比如在雇员表的“性别”列上只有“男”与“女”两个不同值,因此就无必要建立索引。如果建立索引不但不会提高查询效率,反而会严重降低更新速度。
●如果待排序的列有多个,可以在这些列上建立复合索引(compound index)。
●使用系统工具。如Informix数据库有一个tbcheck工具,可以在可疑的索引上进行检查。在一些数据库服务器上,索引可能失效或者因为频繁操作而使得读取效率降低,如果一个使用索引的查询不明不白地慢下来,可以试着用tbcheck工具检查索引的完整性,必要时进行修复。另外,当数据库表更新大量数据后,删除并重建索引可以提高查询速度。
2.避免或简化排序
应当简化或避免对大型表进行重复的排序。当能够利用索引自动以适当的次序产生输出时,优化器就避免了排序的步骤。以下是一些影响因素:
●索引中不包括一个或几个待排序的列;
●group by或order by子句中列的次序与索引的次序不一样;
●排序的列来自不同的表。
为了避免不必要的排序,就要正确地增建索引,合理地合并数据库表(尽管有时可能影响表的规范化,但相对于效率的提高是值得的)。如果排序不可避免,那么应当试图简化它,如缩小排序的列的范围等。
3.消除对大型表行数据的顺序存取
在嵌套查询中,对表的顺序存取对查询效率可能产生致命的影响。比如采用顺序存取策略,一个嵌套3层的查询,如果每层都查询1000行,那么这个查询就要查询10亿行数据。避免这种情况的主要方法就是对连接的列进行索引。例如,两个表:学生表(学号、姓名、年龄……)和选课表(学号、课程号、成绩)。如果两个表要做连接,就要在“学号”这个连接字段上建立索引。
还可以使用并集来避免顺序存取。尽管在所有的检查列上都有索引,但某些形式的where子句强迫优化器使用顺序存取。下面的查询将强迫对orders表执行顺序操作:
SELECT * FROM orders WHERE (customer_num=104 AND order_num>1001) OR order_num=1008
虽然在customer_num和order_num上建有索引,但是在上面的语句中优化器还是使用顺序存取路径扫描整个表。因为这个语句要检索的是分离的行的集合,所以应该改为如下语句:
SELECT * FROM orders WHERE customer_num=104 AND order_num>1001
UNION
SELECT * FROM orders WHERE order_num=1008
这样就能利用索引路径处理查询。
4.避免相关子查询
一个列的标签同时在主查询和where子句中的查询中出现,那么很可能当主查询中的列值改变之后,子查询必须重新查询一次。查询嵌套层次越多,效率越低,因此应当尽量避免子查询。如果子查询不可避免,那么要在子查询中过滤掉尽可能多的行。
5.避免困难的正规表达式
MATCHES和LIKE关键字支持通配符匹配,技术上叫正规表达式。但这种匹配特别耗费时间。例如:SELECT * FROM customer WHERE zipcode LIKE “98_ _ _”
即使在zipcode字段上建立了索引,在这种情况下也还是采用顺序扫描的方式。如果把语句改为SELECT * FROM customer WHERE zipcode >“98000”,在执行查询时就会利用索引来查询,显然会大大提高速度。
另外,还要避免非开始的子串。例如语句:SELECT * FROM customer WHERE zipcode[2,3] >“80”,在where子句中采用了非开始子串,因而这个语句也不会使用索引。
6.使用临时表加速查询
把表的一个子集进行排序并创建临时表,有时能加速查询。它有助于避免多重排序操作,而且在其他方面还能简化优化器的工作。例如:
SELECT cust.name,rcvbles.balance,……other columns
FROM cust,rcvbles
WHERE cust.customer_id = rcvlbes.customer_id
AND rcvblls.balance>0
AND cust.postcode>“98000”
ORDER BY cust.name
如果这个查询要被执行多次而不止一次,可以把所有未付款的客户找出来放在一个临时文件中,并按客户的名字进行排序:
SELECT cust.name,rcvbles.balance,……other columns
FROM cust,rcvbles
WHERE cust.customer_id = rcvlbes.customer_id
AND rcvblls.balance>0
ORDER BY cust.name
INTO TEMP cust_with_balance
然后以下面的方式在临时表中查询:
SELECT * FROM cust_with_balance
WHERE postcode>“98000”
临时表中的行要比主表中的行少,而且物理顺序就是所要求的顺序,减少了磁盘I/O,所以查询工作量可以得到大幅减少。
注意:临时表创建后不会反映主表的修改。在主表中数据频繁修改的情况下,注意不要丢失数据。
数据库系统是管理信息系统的核心,基于数据库的联机事务处理(OLTP)以及联机分析处理(OLAP)是银行、企业、*等部门最为重要的计算机应用之一。从大多数系统的应用实例来看,查询操作在各种数据库操作中所占据的比重最大,而查询操作所基于的SELECT语句在SQL语句中又是代价最大的语句。举例来说,如果数据的量积累到一定的程度,比如一个银行的账户数据库表信息积累到上百万甚至上千万条记录,全表扫描一次往往需要数十分钟,甚至数小时。如果采用比全表扫描更好的查询策略,往往可以使查询时间降为几分钟,由此可见查询优化技术的重要性。
笔者在应用项目的实施中发现,许多程序员在利用一些前端数据库开发工具(如PowerBuilder、Delphi等)开发数据库应用程序时,只注重用户界面的华丽,并不重视查询语句的效率问题,导致所开发出来的应用系统效率低下,资源浪费严重。因此,如何设计高效合理的查询语句就显得非常重要。本文以应用实例为基础,结合数据库理论,介绍查询优化技术在现实系统中的运用。
分析问题
许多程序员认为查询优化是DBMS(数据库管理系统)的任务,与程序员所编写的SQL语句关系不大,这是错误的。一个好的查询计划往往可以使程序性能提高数十倍。查询计划是用户所提交的SQL语句的集合,查询规划是经过优化处理之后所产生的语句集合。DBMS处理查询计划的过程是这样的:在做完查询语句的词法、语法检查之后,将语句提交给DBMS的查询优化器,优化器做完代数优化和存取路径的优化之后,由预编译模块对语句进行处理并生成查询规划,然后在合适的时间提交给系统处理执行,最后将执行结果返回给用户。在实际的数据库产品(如Oracle、Sybase等)的高版本中都是采用基于代价的优化方法,这种优化能根据从系统字典表所得到的信息来估计不同的查询规划的代价,然后选择一个较优的规划。虽然现在的数据库产品在查询优化方面已经做得越来越好,但由用户提交的SQL语句是系统优化的基础,很难设想一个原本糟糕的查询计划经过系统的优化之后会变得高效,因此用户所写语句的优劣至关重要。系统所做查询优化我们暂不讨论,下面重点说明改善用户查询计划的解决方案。
解决问题
下面以关系数据库系统Informix为例,介绍改善用户查询计划的方法。
1.合理使用索引
索引是数据库中重要的数据结构,它的根本目的就是为了提高查询效率。现在大多数的数据库产品都采用IBM最先提出的ISAM索引结构。索引的使用要恰到好处,其使用原则如下:
●在经常进行连接,但是没有指定为外键的列上建立索引,而不经常连接的字段则由优化器自动生成索引。
●在频繁进行排序或分组(即进行group by或order by操作)的列上建立索引。
●在条件表达式中经常用到的不同值较多的列上建立检索,在不同值少的列上不要建立索引。比如在雇员表的“性别”列上只有“男”与“女”两个不同值,因此就无必要建立索引。如果建立索引不但不会提高查询效率,反而会严重降低更新速度。
●如果待排序的列有多个,可以在这些列上建立复合索引(compound index)。
●使用系统工具。如Informix数据库有一个tbcheck工具,可以在可疑的索引上进行检查。在一些数据库服务器上,索引可能失效或者因为频繁操作而使得读取效率降低,如果一个使用索引的查询不明不白地慢下来,可以试着用tbcheck工具检查索引的完整性,必要时进行修复。另外,当数据库表更新大量数据后,删除并重建索引可以提高查询速度。
2.避免或简化排序
应当简化或避免对大型表进行重复的排序。当能够利用索引自动以适当的次序产生输出时,优化器就避免了排序的步骤。以下是一些影响因素:
●索引中不包括一个或几个待排序的列;
●group by或order by子句中列的次序与索引的次序不一样;
●排序的列来自不同的表。
为了避免不必要的排序,就要正确地增建索引,合理地合并数据库表(尽管有时可能影响表的规范化,但相对于效率的提高是值得的)。如果排序不可避免,那么应当试图简化它,如缩小排序的列的范围等。
3.消除对大型表行数据的顺序存取
在嵌套查询中,对表的顺序存取对查询效率可能产生致命的影响。比如采用顺序存取策略,一个嵌套3层的查询,如果每层都查询1000行,那么这个查询就要查询10亿行数据。避免这种情况的主要方法就是对连接的列进行索引。例如,两个表:学生表(学号、姓名、年龄……)和选课表(学号、课程号、成绩)。如果两个表要做连接,就要在“学号”这个连接字段上建立索引。
还可以使用并集来避免顺序存取。尽管在所有的检查列上都有索引,但某些形式的where子句强迫优化器使用顺序存取。下面的查询将强迫对orders表执行顺序操作:
SELECT * FROM orders WHERE (customer_num=104 AND order_num>1001) OR order_num=1008
虽然在customer_num和order_num上建有索引,但是在上面的语句中优化器还是使用顺序存取路径扫描整个表。因为这个语句要检索的是分离的行的集合,所以应该改为如下语句:
SELECT * FROM orders WHERE customer_num=104 AND order_num>1001
UNION
SELECT * FROM orders WHERE order_num=1008
这样就能利用索引路径处理查询。
4.避免相关子查询
一个列的标签同时在主查询和where子句中的查询中出现,那么很可能当主查询中的列值改变之后,子查询必须重新查询一次。查询嵌套层次越多,效率越低,因此应当尽量避免子查询。如果子查询不可避免,那么要在子查询中过滤掉尽可能多的行。
5.避免困难的正规表达式
MATCHES和LIKE关键字支持通配符匹配,技术上叫正规表达式。但这种匹配特别耗费时间。例如:SELECT * FROM customer WHERE zipcode LIKE “98_ _ _”
即使在zipcode字段上建立了索引,在这种情况下也还是采用顺序扫描的方式。如果把语句改为SELECT * FROM customer WHERE zipcode >“98000”,在执行查询时就会利用索引来查询,显然会大大提高速度。
另外,还要避免非开始的子串。例如语句:SELECT * FROM customer WHERE zipcode[2,3] >“80”,在where子句中采用了非开始子串,因而这个语句也不会使用索引。
6.使用临时表加速查询
把表的一个子集进行排序并创建临时表,有时能加速查询。它有助于避免多重排序操作,而且在其他方面还能简化优化器的工作。例如:
SELECT cust.name,rcvbles.balance,……other columns
FROM cust,rcvbles
WHERE cust.customer_id = rcvlbes.customer_id
AND rcvblls.balance>0
AND cust.postcode>“98000”
ORDER BY cust.name
如果这个查询要被执行多次而不止一次,可以把所有未付款的客户找出来放在一个临时文件中,并按客户的名字进行排序:
SELECT cust.name,rcvbles.balance,……other columns
FROM cust,rcvbles
WHERE cust.customer_id = rcvlbes.customer_id
AND rcvblls.balance>0
ORDER BY cust.name
INTO TEMP cust_with_balance
然后以下面的方式在临时表中查询:
SELECT * FROM cust_with_balance
WHERE postcode>“98000”
临时表中的行要比主表中的行少,而且物理顺序就是所要求的顺序,减少了磁盘I/O,所以查询工作量可以得到大幅减少。
注意:临时表创建后不会反映主表的修改。在主表中数据频繁修改的情况下,注意不要丢失数据。
#18
数据库的查询优化技术
数据库系统是管理信息系统的核心,基于数据库的联机事务处理(OLTP)以及联机分析处理(OLAP)是银行、企业、*等部门最为重要的计算机应用之一。从大多数系统的应用实例来看,查询操作在各种数据库操作中所占据的比重最大,而查询操作所基于的SELECT语句在SQL语句中又是代价最大的语句。举例来说,如果数据的量积累到一定的程度,比如一个银行的账户数据库表信息积累到上百万甚至上千万条记录,全表扫描一次往往需要数十分钟,甚至数小时。如果采用比全表扫描更好的查询策略,往往可以使查询时间降为几分钟,由此可见查询优化技术的重要性。
笔者在应用项目的实施中发现,许多程序员在利用一些前端数据库开发工具(如PowerBuilder、Delphi等)开发数据库应用程序时,只注重用户界面的华丽,并不重视查询语句的效率问题,导致所开发出来的应用系统效率低下,资源浪费严重。因此,如何设计高效合理的查询语句就显得非常重要。本文以应用实例为基础,结合数据库理论,介绍查询优化技术在现实系统中的运用。
分析问题
许多程序员认为查询优化是DBMS(数据库管理系统)的任务,与程序员所编写的SQL语句关系不大,这是错误的。一个好的查询计划往往可以使程序性能提高数十倍。查询计划是用户所提交的SQL语句的集合,查询规划是经过优化处理之后所产生的语句集合。DBMS处理查询计划的过程是这样的:在做完查询语句的词法、语法检查之后,将语句提交给DBMS的查询优化器,优化器做完代数优化和存取路径的优化之后,由预编译模块对语句进行处理并生成查询规划,然后在合适的时间提交给系统处理执行,最后将执行结果返回给用户。在实际的数据库产品(如Oracle、Sybase等)的高版本中都是采用基于代价的优化方法,这种优化能根据从系统字典表所得到的信息来估计不同的查询规划的代价,然后选择一个较优的规划。虽然现在的数据库产品在查询优化方面已经做得越来越好,但由用户提交的SQL语句是系统优化的基础,很难设想一个原本糟糕的查询计划经过系统的优化之后会变得高效,因此用户所写语句的优劣至关重要。系统所做查询优化我们暂不讨论,下面重点说明改善用户查询计划的解决方案。
解决问题
下面以关系数据库系统Informix为例,介绍改善用户查询计划的方法。
1.合理使用索引
索引是数据库中重要的数据结构,它的根本目的就是为了提高查询效率。现在大多数的数据库产品都采用IBM最先提出的ISAM索引结构。索引的使用要恰到好处,其使用原则如下:
●在经常进行连接,但是没有指定为外键的列上建立索引,而不经常连接的字段则由优化器自动生成索引。
●在频繁进行排序或分组(即进行group by或order by操作)的列上建立索引。
●在条件表达式中经常用到的不同值较多的列上建立检索,在不同值少的列上不要建立索引。比如在雇员表的“性别”列上只有“男”与“女”两个不同值,因此就无必要建立索引。如果建立索引不但不会提高查询效率,反而会严重降低更新速度。
●如果待排序的列有多个,可以在这些列上建立复合索引(compound index)。
●使用系统工具。如Informix数据库有一个tbcheck工具,可以在可疑的索引上进行检查。在一些数据库服务器上,索引可能失效或者因为频繁操作而使得读取效率降低,如果一个使用索引的查询不明不白地慢下来,可以试着用tbcheck工具检查索引的完整性,必要时进行修复。另外,当数据库表更新大量数据后,删除并重建索引可以提高查询速度。
2.避免或简化排序
应当简化或避免对大型表进行重复的排序。当能够利用索引自动以适当的次序产生输出时,优化器就避免了排序的步骤。以下是一些影响因素:
●索引中不包括一个或几个待排序的列;
●group by或order by子句中列的次序与索引的次序不一样;
●排序的列来自不同的表。
为了避免不必要的排序,就要正确地增建索引,合理地合并数据库表(尽管有时可能影响表的规范化,但相对于效率的提高是值得的)。如果排序不可避免,那么应当试图简化它,如缩小排序的列的范围等。
3.消除对大型表行数据的顺序存取
在嵌套查询中,对表的顺序存取对查询效率可能产生致命的影响。比如采用顺序存取策略,一个嵌套3层的查询,如果每层都查询1000行,那么这个查询就要查询10亿行数据。避免这种情况的主要方法就是对连接的列进行索引。例如,两个表:学生表(学号、姓名、年龄……)和选课表(学号、课程号、成绩)。如果两个表要做连接,就要在“学号”这个连接字段上建立索引。
还可以使用并集来避免顺序存取。尽管在所有的检查列上都有索引,但某些形式的where子句强迫优化器使用顺序存取。下面的查询将强迫对orders表执行顺序操作:
SELECT * FROM orders WHERE (customer_num=104 AND order_num>1001) OR order_num=1008
虽然在customer_num和order_num上建有索引,但是在上面的语句中优化器还是使用顺序存取路径扫描整个表。因为这个语句要检索的是分离的行的集合,所以应该改为如下语句:
SELECT * FROM orders WHERE customer_num=104 AND order_num>1001
UNION
SELECT * FROM orders WHERE order_num=1008
这样就能利用索引路径处理查询。
4.避免相关子查询
一个列的标签同时在主查询和where子句中的查询中出现,那么很可能当主查询中的列值改变之后,子查询必须重新查询一次。查询嵌套层次越多,效率越低,因此应当尽量避免子查询。如果子查询不可避免,那么要在子查询中过滤掉尽可能多的行。
5.避免困难的正规表达式
MATCHES和LIKE关键字支持通配符匹配,技术上叫正规表达式。但这种匹配特别耗费时间。例如:SELECT * FROM customer WHERE zipcode LIKE “98_ _ _”
即使在zipcode字段上建立了索引,在这种情况下也还是采用顺序扫描的方式。如果把语句改为SELECT * FROM customer WHERE zipcode >“98000”,在执行查询时就会利用索引来查询,显然会大大提高速度。
另外,还要避免非开始的子串。例如语句:SELECT * FROM customer WHERE zipcode[2,3] >“80”,在where子句中采用了非开始子串,因而这个语句也不会使用索引。
6.使用临时表加速查询
把表的一个子集进行排序并创建临时表,有时能加速查询。它有助于避免多重排序操作,而且在其他方面还能简化优化器的工作。例如:
SELECT cust.name,rcvbles.balance,……other columns
FROM cust,rcvbles
WHERE cust.customer_id = rcvlbes.customer_id
AND rcvblls.balance>0
AND cust.postcode>“98000”
ORDER BY cust.name
如果这个查询要被执行多次而不止一次,可以把所有未付款的客户找出来放在一个临时文件中,并按客户的名字进行排序:
SELECT cust.name,rcvbles.balance,……other columns
FROM cust,rcvbles
WHERE cust.customer_id = rcvlbes.customer_id
AND rcvblls.balance>0
ORDER BY cust.name
INTO TEMP cust_with_balance
然后以下面的方式在临时表中查询:
SELECT * FROM cust_with_balance
WHERE postcode>“98000”
临时表中的行要比主表中的行少,而且物理顺序就是所要求的顺序,减少了磁盘I/O,所以查询工作量可以得到大幅减少。
注意:临时表创建后不会反映主表的修改。在主表中数据频繁修改的情况下,注意不要丢失数据。
数据库系统是管理信息系统的核心,基于数据库的联机事务处理(OLTP)以及联机分析处理(OLAP)是银行、企业、*等部门最为重要的计算机应用之一。从大多数系统的应用实例来看,查询操作在各种数据库操作中所占据的比重最大,而查询操作所基于的SELECT语句在SQL语句中又是代价最大的语句。举例来说,如果数据的量积累到一定的程度,比如一个银行的账户数据库表信息积累到上百万甚至上千万条记录,全表扫描一次往往需要数十分钟,甚至数小时。如果采用比全表扫描更好的查询策略,往往可以使查询时间降为几分钟,由此可见查询优化技术的重要性。
笔者在应用项目的实施中发现,许多程序员在利用一些前端数据库开发工具(如PowerBuilder、Delphi等)开发数据库应用程序时,只注重用户界面的华丽,并不重视查询语句的效率问题,导致所开发出来的应用系统效率低下,资源浪费严重。因此,如何设计高效合理的查询语句就显得非常重要。本文以应用实例为基础,结合数据库理论,介绍查询优化技术在现实系统中的运用。
分析问题
许多程序员认为查询优化是DBMS(数据库管理系统)的任务,与程序员所编写的SQL语句关系不大,这是错误的。一个好的查询计划往往可以使程序性能提高数十倍。查询计划是用户所提交的SQL语句的集合,查询规划是经过优化处理之后所产生的语句集合。DBMS处理查询计划的过程是这样的:在做完查询语句的词法、语法检查之后,将语句提交给DBMS的查询优化器,优化器做完代数优化和存取路径的优化之后,由预编译模块对语句进行处理并生成查询规划,然后在合适的时间提交给系统处理执行,最后将执行结果返回给用户。在实际的数据库产品(如Oracle、Sybase等)的高版本中都是采用基于代价的优化方法,这种优化能根据从系统字典表所得到的信息来估计不同的查询规划的代价,然后选择一个较优的规划。虽然现在的数据库产品在查询优化方面已经做得越来越好,但由用户提交的SQL语句是系统优化的基础,很难设想一个原本糟糕的查询计划经过系统的优化之后会变得高效,因此用户所写语句的优劣至关重要。系统所做查询优化我们暂不讨论,下面重点说明改善用户查询计划的解决方案。
解决问题
下面以关系数据库系统Informix为例,介绍改善用户查询计划的方法。
1.合理使用索引
索引是数据库中重要的数据结构,它的根本目的就是为了提高查询效率。现在大多数的数据库产品都采用IBM最先提出的ISAM索引结构。索引的使用要恰到好处,其使用原则如下:
●在经常进行连接,但是没有指定为外键的列上建立索引,而不经常连接的字段则由优化器自动生成索引。
●在频繁进行排序或分组(即进行group by或order by操作)的列上建立索引。
●在条件表达式中经常用到的不同值较多的列上建立检索,在不同值少的列上不要建立索引。比如在雇员表的“性别”列上只有“男”与“女”两个不同值,因此就无必要建立索引。如果建立索引不但不会提高查询效率,反而会严重降低更新速度。
●如果待排序的列有多个,可以在这些列上建立复合索引(compound index)。
●使用系统工具。如Informix数据库有一个tbcheck工具,可以在可疑的索引上进行检查。在一些数据库服务器上,索引可能失效或者因为频繁操作而使得读取效率降低,如果一个使用索引的查询不明不白地慢下来,可以试着用tbcheck工具检查索引的完整性,必要时进行修复。另外,当数据库表更新大量数据后,删除并重建索引可以提高查询速度。
2.避免或简化排序
应当简化或避免对大型表进行重复的排序。当能够利用索引自动以适当的次序产生输出时,优化器就避免了排序的步骤。以下是一些影响因素:
●索引中不包括一个或几个待排序的列;
●group by或order by子句中列的次序与索引的次序不一样;
●排序的列来自不同的表。
为了避免不必要的排序,就要正确地增建索引,合理地合并数据库表(尽管有时可能影响表的规范化,但相对于效率的提高是值得的)。如果排序不可避免,那么应当试图简化它,如缩小排序的列的范围等。
3.消除对大型表行数据的顺序存取
在嵌套查询中,对表的顺序存取对查询效率可能产生致命的影响。比如采用顺序存取策略,一个嵌套3层的查询,如果每层都查询1000行,那么这个查询就要查询10亿行数据。避免这种情况的主要方法就是对连接的列进行索引。例如,两个表:学生表(学号、姓名、年龄……)和选课表(学号、课程号、成绩)。如果两个表要做连接,就要在“学号”这个连接字段上建立索引。
还可以使用并集来避免顺序存取。尽管在所有的检查列上都有索引,但某些形式的where子句强迫优化器使用顺序存取。下面的查询将强迫对orders表执行顺序操作:
SELECT * FROM orders WHERE (customer_num=104 AND order_num>1001) OR order_num=1008
虽然在customer_num和order_num上建有索引,但是在上面的语句中优化器还是使用顺序存取路径扫描整个表。因为这个语句要检索的是分离的行的集合,所以应该改为如下语句:
SELECT * FROM orders WHERE customer_num=104 AND order_num>1001
UNION
SELECT * FROM orders WHERE order_num=1008
这样就能利用索引路径处理查询。
4.避免相关子查询
一个列的标签同时在主查询和where子句中的查询中出现,那么很可能当主查询中的列值改变之后,子查询必须重新查询一次。查询嵌套层次越多,效率越低,因此应当尽量避免子查询。如果子查询不可避免,那么要在子查询中过滤掉尽可能多的行。
5.避免困难的正规表达式
MATCHES和LIKE关键字支持通配符匹配,技术上叫正规表达式。但这种匹配特别耗费时间。例如:SELECT * FROM customer WHERE zipcode LIKE “98_ _ _”
即使在zipcode字段上建立了索引,在这种情况下也还是采用顺序扫描的方式。如果把语句改为SELECT * FROM customer WHERE zipcode >“98000”,在执行查询时就会利用索引来查询,显然会大大提高速度。
另外,还要避免非开始的子串。例如语句:SELECT * FROM customer WHERE zipcode[2,3] >“80”,在where子句中采用了非开始子串,因而这个语句也不会使用索引。
6.使用临时表加速查询
把表的一个子集进行排序并创建临时表,有时能加速查询。它有助于避免多重排序操作,而且在其他方面还能简化优化器的工作。例如:
SELECT cust.name,rcvbles.balance,……other columns
FROM cust,rcvbles
WHERE cust.customer_id = rcvlbes.customer_id
AND rcvblls.balance>0
AND cust.postcode>“98000”
ORDER BY cust.name
如果这个查询要被执行多次而不止一次,可以把所有未付款的客户找出来放在一个临时文件中,并按客户的名字进行排序:
SELECT cust.name,rcvbles.balance,……other columns
FROM cust,rcvbles
WHERE cust.customer_id = rcvlbes.customer_id
AND rcvblls.balance>0
ORDER BY cust.name
INTO TEMP cust_with_balance
然后以下面的方式在临时表中查询:
SELECT * FROM cust_with_balance
WHERE postcode>“98000”
临时表中的行要比主表中的行少,而且物理顺序就是所要求的顺序,减少了磁盘I/O,所以查询工作量可以得到大幅减少。
注意:临时表创建后不会反映主表的修改。在主表中数据频繁修改的情况下,注意不要丢失数据。
#19
7.用排序来取代非顺序存取
非顺序磁盘存取是最慢的操作,表现在磁盘存取臂的来回移动。SQL语句隐藏了这一情况,使得我们在写应用程序时很容易写出要求存取大量非顺序页的查询。
有些时候,用数据库的排序能力来替代非顺序的存取能改进查询。
实例分析
下面我们举一个制造公司的例子来说明如何进行查询优化。制造公司数据库中包括3个表,模式如下所示:
1.part表
零件号零件描述其他列
(part_num)(part_desc)(other column)
102,032Seageat 30G disk……
500,049Novel 10M network card……
……
2.vendor表
厂商号厂商名其他列
(vendor _num)(vendor_name) (other column)
910,257Seageat Corp……
523,045IBM Corp……
……
3.parven表
零件号厂商号零件数量
(part_num)(vendor_num)(part_amount)
102,032910,2573,450,000
234,423321,0014,000,000
……
下面的查询将在这些表上定期运行,并产生关于所有零件数量的报表:
SELECT part_desc,vendor_name,part_amount
FROM part,vendor,parven
WHERE part.part_num=parven.part_num
AND parven.vendor_num = vendor.vendor_num
ORDER BY part.part_num
如果不建立索引,上述查询代码的开销将十分巨大。为此,我们在零件号和厂商号上建立索引。索引的建立避免了在嵌套中反复扫描。关于表与索引的统计信息如下:
表行尺寸行数量每页行数量数据页数量
(table)(row size)(Row count)(Rows/Pages)(Data Pages)
part15010,00025400
Vendor1501,000 2540
Parven13 15,000300 50
索引键尺寸每页键数量页面数量
(Indexes)(Key Size)(Keys/Page)(Leaf Pages)
part450020
Vendor45002
Parven825060
看起来是个相对简单的3表连接,但是其查询开销是很大的。通过查看系统表可以看到,在part_num上和vendor_num上有簇索引,因此索引是按照物理顺序存放的。parven表没有特定的存放次序。这些表的大小说明从缓冲页中非顺序存取的成功率很小。此语句的优化查询规划是:首先从part中顺序读取400页,然后再对parven表非顺序存取1万次,每次2页(一个索引页、一个数据页),总计2万个磁盘页,最后对vendor表非顺序存取1.5万次,合3万个磁盘页。可以看出在这个索引好的连接上花费的磁盘存取为5.04万次。
实际上,我们可以通过使用临时表分3个步骤来提高查询效率:
1.从parven表中按vendor_num的次序读数据:
SELECT part_num,vendor_num,price
FROM parven
ORDER BY vendor_num
INTO temp pv_by_vn
这个语句顺序读parven(50页),写一个临时表(50页),并排序。假定排序的开销为200页,总共是300页。
2.把临时表和vendor表连接,把结果输出到一个临时表,并按part_num排序:
SELECT pv_by_vn,* vendor.vendor_num
FROM pv_by_vn,vendor
WHERE pv_by_vn.vendor_num=vendor.vendor_num
ORDER BY pv_by_vn.part_num
INTO TMP pvvn_by_pn
DROP TABLE pv_by_vn
这个查询读取pv_by_vn(50页),它通过索引存取vendor表1.5万次,但由于按vendor_num次序排列,实际上只是通过索引顺序地读vendor表(40+2=42页),输出的表每页约95行,共160页。写并存取这些页引发5*160=800次的读写,索引共读写892页。
3.把输出和part连接得到最后的结果:
SELECT pvvn_by_pn.*,part.part_desc
FROM pvvn_by_pn,part
WHERE pvvn_by_pn.part_num=part.part_num
DROP TABLE pvvn_by_pn
这样,查询顺序地读pvvn_by_pn(160页),通过索引读part表1.5万次,由于建有索引,所以实际上进行1772次磁盘读写,优化比例为30∶1。笔者在Informix Dynamic Sever上做同样的实验,发现在时间耗费上的优化比例为5∶1(如果增加数据量,比例可能会更大)。
小结
20%的代码用去了80%的时间,这是程序设计中的一个著名定律,在数据库应用程序中也同样如此。我们的优化要抓住关键问题,对于数据库应用程序来说,重点在于SQL的执行效率。查询优化的重点环节是使得数据库服务器少从磁盘中读数据以及顺序读页而不是非顺序读页。
非顺序磁盘存取是最慢的操作,表现在磁盘存取臂的来回移动。SQL语句隐藏了这一情况,使得我们在写应用程序时很容易写出要求存取大量非顺序页的查询。
有些时候,用数据库的排序能力来替代非顺序的存取能改进查询。
实例分析
下面我们举一个制造公司的例子来说明如何进行查询优化。制造公司数据库中包括3个表,模式如下所示:
1.part表
零件号零件描述其他列
(part_num)(part_desc)(other column)
102,032Seageat 30G disk……
500,049Novel 10M network card……
……
2.vendor表
厂商号厂商名其他列
(vendor _num)(vendor_name) (other column)
910,257Seageat Corp……
523,045IBM Corp……
……
3.parven表
零件号厂商号零件数量
(part_num)(vendor_num)(part_amount)
102,032910,2573,450,000
234,423321,0014,000,000
……
下面的查询将在这些表上定期运行,并产生关于所有零件数量的报表:
SELECT part_desc,vendor_name,part_amount
FROM part,vendor,parven
WHERE part.part_num=parven.part_num
AND parven.vendor_num = vendor.vendor_num
ORDER BY part.part_num
如果不建立索引,上述查询代码的开销将十分巨大。为此,我们在零件号和厂商号上建立索引。索引的建立避免了在嵌套中反复扫描。关于表与索引的统计信息如下:
表行尺寸行数量每页行数量数据页数量
(table)(row size)(Row count)(Rows/Pages)(Data Pages)
part15010,00025400
Vendor1501,000 2540
Parven13 15,000300 50
索引键尺寸每页键数量页面数量
(Indexes)(Key Size)(Keys/Page)(Leaf Pages)
part450020
Vendor45002
Parven825060
看起来是个相对简单的3表连接,但是其查询开销是很大的。通过查看系统表可以看到,在part_num上和vendor_num上有簇索引,因此索引是按照物理顺序存放的。parven表没有特定的存放次序。这些表的大小说明从缓冲页中非顺序存取的成功率很小。此语句的优化查询规划是:首先从part中顺序读取400页,然后再对parven表非顺序存取1万次,每次2页(一个索引页、一个数据页),总计2万个磁盘页,最后对vendor表非顺序存取1.5万次,合3万个磁盘页。可以看出在这个索引好的连接上花费的磁盘存取为5.04万次。
实际上,我们可以通过使用临时表分3个步骤来提高查询效率:
1.从parven表中按vendor_num的次序读数据:
SELECT part_num,vendor_num,price
FROM parven
ORDER BY vendor_num
INTO temp pv_by_vn
这个语句顺序读parven(50页),写一个临时表(50页),并排序。假定排序的开销为200页,总共是300页。
2.把临时表和vendor表连接,把结果输出到一个临时表,并按part_num排序:
SELECT pv_by_vn,* vendor.vendor_num
FROM pv_by_vn,vendor
WHERE pv_by_vn.vendor_num=vendor.vendor_num
ORDER BY pv_by_vn.part_num
INTO TMP pvvn_by_pn
DROP TABLE pv_by_vn
这个查询读取pv_by_vn(50页),它通过索引存取vendor表1.5万次,但由于按vendor_num次序排列,实际上只是通过索引顺序地读vendor表(40+2=42页),输出的表每页约95行,共160页。写并存取这些页引发5*160=800次的读写,索引共读写892页。
3.把输出和part连接得到最后的结果:
SELECT pvvn_by_pn.*,part.part_desc
FROM pvvn_by_pn,part
WHERE pvvn_by_pn.part_num=part.part_num
DROP TABLE pvvn_by_pn
这样,查询顺序地读pvvn_by_pn(160页),通过索引读part表1.5万次,由于建有索引,所以实际上进行1772次磁盘读写,优化比例为30∶1。笔者在Informix Dynamic Sever上做同样的实验,发现在时间耗费上的优化比例为5∶1(如果增加数据量,比例可能会更大)。
小结
20%的代码用去了80%的时间,这是程序设计中的一个著名定律,在数据库应用程序中也同样如此。我们的优化要抓住关键问题,对于数据库应用程序来说,重点在于SQL的执行效率。查询优化的重点环节是使得数据库服务器少从磁盘中读数据以及顺序读页而不是非顺序读页。
#20
解决了,多谢,是一个表没有建索引,谢谢,查了我好久,给分
#21
#1
你数据量为什么要这么大呢?你的用户可以一口气看完几十万行记录吗?你没有必要要这么做,要不你用分页技术每页1000行,用户能看完了不起了。
declare @SQLStr varchar(8000)
set @SQLStr='SELECT Top '+cast(@每页大小 as varchar)+' * FROM 表 WHERE 主键列 NOT IN (SELECT TOP '+cast(@每页大小*@第几页 as varchar)+' 主键列 from 表 )'
exec(@SQLStr)
declare @SQLStr varchar(8000)
set @SQLStr='SELECT Top '+cast(@每页大小 as varchar)+' * FROM 表 WHERE 主键列 NOT IN (SELECT TOP '+cast(@每页大小*@第几页 as varchar)+' 主键列 from 表 )'
exec(@SQLStr)
#2
select IDENTITY(int, 1,1) AS ID_Num,* into #temp from 表
select * from #temp where ID_Num between 10 and 20
select * from #temp where ID_Num between 10 and 20
#3
这个问题不应该从设置超时上去解决.
应该对你的数据库进行优化.创建必要的主键,索引
查询数据时避免一次性取大量的数据到客户端等.
应该对你的数据库进行优化.创建必要的主键,索引
查询数据时避免一次性取大量的数据到客户端等.
#4
推荐你看一篇优化SQL的摘文,希望对你有所帮助
如何让你的SQL运行得更快
---- 人们在使用SQL时往往会陷入一个误区,即太关注于所得的结果是否正确,而忽略
了不同的实现方法之间可能存在的性能差异,这种性能差异在大型的或是复杂的数据库
环境中(如联机事务处理OLTP或决策支持系统DSS)中表现得尤为明显。笔者在工作实践
中发现,不良的SQL往往来自于不恰当的索引设计、不充份的连接条件和不可优化的whe
re子句。在对它们进行适当的优化后,其运行速度有了明显地提高!下面我将从这三个
方面分别进行总结:
---- 为了更直观地说明问题,所有实例中的SQL运行时间均经过测试,不超过1秒的均
表示为(< 1秒)。
---- 测试环境--
---- 主机:HP LH II
---- 主频:330MHZ
---- 内存:128兆
---- 操作系统:Operserver5.0.4
----数据库:Sybase11.0.3
一、不合理的索引设计
----例:表record有620000行,试看在不同的索引下,下面几个 SQL的运行情况:
---- 1.在date上建有一非个群集索引
select count(*) from record where date >
'19991201' and date < '19991214'and amount >
2000 (25秒)
select date,sum(amount) from record group by date
(55秒)
select count(*) from record where date >
'19990901' and place in ('BJ','SH') (27秒)
---- 分析:
----date上有大量的重复值,在非群集索引下,数据在物理上随机存放在数据页上,在
范围查找时,必须执行一次表扫描才能找到这一范围内的全部行。
---- 2.在date上的一个群集索引
select count(*) from record where date >
'19991201' and date < '19991214' and amount >
2000 (14秒)
select date,sum(amount) from record group by date
(28秒)
select count(*) from record where date >
'19990901' and place in ('BJ','SH')(14秒)
---- 分析:
---- 在群集索引下,数据在物理上按顺序在数据页上,重复值也排列在一起,因而在范
围查找时,可以先找到这个范围的起末点,且只在这个范围内扫描数据页,避免了大范
围扫描,提高了查询速度。
---- 3.在place,date,amount上的组合索引
select count(*) from record where date >
'19991201' and date < '19991214' and amount >
2000 (26秒)
select date,sum(amount) from record group by date
(27秒)
select count(*) from record where date >
'19990901' and place in ('BJ', 'SH')(< 1秒)
---- 分析:
---- 这是一个不很合理的组合索引,因为它的前导列是place,第一和第二条SQL没有引
用place,因此也没有利用上索引;第三个SQL使用了place,且引用的所有列都包含在组
合索引中,形成了索引覆盖,所以它的速度是非常快的。
---- 4.在date,place,amount上的组合索引
select count(*) from record where date >
'19991201' and date < '19991214' and amount >
2000(< 1秒)
select date,sum(amount) from record group by date
(11秒)
select count(*) from record where date >
'19990901' and place in ('BJ','SH')(< 1秒)
---- 分析:
---- 这是一个合理的组合索引。它将date作为前导列,使每个SQL都可以利用索引,并
且在第一和第三个SQL中形成了索引覆盖,因而性能达到了最优。
---- 5.总结:
---- 缺省情况下建立的索引是非群集索引,但有时它并不是最佳的;合理的索引设计要
建立在对各种查询的分析和预测上。一般来说:
---- ①.有大量重复值、且经常有范围查询
(between, >,< ,>=,< =)和order by
、group by发生的列,可考虑建立群集索引;
---- ②.经常同时存取多列,且每列都含有重复值可考虑建立组合索引;
---- ③.组合索引要尽量使关键查询形成索引覆盖,其前导列一定是使用最频繁的列。
二、不充份的连接条件:
---- 例:表card有7896行,在card_no上有一个非聚集索引,表account有191122行,在
account_no上有一个非聚集索引,试看在不同的表连接条件下,两个SQL的执行情况:
select sum(a.amount) from account a,
card b where a.card_no = b.card_no(20秒)
---- 将SQL改为:
select sum(a.amount) from account a,
card b where a.card_no = b.card_no and a.
account_no=b.account_no(< 1秒)
---- 分析:
---- 在第一个连接条件下,最佳查询方案是将account作外层表,card作内层表,利用
card上的索引,其I/O次数可由以下公式估算为:
---- 外层表account上的22541页+(外层表account的191122行*内层表card上对应外层
表第一行所要查找的3页)=595907次I/O
---- 在第二个连接条件下,最佳查询方案是将card作外层表,account作内层表,利用
account上的索引,其I/O次数可由以下公式估算为:
---- 外层表card上的1944页+(外层表card的7896行*内层表account上对应外层表每一
行所要查找的4页)= 33528次I/O
---- 可见,只有充份的连接条件,真正的最佳方案才会被执行。
---- 总结:
---- 1.多表操作在被实际执行前,查询优化器会根据连接条件,列出几组可能的连接方
案并从中找出系统开销最小的最佳方案。连接条件要充份考虑带有索引的表、行数多的
表;内外表的选择可由公式:外层表中的匹配行数*内层表中每一次查找的次数确定,乘
积最小为最佳方案。
---- 2.查看执行方案的方法-- 用set showplanon,打开showplan选项,就可以看到连
接顺序、使用何种索引的信息;想看更详细的信息,需用sa角色执行dbcc(3604,310,30
2)。
三、不可优化的where子句
---- 1.例:下列SQL条件语句中的列都建有恰当的索引,但执行速度却非常慢:
select * from record where
substring(card_no,1,4)='5378'(13秒)
select * from record where
amount/30< 1000(11秒)
select * from record where
convert(char(10),date,112)='19991201'(10秒)
---- 分析:
---- where子句中对列的任何操作结果都是在SQL运行时逐列计算得到的,因此它不得不
进行表搜索,而没有使用该列上面的索引;如果这些结果在查询编译时就能得到,那么
就可以被SQL优化器优化,使用索引,避免表搜索,因此将SQL重写成下面这样:
select * from record where card_no like
'5378%'(< 1秒)
select * from record where amount
< 1000*30(< 1秒)
select * from record where date= '1999/12/01'
(< 1秒)
---- 你会发现SQL明显快起来!
---- 2.例:表stuff有200000行,id_no上有非群集索引,请看下面这个SQL:
select count(*) from stuff where id_no in('0','1')
(23秒)
---- 分析:
---- where条件中的'in'在逻辑上相当于'or',所以语法分析器会将in ('0','1')转化
为id_no ='0' or id_no='1'来执行。我们期望它会根据每个or子句分别查找,再将结果
相加,这样可以利用id_no上的索引;但实际上(根据showplan),它却采用了"OR策略"
,即先取出满足每个or子句的行,存入临时数据库的工作表中,再建立唯一索引以去掉
重复行,最后从这个临时表中计算结果。因此,实际过程没有利用id_no上索引,并且完
成时间还要受tempdb数据库性能的影响。
---- 实践证明,表的行数越多,工作表的性能就越差,当stuff有620000行时,执行时
间竟达到220秒!还不如将or子句分开:
select count(*) from stuff where id_no='0'
select count(*) from stuff where id_no='1'
---- 得到两个结果,再作一次加法合算。因为每句都使用了索引,执行时间只有3秒,
在620000行下,时间也只有4秒。或者,用更好的方法,写一个简单的存储过程:
create proc count_stuff as
declare @a int
declare @b int
declare @c int
declare @d char(10)
begin
select @a=count(*) from stuff where id_no='0'
select @b=count(*) from stuff where id_no='1'
end
select @c=@a+@b
select @d=convert(char(10),@c)
print @d
---- 直接算出结果,执行时间同上面一样快!
---- 总结:
---- 可见,所谓优化即where子句利用了索引,不可优化即发生了表扫描或额外开销。
---- 1.任何对列的操作都将导致表扫描,它包括数据库函数、计算表达式等等,查询时
要尽可能将操作移至等号右边。
---- 2.in、or子句常会使用工作表,使索引失效;如果不产生大量重复值,可以考虑把
子句拆开;拆开的子句中应该包含索引。
---- 3.要善于使用存储过程,它使SQL变得更加灵活和高效。
---- 从以上这些例子可以看出,SQL优化的实质就是在结果正确的前提下,用优化器可
以识别的语句,充份利用索引,减少表扫描的I/O次数,尽量避免表搜索的发生。其实S
QL的性能优化是一个复杂的过程,上述这些只是在应用层次的一种体现,深入研究还会
涉及数据库层的资源配置、网络层的流量控制以及操作系统层的总体设计。
如何让你的SQL运行得更快
---- 人们在使用SQL时往往会陷入一个误区,即太关注于所得的结果是否正确,而忽略
了不同的实现方法之间可能存在的性能差异,这种性能差异在大型的或是复杂的数据库
环境中(如联机事务处理OLTP或决策支持系统DSS)中表现得尤为明显。笔者在工作实践
中发现,不良的SQL往往来自于不恰当的索引设计、不充份的连接条件和不可优化的whe
re子句。在对它们进行适当的优化后,其运行速度有了明显地提高!下面我将从这三个
方面分别进行总结:
---- 为了更直观地说明问题,所有实例中的SQL运行时间均经过测试,不超过1秒的均
表示为(< 1秒)。
---- 测试环境--
---- 主机:HP LH II
---- 主频:330MHZ
---- 内存:128兆
---- 操作系统:Operserver5.0.4
----数据库:Sybase11.0.3
一、不合理的索引设计
----例:表record有620000行,试看在不同的索引下,下面几个 SQL的运行情况:
---- 1.在date上建有一非个群集索引
select count(*) from record where date >
'19991201' and date < '19991214'and amount >
2000 (25秒)
select date,sum(amount) from record group by date
(55秒)
select count(*) from record where date >
'19990901' and place in ('BJ','SH') (27秒)
---- 分析:
----date上有大量的重复值,在非群集索引下,数据在物理上随机存放在数据页上,在
范围查找时,必须执行一次表扫描才能找到这一范围内的全部行。
---- 2.在date上的一个群集索引
select count(*) from record where date >
'19991201' and date < '19991214' and amount >
2000 (14秒)
select date,sum(amount) from record group by date
(28秒)
select count(*) from record where date >
'19990901' and place in ('BJ','SH')(14秒)
---- 分析:
---- 在群集索引下,数据在物理上按顺序在数据页上,重复值也排列在一起,因而在范
围查找时,可以先找到这个范围的起末点,且只在这个范围内扫描数据页,避免了大范
围扫描,提高了查询速度。
---- 3.在place,date,amount上的组合索引
select count(*) from record where date >
'19991201' and date < '19991214' and amount >
2000 (26秒)
select date,sum(amount) from record group by date
(27秒)
select count(*) from record where date >
'19990901' and place in ('BJ', 'SH')(< 1秒)
---- 分析:
---- 这是一个不很合理的组合索引,因为它的前导列是place,第一和第二条SQL没有引
用place,因此也没有利用上索引;第三个SQL使用了place,且引用的所有列都包含在组
合索引中,形成了索引覆盖,所以它的速度是非常快的。
---- 4.在date,place,amount上的组合索引
select count(*) from record where date >
'19991201' and date < '19991214' and amount >
2000(< 1秒)
select date,sum(amount) from record group by date
(11秒)
select count(*) from record where date >
'19990901' and place in ('BJ','SH')(< 1秒)
---- 分析:
---- 这是一个合理的组合索引。它将date作为前导列,使每个SQL都可以利用索引,并
且在第一和第三个SQL中形成了索引覆盖,因而性能达到了最优。
---- 5.总结:
---- 缺省情况下建立的索引是非群集索引,但有时它并不是最佳的;合理的索引设计要
建立在对各种查询的分析和预测上。一般来说:
---- ①.有大量重复值、且经常有范围查询
(between, >,< ,>=,< =)和order by
、group by发生的列,可考虑建立群集索引;
---- ②.经常同时存取多列,且每列都含有重复值可考虑建立组合索引;
---- ③.组合索引要尽量使关键查询形成索引覆盖,其前导列一定是使用最频繁的列。
二、不充份的连接条件:
---- 例:表card有7896行,在card_no上有一个非聚集索引,表account有191122行,在
account_no上有一个非聚集索引,试看在不同的表连接条件下,两个SQL的执行情况:
select sum(a.amount) from account a,
card b where a.card_no = b.card_no(20秒)
---- 将SQL改为:
select sum(a.amount) from account a,
card b where a.card_no = b.card_no and a.
account_no=b.account_no(< 1秒)
---- 分析:
---- 在第一个连接条件下,最佳查询方案是将account作外层表,card作内层表,利用
card上的索引,其I/O次数可由以下公式估算为:
---- 外层表account上的22541页+(外层表account的191122行*内层表card上对应外层
表第一行所要查找的3页)=595907次I/O
---- 在第二个连接条件下,最佳查询方案是将card作外层表,account作内层表,利用
account上的索引,其I/O次数可由以下公式估算为:
---- 外层表card上的1944页+(外层表card的7896行*内层表account上对应外层表每一
行所要查找的4页)= 33528次I/O
---- 可见,只有充份的连接条件,真正的最佳方案才会被执行。
---- 总结:
---- 1.多表操作在被实际执行前,查询优化器会根据连接条件,列出几组可能的连接方
案并从中找出系统开销最小的最佳方案。连接条件要充份考虑带有索引的表、行数多的
表;内外表的选择可由公式:外层表中的匹配行数*内层表中每一次查找的次数确定,乘
积最小为最佳方案。
---- 2.查看执行方案的方法-- 用set showplanon,打开showplan选项,就可以看到连
接顺序、使用何种索引的信息;想看更详细的信息,需用sa角色执行dbcc(3604,310,30
2)。
三、不可优化的where子句
---- 1.例:下列SQL条件语句中的列都建有恰当的索引,但执行速度却非常慢:
select * from record where
substring(card_no,1,4)='5378'(13秒)
select * from record where
amount/30< 1000(11秒)
select * from record where
convert(char(10),date,112)='19991201'(10秒)
---- 分析:
---- where子句中对列的任何操作结果都是在SQL运行时逐列计算得到的,因此它不得不
进行表搜索,而没有使用该列上面的索引;如果这些结果在查询编译时就能得到,那么
就可以被SQL优化器优化,使用索引,避免表搜索,因此将SQL重写成下面这样:
select * from record where card_no like
'5378%'(< 1秒)
select * from record where amount
< 1000*30(< 1秒)
select * from record where date= '1999/12/01'
(< 1秒)
---- 你会发现SQL明显快起来!
---- 2.例:表stuff有200000行,id_no上有非群集索引,请看下面这个SQL:
select count(*) from stuff where id_no in('0','1')
(23秒)
---- 分析:
---- where条件中的'in'在逻辑上相当于'or',所以语法分析器会将in ('0','1')转化
为id_no ='0' or id_no='1'来执行。我们期望它会根据每个or子句分别查找,再将结果
相加,这样可以利用id_no上的索引;但实际上(根据showplan),它却采用了"OR策略"
,即先取出满足每个or子句的行,存入临时数据库的工作表中,再建立唯一索引以去掉
重复行,最后从这个临时表中计算结果。因此,实际过程没有利用id_no上索引,并且完
成时间还要受tempdb数据库性能的影响。
---- 实践证明,表的行数越多,工作表的性能就越差,当stuff有620000行时,执行时
间竟达到220秒!还不如将or子句分开:
select count(*) from stuff where id_no='0'
select count(*) from stuff where id_no='1'
---- 得到两个结果,再作一次加法合算。因为每句都使用了索引,执行时间只有3秒,
在620000行下,时间也只有4秒。或者,用更好的方法,写一个简单的存储过程:
create proc count_stuff as
declare @a int
declare @b int
declare @c int
declare @d char(10)
begin
select @a=count(*) from stuff where id_no='0'
select @b=count(*) from stuff where id_no='1'
end
select @c=@a+@b
select @d=convert(char(10),@c)
print @d
---- 直接算出结果,执行时间同上面一样快!
---- 总结:
---- 可见,所谓优化即where子句利用了索引,不可优化即发生了表扫描或额外开销。
---- 1.任何对列的操作都将导致表扫描,它包括数据库函数、计算表达式等等,查询时
要尽可能将操作移至等号右边。
---- 2.in、or子句常会使用工作表,使索引失效;如果不产生大量重复值,可以考虑把
子句拆开;拆开的子句中应该包含索引。
---- 3.要善于使用存储过程,它使SQL变得更加灵活和高效。
---- 从以上这些例子可以看出,SQL优化的实质就是在结果正确的前提下,用优化器可
以识别的语句,充份利用索引,减少表扫描的I/O次数,尽量避免表搜索的发生。其实S
QL的性能优化是一个复杂的过程,上述这些只是在应用层次的一种体现,深入研究还会
涉及数据库层的资源配置、网络层的流量控制以及操作系统层的总体设计。
#5
好长,都看得眼花了,决定不看了
#6
超过半分钟的语句就算不超时用户也受不了了,看能否优化一下你的语句吧。
#7
谢谢各位朋友!
我测试了,主要是在执行存储过程时,出现超时!
我测试了,主要是在执行存储过程时,出现超时!
#8
SP???
那是SP的运算有问题吧,或者用游标没释放什么的
那是SP的运算有问题吧,或者用游标没释放什么的
#9
在报表列印之前,需要执行一个存储过程,里面也没有用到游标,只是建了一个临时表,然后分别从试图和表向其中插入数据而已,语句都很简单,好像也没有什么可优化的!
#10
必须列印那么多
因为存在需要显示期初和期末的问题,比如:你列印1-5月和5-9月 和1-9月时,那么列印5-9月就会存在一个期初的问题,所以必须一次性把1-9月的数据全部列印出来
因为存在需要显示期初和期末的问题,比如:你列印1-5月和5-9月 和1-9月时,那么列印5-9月就会存在一个期初的问题,所以必须一次性把1-9月的数据全部列印出来
#11
把你的SP贴出来看看!
#12
优化索引,优化语句.
引与系统性能
索引可以加快数据检索的速度但它会使数据的插入删除和更新变慢尤其是簇索
引数据是按照逻辑顺序存放在一定的物理位置当变更数据时根据新的数据顺序需
要将许多数据进行物理位置的移动这将增加系统的负担对非簇索引数据更新时也需
要更新索引页这也需要占用系统时间因此在一个表中使用太多的索引会影响数据库
的性能对于一个经常会改变的表应该尽量限制表只使用一个簇索引和不超过3~4 个
非簇索引对事务处理特别繁重的表其索引应尽量不超过3 个
索引调整向导Index Tuning Wizard
索引调整向导可以帮助选择并创建一个最优化的索引集合以提高数据库的性能
要使用索引调整向导需要一个工作负荷记录Workload 工作负荷记录由SQL 脚
本或SQL Server Profiler 创建的存储在文件或表中的跟踪组成如果没有现存的针对要进
行索引调整的数据库或表的工作负荷记录可以通过SQL Server Profiler 来创建一个其
具体方法请参见第19 章中SQL Server Profiler 的用法可以用Sample 1 – TSQL 跟踪定
义来创建或新建一个跟踪索引调整向导可以使用查询优化器根据工作负荷记录分析索引
的性能并提出相应的调整建议可以立即让系统根据建议修改索引也可以将任务列入
计划以后再创建
引与系统性能
索引可以加快数据检索的速度但它会使数据的插入删除和更新变慢尤其是簇索
引数据是按照逻辑顺序存放在一定的物理位置当变更数据时根据新的数据顺序需
要将许多数据进行物理位置的移动这将增加系统的负担对非簇索引数据更新时也需
要更新索引页这也需要占用系统时间因此在一个表中使用太多的索引会影响数据库
的性能对于一个经常会改变的表应该尽量限制表只使用一个簇索引和不超过3~4 个
非簇索引对事务处理特别繁重的表其索引应尽量不超过3 个
索引调整向导Index Tuning Wizard
索引调整向导可以帮助选择并创建一个最优化的索引集合以提高数据库的性能
要使用索引调整向导需要一个工作负荷记录Workload 工作负荷记录由SQL 脚
本或SQL Server Profiler 创建的存储在文件或表中的跟踪组成如果没有现存的针对要进
行索引调整的数据库或表的工作负荷记录可以通过SQL Server Profiler 来创建一个其
具体方法请参见第19 章中SQL Server Profiler 的用法可以用Sample 1 – TSQL 跟踪定
义来创建或新建一个跟踪索引调整向导可以使用查询优化器根据工作负荷记录分析索引
的性能并提出相应的调整建议可以立即让系统根据建议修改索引也可以将任务列入
计划以后再创建
#13
收藏
#14
CREATE PROCEDURE [REP_CPBCRKMX001]
@STHTPH varchar(20)='',
@EDHTPH varchar(20)='z',
@STDATE datetime,
@EDDATE datetime,
@QCDATE datetime,
@FIRST int=1
AS
CREATE TABLE #cpbtempdata (HTPH varchar(20) NOT NULL, ZDBH varchar(20) NOT NULL, JLR datetime NOT NULL, PZH varchar(26) NOT NULL, KHBH VARCHAR(20) NULL,KHJC VARCHAR(20) NULL,
XXX varchar(3) NOT NULL, HTPM varchar(40) NULL, HTDWMC varchar(10) NULL, PZR datetime NULL,
PZMC varchar(20) NOT NULL,LBPH varchar(20) NULL,LBPM varchar(40) NULL,LBDWMC varchar(10) NULL,
LBRKZ float NULL DEFAULT 0, HTRKZ float NULL DEFAULT 0, LBCKZ1 float NULL DEFAULT 0,
HTCKZ1 float NULL DEFAULT 0, LBCKZ2 float NULL DEFAULT 0, HTCKZ2 float NULL DEFAULT 0,
LBCKZ3 float NULL DEFAULT 0, HTCKZ3 float NULL DEFAULT 0, HTCKZ float NULL DEFAULT 0,JY FLOAT DEFAULT 0,paixu varchar(2)
PRIMARY KEY(HTPH,ZDBH,JLR,PZH,XXX)) --//总表
--清空库
TRUNCATE TABLE #cpbtempdata
--本期-------------------------------------------------------------------------------------------------------------
--J 成品入库--进---------------------------------
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,paixu)
SELECT 'J1',ZDBH, RKDH,'成品入库',HTPH,RKDR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,'a'
FROM CINORD_V
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and htrkz>=0
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,paixu)
SELECT 'J1',ZDBH, RKDH,'成品入库',HTPH,RKDR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,'b1'
FROM CINORD_V
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and htrkz<0
--J 补做接单入库--进---------------------------------
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,paixu)
SELECT 'J2',ZDBH, RKDH,'成品入库',HTPH,RKDR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,'a'
FROM CINORD_V
WHERE JLR<@QCDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
--X 销退入库--直接---------------------------------
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBCKZ1,HTCKZ1,KHBH,paixu)
SELECT 'X1',ZDBH,THDH,'销退入库',HTPH,THDR,JLR,LBPH,LBDWMC,-LBthZ,-HTthZ,KHBH,'b'
FROM RECINORD_V01
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and JZZL='直接出口'
--X 销退入库--间接---------------------------------
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBCKZ2,HTCKZ2,KHBH,paixu)
SELECT 'X2',ZDBH,THDH,'销退入库',HTPH,THDR,JLR,LBPH,LBDWMC,-LBthZ,-HTthZ,KHBH,'b'
FROM RECINORD_V01
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and JZZL='间接出口'
--X 销退入库--内销---------------------------------
INSERT INTO #cpbtempdata (XXX,ZDBH, PZH, PZMC, HTPH, PZR, JLR, LBPH, LBDWMC, LBCKZ3, HTCKZ3,KHBH,paixu)
SELECT 'X3',ZDBH, THDH, '销退入库', HTPH, THDR, JLR, LBPH,LBDWMC,-LBthZ,-HTthZ,KHBH,'b'
FROM RECINORD_V01
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and JZZL='内销'
@STHTPH varchar(20)='',
@EDHTPH varchar(20)='z',
@STDATE datetime,
@EDDATE datetime,
@QCDATE datetime,
@FIRST int=1
AS
CREATE TABLE #cpbtempdata (HTPH varchar(20) NOT NULL, ZDBH varchar(20) NOT NULL, JLR datetime NOT NULL, PZH varchar(26) NOT NULL, KHBH VARCHAR(20) NULL,KHJC VARCHAR(20) NULL,
XXX varchar(3) NOT NULL, HTPM varchar(40) NULL, HTDWMC varchar(10) NULL, PZR datetime NULL,
PZMC varchar(20) NOT NULL,LBPH varchar(20) NULL,LBPM varchar(40) NULL,LBDWMC varchar(10) NULL,
LBRKZ float NULL DEFAULT 0, HTRKZ float NULL DEFAULT 0, LBCKZ1 float NULL DEFAULT 0,
HTCKZ1 float NULL DEFAULT 0, LBCKZ2 float NULL DEFAULT 0, HTCKZ2 float NULL DEFAULT 0,
LBCKZ3 float NULL DEFAULT 0, HTCKZ3 float NULL DEFAULT 0, HTCKZ float NULL DEFAULT 0,JY FLOAT DEFAULT 0,paixu varchar(2)
PRIMARY KEY(HTPH,ZDBH,JLR,PZH,XXX)) --//总表
--清空库
TRUNCATE TABLE #cpbtempdata
--本期-------------------------------------------------------------------------------------------------------------
--J 成品入库--进---------------------------------
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,paixu)
SELECT 'J1',ZDBH, RKDH,'成品入库',HTPH,RKDR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,'a'
FROM CINORD_V
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and htrkz>=0
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,paixu)
SELECT 'J1',ZDBH, RKDH,'成品入库',HTPH,RKDR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,'b1'
FROM CINORD_V
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and htrkz<0
--J 补做接单入库--进---------------------------------
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,paixu)
SELECT 'J2',ZDBH, RKDH,'成品入库',HTPH,RKDR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,'a'
FROM CINORD_V
WHERE JLR<@QCDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
--X 销退入库--直接---------------------------------
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBCKZ1,HTCKZ1,KHBH,paixu)
SELECT 'X1',ZDBH,THDH,'销退入库',HTPH,THDR,JLR,LBPH,LBDWMC,-LBthZ,-HTthZ,KHBH,'b'
FROM RECINORD_V01
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and JZZL='直接出口'
--X 销退入库--间接---------------------------------
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBCKZ2,HTCKZ2,KHBH,paixu)
SELECT 'X2',ZDBH,THDH,'销退入库',HTPH,THDR,JLR,LBPH,LBDWMC,-LBthZ,-HTthZ,KHBH,'b'
FROM RECINORD_V01
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and JZZL='间接出口'
--X 销退入库--内销---------------------------------
INSERT INTO #cpbtempdata (XXX,ZDBH, PZH, PZMC, HTPH, PZR, JLR, LBPH, LBDWMC, LBCKZ3, HTCKZ3,KHBH,paixu)
SELECT 'X3',ZDBH, THDH, '销退入库', HTPH, THDR, JLR, LBPH,LBDWMC,-LBthZ,-HTthZ,KHBH,'b'
FROM RECINORD_V01
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and JZZL='内销'
#15
--X 成品出货--直接---------------------------------
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBCKZ1,HTCKZ1,KHBH,paixu)
SELECT 'X4',ZDBH, CHDH,'成品出货', HTPH, CHDR, JLR, LBPH, LBDWMC, LBCHZ, HTCHZ,KHBH,'c'
FROM COURD_V
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and JZZL='直接出口'
--X 成品出货--间接---------------------------------
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBCKZ2,HTCKZ2,KHBH,paixu)
SELECT 'X5',ZDBH, CHDH,'成品出货', HTPH, CHDR, JLR, LBPH, LBDWMC, LBCHZ, HTCHZ,KHBH,'c'
FROM COURD_V
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and JZZL='间接出口'
--X 成品出货--内销---------------------------------
INSERT INTO #cpbtempdata (XXX,ZDBH, PZH, PZMC, HTPH, KHBH, PZR, JLR,LBPH,LBDWMC,LBCKZ3,HTCKZ3,PAIXU)
SELECT 'X9',ZDBH,CHDH,'成品出货', HTPH,KHBH, CHDR, JLR,LBPH, LBDWMC, LBCHZ, HTCHZ,'c'
FROM courd_v
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and JZZL='内销'
and LXMC<>'海关批准内销'
and LXMC<>'其它内销'
INSERT INTO #cpbtempdata (XXX,ZDBH, PZH, PZMC, HTPH, KHBH, PZR, JLR,LBPH,LBDWMC,LBCKZ3,HTCKZ3,PAIXU)
SELECT 'X9',ZDBH,CHDH,'海关批准内销', HTPH,KHBH, CHDR, JLR,LBPH, LBDWMC, LBCHZ, HTCHZ,'c'
FROM courd_v
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and JZZL='内销' and LXMC='海关批准内销'
INSERT INTO #cpbtempdata (XXX,ZDBH, PZH, PZMC, HTPH, KHBH, PZR, JLR,LBPH,LBDWMC,LBCKZ3,HTCKZ3,PAIXU)
SELECT 'X8',ZDBH,CHDH,'其它内销', HTPH,KHBH, CHDR, JLR,LBPH, LBDWMC, LBCHZ, HTCHZ,'c'
FROM courd_v
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and JZZL='内销' and LXMC='其它内销'
--/////////////////////////////////////////////////////
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,paixu)
SELECT 'J11',ZDBH, RKDH,'成品入库',HTPH,RKDR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,'a'
FROM CINORD_V
WHERE JLR>=@QCDATE and JLR<@STDATE and htrkz>=0
and zdbh in (select zdbh from #cpbtempdata )
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,paixu)
SELECT 'J11',ZDBH, RKDH,'成品入库',HTPH,RKDR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,'b1'
FROM CINORD_V
WHERE JLR>=@QCDATE and JLR<@STDATE and htrkz<0
and zdbh in (select zdbh from #cpbtempdata )
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBCKZ1,HTCKZ1,KHBH,paixu)
SELECT 'X11',ZDBH,THDH,'销退入库',HTPH,THDR,JLR,LBPH,LBDWMC,-LBthZ,-HTthZ,KHBH,'b'
FROM RECINORD_V01
WHERE JLR>=@QCDATE and JLR<@STDATE
and zdbh in (select zdbh from #cpbtempdata )
and JZZL='直接出口'
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBCKZ2,HTCKZ2,KHBH,paixu)
SELECT 'X22',ZDBH,THDH,'销退入库',HTPH,THDR,JLR,LBPH,LBDWMC,-LBthZ,-HTthZ,KHBH,'b'
FROM RECINORD_V01
WHERE JLR>=@QCDATE and JLR<@STDATE
and zdbh in (select zdbh from #cpbtempdata )
and JZZL='间接出口'
--X 销退入库--内销---------------------------------
INSERT INTO #cpbtempdata (XXX,ZDBH, PZH, PZMC, HTPH, PZR, JLR, LBPH, LBDWMC, LBCKZ3, HTCKZ3,KHBH,paixu)
SELECT 'X33',ZDBH, THDH, '销退入库', HTPH, THDR, JLR, LBPH,LBDWMC,-LBthZ,-HTthZ,KHBH,'b'
FROM RECINORD_V01
WHERE JLR>=@QCDATE and JLR<@STDATE
and zdbh in (select zdbh from #cpbtempdata )
and JZZL='内销'
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBCKZ1,HTCKZ1,KHBH,paixu)
SELECT 'X44',ZDBH, CHDH,'成品出货', HTPH, CHDR, JLR, LBPH, LBDWMC, LBCHZ, HTCHZ,KHBH,'c'
FROM COURD_V
WHERE JLR>=@QCDATE and JLR<@STDATE
and zdbh in (select zdbh from #cpbtempdata )
and JZZL='直接出口'
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBCKZ2,HTCKZ2,KHBH,paixu)
SELECT 'X55',ZDBH, CHDH,'成品出货', HTPH, CHDR, JLR, LBPH, LBDWMC, LBCHZ, HTCHZ,KHBH,'c'
FROM COURD_V
WHERE JLR>=@QCDATE and JLR<@STDATE
and zdbh in (select zdbh from #cpbtempdata )
and JZZL='间接出口'
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBCKZ3,HTCKZ3,KHBH,paixu)
SELECT 'X66',ZDBH, CHDH,'成品出货', HTPH, CHDR, JLR, LBPH, LBDWMC, LBCHZ, HTCHZ,KHBH,'c'
FROM COURD_V
WHERE JLR>=@QCDATE and JLR<@STDATE
and zdbh in (select zdbh from #cpbtempdata )
and JZZL='内销'
--更新合同料件名称/单位名称
UPDATE #cpbtempdata SET HTPM=(SELECT TOP 1 HTPM FROM CMCBOM_V WHERE CMCBOM_V.HTPH=#cpbtempdata.HTPH),
HTDWMC=(SELECT TOP 1 DWMC FROM CMCBOM_V WHERE CMCBOM_V.HTPH=#cpbtempdata.HTPH)
UPDATE #cpbtempdata SET LBPM=(SELECT TOP 1 LBPM FROM MATER WHERE MATER.LBPH=#cpbtempdata.LBPH)
UPDATE #cpbtempdata SET KHJC=(SELECT KHJC FROM CUST WHERE CUST.KHBH=#cpbtempdata.KHBH)
IF(@FIRST=0)
SELECT * FROM #cpbtempdata order by htph,zdbh,jlr,paixu
ELSE IF(@FIRST=1)
SELECT * FROM #cpbtempdata WHERE ZDBH IN (SELECT ZDBH FROM JDRKCH_V WHERE HTJDZ=SUMHTRKZ AND HTJDZ=SUMHTCHZ AND LBJDZ=SUMLBRKZ AND LBJDZ=SUMLBCHZ) order by htph,zdbh,jlr,paixu
ELSE
SELECT * FROM #cpbtempdata WHERE ZDBH NOT IN(SELECT ZDBH FROM JDRKCH_V WHERE HTJDZ=SUMHTRKZ AND HTJDZ=SUMHTCHZ AND LBJDZ=SUMLBRKZ AND LBJDZ=SUMLBCHZ) order by htph,zdbh,jlr,paixu
GO
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBCKZ1,HTCKZ1,KHBH,paixu)
SELECT 'X4',ZDBH, CHDH,'成品出货', HTPH, CHDR, JLR, LBPH, LBDWMC, LBCHZ, HTCHZ,KHBH,'c'
FROM COURD_V
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and JZZL='直接出口'
--X 成品出货--间接---------------------------------
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBCKZ2,HTCKZ2,KHBH,paixu)
SELECT 'X5',ZDBH, CHDH,'成品出货', HTPH, CHDR, JLR, LBPH, LBDWMC, LBCHZ, HTCHZ,KHBH,'c'
FROM COURD_V
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and JZZL='间接出口'
--X 成品出货--内销---------------------------------
INSERT INTO #cpbtempdata (XXX,ZDBH, PZH, PZMC, HTPH, KHBH, PZR, JLR,LBPH,LBDWMC,LBCKZ3,HTCKZ3,PAIXU)
SELECT 'X9',ZDBH,CHDH,'成品出货', HTPH,KHBH, CHDR, JLR,LBPH, LBDWMC, LBCHZ, HTCHZ,'c'
FROM courd_v
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and JZZL='内销'
and LXMC<>'海关批准内销'
and LXMC<>'其它内销'
INSERT INTO #cpbtempdata (XXX,ZDBH, PZH, PZMC, HTPH, KHBH, PZR, JLR,LBPH,LBDWMC,LBCKZ3,HTCKZ3,PAIXU)
SELECT 'X9',ZDBH,CHDH,'海关批准内销', HTPH,KHBH, CHDR, JLR,LBPH, LBDWMC, LBCHZ, HTCHZ,'c'
FROM courd_v
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and JZZL='内销' and LXMC='海关批准内销'
INSERT INTO #cpbtempdata (XXX,ZDBH, PZH, PZMC, HTPH, KHBH, PZR, JLR,LBPH,LBDWMC,LBCKZ3,HTCKZ3,PAIXU)
SELECT 'X8',ZDBH,CHDH,'其它内销', HTPH,KHBH, CHDR, JLR,LBPH, LBDWMC, LBCHZ, HTCHZ,'c'
FROM courd_v
WHERE JLR>=@QCDATE and JLR>=@STDATE and JLR<=@EDDATE
and HTPH>=@STHTPH and HTPH<=@EDHTPH
and JZZL='内销' and LXMC='其它内销'
--/////////////////////////////////////////////////////
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,paixu)
SELECT 'J11',ZDBH, RKDH,'成品入库',HTPH,RKDR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,'a'
FROM CINORD_V
WHERE JLR>=@QCDATE and JLR<@STDATE and htrkz>=0
and zdbh in (select zdbh from #cpbtempdata )
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,paixu)
SELECT 'J11',ZDBH, RKDH,'成品入库',HTPH,RKDR,JLR,LBPH,LBDWMC,LBRKZ,HTRKZ,'b1'
FROM CINORD_V
WHERE JLR>=@QCDATE and JLR<@STDATE and htrkz<0
and zdbh in (select zdbh from #cpbtempdata )
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBCKZ1,HTCKZ1,KHBH,paixu)
SELECT 'X11',ZDBH,THDH,'销退入库',HTPH,THDR,JLR,LBPH,LBDWMC,-LBthZ,-HTthZ,KHBH,'b'
FROM RECINORD_V01
WHERE JLR>=@QCDATE and JLR<@STDATE
and zdbh in (select zdbh from #cpbtempdata )
and JZZL='直接出口'
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBCKZ2,HTCKZ2,KHBH,paixu)
SELECT 'X22',ZDBH,THDH,'销退入库',HTPH,THDR,JLR,LBPH,LBDWMC,-LBthZ,-HTthZ,KHBH,'b'
FROM RECINORD_V01
WHERE JLR>=@QCDATE and JLR<@STDATE
and zdbh in (select zdbh from #cpbtempdata )
and JZZL='间接出口'
--X 销退入库--内销---------------------------------
INSERT INTO #cpbtempdata (XXX,ZDBH, PZH, PZMC, HTPH, PZR, JLR, LBPH, LBDWMC, LBCKZ3, HTCKZ3,KHBH,paixu)
SELECT 'X33',ZDBH, THDH, '销退入库', HTPH, THDR, JLR, LBPH,LBDWMC,-LBthZ,-HTthZ,KHBH,'b'
FROM RECINORD_V01
WHERE JLR>=@QCDATE and JLR<@STDATE
and zdbh in (select zdbh from #cpbtempdata )
and JZZL='内销'
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBCKZ1,HTCKZ1,KHBH,paixu)
SELECT 'X44',ZDBH, CHDH,'成品出货', HTPH, CHDR, JLR, LBPH, LBDWMC, LBCHZ, HTCHZ,KHBH,'c'
FROM COURD_V
WHERE JLR>=@QCDATE and JLR<@STDATE
and zdbh in (select zdbh from #cpbtempdata )
and JZZL='直接出口'
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBCKZ2,HTCKZ2,KHBH,paixu)
SELECT 'X55',ZDBH, CHDH,'成品出货', HTPH, CHDR, JLR, LBPH, LBDWMC, LBCHZ, HTCHZ,KHBH,'c'
FROM COURD_V
WHERE JLR>=@QCDATE and JLR<@STDATE
and zdbh in (select zdbh from #cpbtempdata )
and JZZL='间接出口'
INSERT INTO #cpbtempdata (XXX,ZDBH,PZH,PZMC,HTPH,PZR,JLR,LBPH,LBDWMC,LBCKZ3,HTCKZ3,KHBH,paixu)
SELECT 'X66',ZDBH, CHDH,'成品出货', HTPH, CHDR, JLR, LBPH, LBDWMC, LBCHZ, HTCHZ,KHBH,'c'
FROM COURD_V
WHERE JLR>=@QCDATE and JLR<@STDATE
and zdbh in (select zdbh from #cpbtempdata )
and JZZL='内销'
--更新合同料件名称/单位名称
UPDATE #cpbtempdata SET HTPM=(SELECT TOP 1 HTPM FROM CMCBOM_V WHERE CMCBOM_V.HTPH=#cpbtempdata.HTPH),
HTDWMC=(SELECT TOP 1 DWMC FROM CMCBOM_V WHERE CMCBOM_V.HTPH=#cpbtempdata.HTPH)
UPDATE #cpbtempdata SET LBPM=(SELECT TOP 1 LBPM FROM MATER WHERE MATER.LBPH=#cpbtempdata.LBPH)
UPDATE #cpbtempdata SET KHJC=(SELECT KHJC FROM CUST WHERE CUST.KHBH=#cpbtempdata.KHBH)
IF(@FIRST=0)
SELECT * FROM #cpbtempdata order by htph,zdbh,jlr,paixu
ELSE IF(@FIRST=1)
SELECT * FROM #cpbtempdata WHERE ZDBH IN (SELECT ZDBH FROM JDRKCH_V WHERE HTJDZ=SUMHTRKZ AND HTJDZ=SUMHTCHZ AND LBJDZ=SUMLBRKZ AND LBJDZ=SUMLBCHZ) order by htph,zdbh,jlr,paixu
ELSE
SELECT * FROM #cpbtempdata WHERE ZDBH NOT IN(SELECT ZDBH FROM JDRKCH_V WHERE HTJDZ=SUMHTRKZ AND HTJDZ=SUMHTCHZ AND LBJDZ=SUMLBRKZ AND LBJDZ=SUMLBCHZ) order by htph,zdbh,jlr,paixu
GO
#16
以上就是那个存储过程
我还没有建索引呢,只建了主键
我还没有建索引呢,只建了主键
#17
数据库的查询优化技术
数据库系统是管理信息系统的核心,基于数据库的联机事务处理(OLTP)以及联机分析处理(OLAP)是银行、企业、*等部门最为重要的计算机应用之一。从大多数系统的应用实例来看,查询操作在各种数据库操作中所占据的比重最大,而查询操作所基于的SELECT语句在SQL语句中又是代价最大的语句。举例来说,如果数据的量积累到一定的程度,比如一个银行的账户数据库表信息积累到上百万甚至上千万条记录,全表扫描一次往往需要数十分钟,甚至数小时。如果采用比全表扫描更好的查询策略,往往可以使查询时间降为几分钟,由此可见查询优化技术的重要性。
笔者在应用项目的实施中发现,许多程序员在利用一些前端数据库开发工具(如PowerBuilder、Delphi等)开发数据库应用程序时,只注重用户界面的华丽,并不重视查询语句的效率问题,导致所开发出来的应用系统效率低下,资源浪费严重。因此,如何设计高效合理的查询语句就显得非常重要。本文以应用实例为基础,结合数据库理论,介绍查询优化技术在现实系统中的运用。
分析问题
许多程序员认为查询优化是DBMS(数据库管理系统)的任务,与程序员所编写的SQL语句关系不大,这是错误的。一个好的查询计划往往可以使程序性能提高数十倍。查询计划是用户所提交的SQL语句的集合,查询规划是经过优化处理之后所产生的语句集合。DBMS处理查询计划的过程是这样的:在做完查询语句的词法、语法检查之后,将语句提交给DBMS的查询优化器,优化器做完代数优化和存取路径的优化之后,由预编译模块对语句进行处理并生成查询规划,然后在合适的时间提交给系统处理执行,最后将执行结果返回给用户。在实际的数据库产品(如Oracle、Sybase等)的高版本中都是采用基于代价的优化方法,这种优化能根据从系统字典表所得到的信息来估计不同的查询规划的代价,然后选择一个较优的规划。虽然现在的数据库产品在查询优化方面已经做得越来越好,但由用户提交的SQL语句是系统优化的基础,很难设想一个原本糟糕的查询计划经过系统的优化之后会变得高效,因此用户所写语句的优劣至关重要。系统所做查询优化我们暂不讨论,下面重点说明改善用户查询计划的解决方案。
解决问题
下面以关系数据库系统Informix为例,介绍改善用户查询计划的方法。
1.合理使用索引
索引是数据库中重要的数据结构,它的根本目的就是为了提高查询效率。现在大多数的数据库产品都采用IBM最先提出的ISAM索引结构。索引的使用要恰到好处,其使用原则如下:
●在经常进行连接,但是没有指定为外键的列上建立索引,而不经常连接的字段则由优化器自动生成索引。
●在频繁进行排序或分组(即进行group by或order by操作)的列上建立索引。
●在条件表达式中经常用到的不同值较多的列上建立检索,在不同值少的列上不要建立索引。比如在雇员表的“性别”列上只有“男”与“女”两个不同值,因此就无必要建立索引。如果建立索引不但不会提高查询效率,反而会严重降低更新速度。
●如果待排序的列有多个,可以在这些列上建立复合索引(compound index)。
●使用系统工具。如Informix数据库有一个tbcheck工具,可以在可疑的索引上进行检查。在一些数据库服务器上,索引可能失效或者因为频繁操作而使得读取效率降低,如果一个使用索引的查询不明不白地慢下来,可以试着用tbcheck工具检查索引的完整性,必要时进行修复。另外,当数据库表更新大量数据后,删除并重建索引可以提高查询速度。
2.避免或简化排序
应当简化或避免对大型表进行重复的排序。当能够利用索引自动以适当的次序产生输出时,优化器就避免了排序的步骤。以下是一些影响因素:
●索引中不包括一个或几个待排序的列;
●group by或order by子句中列的次序与索引的次序不一样;
●排序的列来自不同的表。
为了避免不必要的排序,就要正确地增建索引,合理地合并数据库表(尽管有时可能影响表的规范化,但相对于效率的提高是值得的)。如果排序不可避免,那么应当试图简化它,如缩小排序的列的范围等。
3.消除对大型表行数据的顺序存取
在嵌套查询中,对表的顺序存取对查询效率可能产生致命的影响。比如采用顺序存取策略,一个嵌套3层的查询,如果每层都查询1000行,那么这个查询就要查询10亿行数据。避免这种情况的主要方法就是对连接的列进行索引。例如,两个表:学生表(学号、姓名、年龄……)和选课表(学号、课程号、成绩)。如果两个表要做连接,就要在“学号”这个连接字段上建立索引。
还可以使用并集来避免顺序存取。尽管在所有的检查列上都有索引,但某些形式的where子句强迫优化器使用顺序存取。下面的查询将强迫对orders表执行顺序操作:
SELECT * FROM orders WHERE (customer_num=104 AND order_num>1001) OR order_num=1008
虽然在customer_num和order_num上建有索引,但是在上面的语句中优化器还是使用顺序存取路径扫描整个表。因为这个语句要检索的是分离的行的集合,所以应该改为如下语句:
SELECT * FROM orders WHERE customer_num=104 AND order_num>1001
UNION
SELECT * FROM orders WHERE order_num=1008
这样就能利用索引路径处理查询。
4.避免相关子查询
一个列的标签同时在主查询和where子句中的查询中出现,那么很可能当主查询中的列值改变之后,子查询必须重新查询一次。查询嵌套层次越多,效率越低,因此应当尽量避免子查询。如果子查询不可避免,那么要在子查询中过滤掉尽可能多的行。
5.避免困难的正规表达式
MATCHES和LIKE关键字支持通配符匹配,技术上叫正规表达式。但这种匹配特别耗费时间。例如:SELECT * FROM customer WHERE zipcode LIKE “98_ _ _”
即使在zipcode字段上建立了索引,在这种情况下也还是采用顺序扫描的方式。如果把语句改为SELECT * FROM customer WHERE zipcode >“98000”,在执行查询时就会利用索引来查询,显然会大大提高速度。
另外,还要避免非开始的子串。例如语句:SELECT * FROM customer WHERE zipcode[2,3] >“80”,在where子句中采用了非开始子串,因而这个语句也不会使用索引。
6.使用临时表加速查询
把表的一个子集进行排序并创建临时表,有时能加速查询。它有助于避免多重排序操作,而且在其他方面还能简化优化器的工作。例如:
SELECT cust.name,rcvbles.balance,……other columns
FROM cust,rcvbles
WHERE cust.customer_id = rcvlbes.customer_id
AND rcvblls.balance>0
AND cust.postcode>“98000”
ORDER BY cust.name
如果这个查询要被执行多次而不止一次,可以把所有未付款的客户找出来放在一个临时文件中,并按客户的名字进行排序:
SELECT cust.name,rcvbles.balance,……other columns
FROM cust,rcvbles
WHERE cust.customer_id = rcvlbes.customer_id
AND rcvblls.balance>0
ORDER BY cust.name
INTO TEMP cust_with_balance
然后以下面的方式在临时表中查询:
SELECT * FROM cust_with_balance
WHERE postcode>“98000”
临时表中的行要比主表中的行少,而且物理顺序就是所要求的顺序,减少了磁盘I/O,所以查询工作量可以得到大幅减少。
注意:临时表创建后不会反映主表的修改。在主表中数据频繁修改的情况下,注意不要丢失数据。
数据库系统是管理信息系统的核心,基于数据库的联机事务处理(OLTP)以及联机分析处理(OLAP)是银行、企业、*等部门最为重要的计算机应用之一。从大多数系统的应用实例来看,查询操作在各种数据库操作中所占据的比重最大,而查询操作所基于的SELECT语句在SQL语句中又是代价最大的语句。举例来说,如果数据的量积累到一定的程度,比如一个银行的账户数据库表信息积累到上百万甚至上千万条记录,全表扫描一次往往需要数十分钟,甚至数小时。如果采用比全表扫描更好的查询策略,往往可以使查询时间降为几分钟,由此可见查询优化技术的重要性。
笔者在应用项目的实施中发现,许多程序员在利用一些前端数据库开发工具(如PowerBuilder、Delphi等)开发数据库应用程序时,只注重用户界面的华丽,并不重视查询语句的效率问题,导致所开发出来的应用系统效率低下,资源浪费严重。因此,如何设计高效合理的查询语句就显得非常重要。本文以应用实例为基础,结合数据库理论,介绍查询优化技术在现实系统中的运用。
分析问题
许多程序员认为查询优化是DBMS(数据库管理系统)的任务,与程序员所编写的SQL语句关系不大,这是错误的。一个好的查询计划往往可以使程序性能提高数十倍。查询计划是用户所提交的SQL语句的集合,查询规划是经过优化处理之后所产生的语句集合。DBMS处理查询计划的过程是这样的:在做完查询语句的词法、语法检查之后,将语句提交给DBMS的查询优化器,优化器做完代数优化和存取路径的优化之后,由预编译模块对语句进行处理并生成查询规划,然后在合适的时间提交给系统处理执行,最后将执行结果返回给用户。在实际的数据库产品(如Oracle、Sybase等)的高版本中都是采用基于代价的优化方法,这种优化能根据从系统字典表所得到的信息来估计不同的查询规划的代价,然后选择一个较优的规划。虽然现在的数据库产品在查询优化方面已经做得越来越好,但由用户提交的SQL语句是系统优化的基础,很难设想一个原本糟糕的查询计划经过系统的优化之后会变得高效,因此用户所写语句的优劣至关重要。系统所做查询优化我们暂不讨论,下面重点说明改善用户查询计划的解决方案。
解决问题
下面以关系数据库系统Informix为例,介绍改善用户查询计划的方法。
1.合理使用索引
索引是数据库中重要的数据结构,它的根本目的就是为了提高查询效率。现在大多数的数据库产品都采用IBM最先提出的ISAM索引结构。索引的使用要恰到好处,其使用原则如下:
●在经常进行连接,但是没有指定为外键的列上建立索引,而不经常连接的字段则由优化器自动生成索引。
●在频繁进行排序或分组(即进行group by或order by操作)的列上建立索引。
●在条件表达式中经常用到的不同值较多的列上建立检索,在不同值少的列上不要建立索引。比如在雇员表的“性别”列上只有“男”与“女”两个不同值,因此就无必要建立索引。如果建立索引不但不会提高查询效率,反而会严重降低更新速度。
●如果待排序的列有多个,可以在这些列上建立复合索引(compound index)。
●使用系统工具。如Informix数据库有一个tbcheck工具,可以在可疑的索引上进行检查。在一些数据库服务器上,索引可能失效或者因为频繁操作而使得读取效率降低,如果一个使用索引的查询不明不白地慢下来,可以试着用tbcheck工具检查索引的完整性,必要时进行修复。另外,当数据库表更新大量数据后,删除并重建索引可以提高查询速度。
2.避免或简化排序
应当简化或避免对大型表进行重复的排序。当能够利用索引自动以适当的次序产生输出时,优化器就避免了排序的步骤。以下是一些影响因素:
●索引中不包括一个或几个待排序的列;
●group by或order by子句中列的次序与索引的次序不一样;
●排序的列来自不同的表。
为了避免不必要的排序,就要正确地增建索引,合理地合并数据库表(尽管有时可能影响表的规范化,但相对于效率的提高是值得的)。如果排序不可避免,那么应当试图简化它,如缩小排序的列的范围等。
3.消除对大型表行数据的顺序存取
在嵌套查询中,对表的顺序存取对查询效率可能产生致命的影响。比如采用顺序存取策略,一个嵌套3层的查询,如果每层都查询1000行,那么这个查询就要查询10亿行数据。避免这种情况的主要方法就是对连接的列进行索引。例如,两个表:学生表(学号、姓名、年龄……)和选课表(学号、课程号、成绩)。如果两个表要做连接,就要在“学号”这个连接字段上建立索引。
还可以使用并集来避免顺序存取。尽管在所有的检查列上都有索引,但某些形式的where子句强迫优化器使用顺序存取。下面的查询将强迫对orders表执行顺序操作:
SELECT * FROM orders WHERE (customer_num=104 AND order_num>1001) OR order_num=1008
虽然在customer_num和order_num上建有索引,但是在上面的语句中优化器还是使用顺序存取路径扫描整个表。因为这个语句要检索的是分离的行的集合,所以应该改为如下语句:
SELECT * FROM orders WHERE customer_num=104 AND order_num>1001
UNION
SELECT * FROM orders WHERE order_num=1008
这样就能利用索引路径处理查询。
4.避免相关子查询
一个列的标签同时在主查询和where子句中的查询中出现,那么很可能当主查询中的列值改变之后,子查询必须重新查询一次。查询嵌套层次越多,效率越低,因此应当尽量避免子查询。如果子查询不可避免,那么要在子查询中过滤掉尽可能多的行。
5.避免困难的正规表达式
MATCHES和LIKE关键字支持通配符匹配,技术上叫正规表达式。但这种匹配特别耗费时间。例如:SELECT * FROM customer WHERE zipcode LIKE “98_ _ _”
即使在zipcode字段上建立了索引,在这种情况下也还是采用顺序扫描的方式。如果把语句改为SELECT * FROM customer WHERE zipcode >“98000”,在执行查询时就会利用索引来查询,显然会大大提高速度。
另外,还要避免非开始的子串。例如语句:SELECT * FROM customer WHERE zipcode[2,3] >“80”,在where子句中采用了非开始子串,因而这个语句也不会使用索引。
6.使用临时表加速查询
把表的一个子集进行排序并创建临时表,有时能加速查询。它有助于避免多重排序操作,而且在其他方面还能简化优化器的工作。例如:
SELECT cust.name,rcvbles.balance,……other columns
FROM cust,rcvbles
WHERE cust.customer_id = rcvlbes.customer_id
AND rcvblls.balance>0
AND cust.postcode>“98000”
ORDER BY cust.name
如果这个查询要被执行多次而不止一次,可以把所有未付款的客户找出来放在一个临时文件中,并按客户的名字进行排序:
SELECT cust.name,rcvbles.balance,……other columns
FROM cust,rcvbles
WHERE cust.customer_id = rcvlbes.customer_id
AND rcvblls.balance>0
ORDER BY cust.name
INTO TEMP cust_with_balance
然后以下面的方式在临时表中查询:
SELECT * FROM cust_with_balance
WHERE postcode>“98000”
临时表中的行要比主表中的行少,而且物理顺序就是所要求的顺序,减少了磁盘I/O,所以查询工作量可以得到大幅减少。
注意:临时表创建后不会反映主表的修改。在主表中数据频繁修改的情况下,注意不要丢失数据。
#18
数据库的查询优化技术
数据库系统是管理信息系统的核心,基于数据库的联机事务处理(OLTP)以及联机分析处理(OLAP)是银行、企业、*等部门最为重要的计算机应用之一。从大多数系统的应用实例来看,查询操作在各种数据库操作中所占据的比重最大,而查询操作所基于的SELECT语句在SQL语句中又是代价最大的语句。举例来说,如果数据的量积累到一定的程度,比如一个银行的账户数据库表信息积累到上百万甚至上千万条记录,全表扫描一次往往需要数十分钟,甚至数小时。如果采用比全表扫描更好的查询策略,往往可以使查询时间降为几分钟,由此可见查询优化技术的重要性。
笔者在应用项目的实施中发现,许多程序员在利用一些前端数据库开发工具(如PowerBuilder、Delphi等)开发数据库应用程序时,只注重用户界面的华丽,并不重视查询语句的效率问题,导致所开发出来的应用系统效率低下,资源浪费严重。因此,如何设计高效合理的查询语句就显得非常重要。本文以应用实例为基础,结合数据库理论,介绍查询优化技术在现实系统中的运用。
分析问题
许多程序员认为查询优化是DBMS(数据库管理系统)的任务,与程序员所编写的SQL语句关系不大,这是错误的。一个好的查询计划往往可以使程序性能提高数十倍。查询计划是用户所提交的SQL语句的集合,查询规划是经过优化处理之后所产生的语句集合。DBMS处理查询计划的过程是这样的:在做完查询语句的词法、语法检查之后,将语句提交给DBMS的查询优化器,优化器做完代数优化和存取路径的优化之后,由预编译模块对语句进行处理并生成查询规划,然后在合适的时间提交给系统处理执行,最后将执行结果返回给用户。在实际的数据库产品(如Oracle、Sybase等)的高版本中都是采用基于代价的优化方法,这种优化能根据从系统字典表所得到的信息来估计不同的查询规划的代价,然后选择一个较优的规划。虽然现在的数据库产品在查询优化方面已经做得越来越好,但由用户提交的SQL语句是系统优化的基础,很难设想一个原本糟糕的查询计划经过系统的优化之后会变得高效,因此用户所写语句的优劣至关重要。系统所做查询优化我们暂不讨论,下面重点说明改善用户查询计划的解决方案。
解决问题
下面以关系数据库系统Informix为例,介绍改善用户查询计划的方法。
1.合理使用索引
索引是数据库中重要的数据结构,它的根本目的就是为了提高查询效率。现在大多数的数据库产品都采用IBM最先提出的ISAM索引结构。索引的使用要恰到好处,其使用原则如下:
●在经常进行连接,但是没有指定为外键的列上建立索引,而不经常连接的字段则由优化器自动生成索引。
●在频繁进行排序或分组(即进行group by或order by操作)的列上建立索引。
●在条件表达式中经常用到的不同值较多的列上建立检索,在不同值少的列上不要建立索引。比如在雇员表的“性别”列上只有“男”与“女”两个不同值,因此就无必要建立索引。如果建立索引不但不会提高查询效率,反而会严重降低更新速度。
●如果待排序的列有多个,可以在这些列上建立复合索引(compound index)。
●使用系统工具。如Informix数据库有一个tbcheck工具,可以在可疑的索引上进行检查。在一些数据库服务器上,索引可能失效或者因为频繁操作而使得读取效率降低,如果一个使用索引的查询不明不白地慢下来,可以试着用tbcheck工具检查索引的完整性,必要时进行修复。另外,当数据库表更新大量数据后,删除并重建索引可以提高查询速度。
2.避免或简化排序
应当简化或避免对大型表进行重复的排序。当能够利用索引自动以适当的次序产生输出时,优化器就避免了排序的步骤。以下是一些影响因素:
●索引中不包括一个或几个待排序的列;
●group by或order by子句中列的次序与索引的次序不一样;
●排序的列来自不同的表。
为了避免不必要的排序,就要正确地增建索引,合理地合并数据库表(尽管有时可能影响表的规范化,但相对于效率的提高是值得的)。如果排序不可避免,那么应当试图简化它,如缩小排序的列的范围等。
3.消除对大型表行数据的顺序存取
在嵌套查询中,对表的顺序存取对查询效率可能产生致命的影响。比如采用顺序存取策略,一个嵌套3层的查询,如果每层都查询1000行,那么这个查询就要查询10亿行数据。避免这种情况的主要方法就是对连接的列进行索引。例如,两个表:学生表(学号、姓名、年龄……)和选课表(学号、课程号、成绩)。如果两个表要做连接,就要在“学号”这个连接字段上建立索引。
还可以使用并集来避免顺序存取。尽管在所有的检查列上都有索引,但某些形式的where子句强迫优化器使用顺序存取。下面的查询将强迫对orders表执行顺序操作:
SELECT * FROM orders WHERE (customer_num=104 AND order_num>1001) OR order_num=1008
虽然在customer_num和order_num上建有索引,但是在上面的语句中优化器还是使用顺序存取路径扫描整个表。因为这个语句要检索的是分离的行的集合,所以应该改为如下语句:
SELECT * FROM orders WHERE customer_num=104 AND order_num>1001
UNION
SELECT * FROM orders WHERE order_num=1008
这样就能利用索引路径处理查询。
4.避免相关子查询
一个列的标签同时在主查询和where子句中的查询中出现,那么很可能当主查询中的列值改变之后,子查询必须重新查询一次。查询嵌套层次越多,效率越低,因此应当尽量避免子查询。如果子查询不可避免,那么要在子查询中过滤掉尽可能多的行。
5.避免困难的正规表达式
MATCHES和LIKE关键字支持通配符匹配,技术上叫正规表达式。但这种匹配特别耗费时间。例如:SELECT * FROM customer WHERE zipcode LIKE “98_ _ _”
即使在zipcode字段上建立了索引,在这种情况下也还是采用顺序扫描的方式。如果把语句改为SELECT * FROM customer WHERE zipcode >“98000”,在执行查询时就会利用索引来查询,显然会大大提高速度。
另外,还要避免非开始的子串。例如语句:SELECT * FROM customer WHERE zipcode[2,3] >“80”,在where子句中采用了非开始子串,因而这个语句也不会使用索引。
6.使用临时表加速查询
把表的一个子集进行排序并创建临时表,有时能加速查询。它有助于避免多重排序操作,而且在其他方面还能简化优化器的工作。例如:
SELECT cust.name,rcvbles.balance,……other columns
FROM cust,rcvbles
WHERE cust.customer_id = rcvlbes.customer_id
AND rcvblls.balance>0
AND cust.postcode>“98000”
ORDER BY cust.name
如果这个查询要被执行多次而不止一次,可以把所有未付款的客户找出来放在一个临时文件中,并按客户的名字进行排序:
SELECT cust.name,rcvbles.balance,……other columns
FROM cust,rcvbles
WHERE cust.customer_id = rcvlbes.customer_id
AND rcvblls.balance>0
ORDER BY cust.name
INTO TEMP cust_with_balance
然后以下面的方式在临时表中查询:
SELECT * FROM cust_with_balance
WHERE postcode>“98000”
临时表中的行要比主表中的行少,而且物理顺序就是所要求的顺序,减少了磁盘I/O,所以查询工作量可以得到大幅减少。
注意:临时表创建后不会反映主表的修改。在主表中数据频繁修改的情况下,注意不要丢失数据。
数据库系统是管理信息系统的核心,基于数据库的联机事务处理(OLTP)以及联机分析处理(OLAP)是银行、企业、*等部门最为重要的计算机应用之一。从大多数系统的应用实例来看,查询操作在各种数据库操作中所占据的比重最大,而查询操作所基于的SELECT语句在SQL语句中又是代价最大的语句。举例来说,如果数据的量积累到一定的程度,比如一个银行的账户数据库表信息积累到上百万甚至上千万条记录,全表扫描一次往往需要数十分钟,甚至数小时。如果采用比全表扫描更好的查询策略,往往可以使查询时间降为几分钟,由此可见查询优化技术的重要性。
笔者在应用项目的实施中发现,许多程序员在利用一些前端数据库开发工具(如PowerBuilder、Delphi等)开发数据库应用程序时,只注重用户界面的华丽,并不重视查询语句的效率问题,导致所开发出来的应用系统效率低下,资源浪费严重。因此,如何设计高效合理的查询语句就显得非常重要。本文以应用实例为基础,结合数据库理论,介绍查询优化技术在现实系统中的运用。
分析问题
许多程序员认为查询优化是DBMS(数据库管理系统)的任务,与程序员所编写的SQL语句关系不大,这是错误的。一个好的查询计划往往可以使程序性能提高数十倍。查询计划是用户所提交的SQL语句的集合,查询规划是经过优化处理之后所产生的语句集合。DBMS处理查询计划的过程是这样的:在做完查询语句的词法、语法检查之后,将语句提交给DBMS的查询优化器,优化器做完代数优化和存取路径的优化之后,由预编译模块对语句进行处理并生成查询规划,然后在合适的时间提交给系统处理执行,最后将执行结果返回给用户。在实际的数据库产品(如Oracle、Sybase等)的高版本中都是采用基于代价的优化方法,这种优化能根据从系统字典表所得到的信息来估计不同的查询规划的代价,然后选择一个较优的规划。虽然现在的数据库产品在查询优化方面已经做得越来越好,但由用户提交的SQL语句是系统优化的基础,很难设想一个原本糟糕的查询计划经过系统的优化之后会变得高效,因此用户所写语句的优劣至关重要。系统所做查询优化我们暂不讨论,下面重点说明改善用户查询计划的解决方案。
解决问题
下面以关系数据库系统Informix为例,介绍改善用户查询计划的方法。
1.合理使用索引
索引是数据库中重要的数据结构,它的根本目的就是为了提高查询效率。现在大多数的数据库产品都采用IBM最先提出的ISAM索引结构。索引的使用要恰到好处,其使用原则如下:
●在经常进行连接,但是没有指定为外键的列上建立索引,而不经常连接的字段则由优化器自动生成索引。
●在频繁进行排序或分组(即进行group by或order by操作)的列上建立索引。
●在条件表达式中经常用到的不同值较多的列上建立检索,在不同值少的列上不要建立索引。比如在雇员表的“性别”列上只有“男”与“女”两个不同值,因此就无必要建立索引。如果建立索引不但不会提高查询效率,反而会严重降低更新速度。
●如果待排序的列有多个,可以在这些列上建立复合索引(compound index)。
●使用系统工具。如Informix数据库有一个tbcheck工具,可以在可疑的索引上进行检查。在一些数据库服务器上,索引可能失效或者因为频繁操作而使得读取效率降低,如果一个使用索引的查询不明不白地慢下来,可以试着用tbcheck工具检查索引的完整性,必要时进行修复。另外,当数据库表更新大量数据后,删除并重建索引可以提高查询速度。
2.避免或简化排序
应当简化或避免对大型表进行重复的排序。当能够利用索引自动以适当的次序产生输出时,优化器就避免了排序的步骤。以下是一些影响因素:
●索引中不包括一个或几个待排序的列;
●group by或order by子句中列的次序与索引的次序不一样;
●排序的列来自不同的表。
为了避免不必要的排序,就要正确地增建索引,合理地合并数据库表(尽管有时可能影响表的规范化,但相对于效率的提高是值得的)。如果排序不可避免,那么应当试图简化它,如缩小排序的列的范围等。
3.消除对大型表行数据的顺序存取
在嵌套查询中,对表的顺序存取对查询效率可能产生致命的影响。比如采用顺序存取策略,一个嵌套3层的查询,如果每层都查询1000行,那么这个查询就要查询10亿行数据。避免这种情况的主要方法就是对连接的列进行索引。例如,两个表:学生表(学号、姓名、年龄……)和选课表(学号、课程号、成绩)。如果两个表要做连接,就要在“学号”这个连接字段上建立索引。
还可以使用并集来避免顺序存取。尽管在所有的检查列上都有索引,但某些形式的where子句强迫优化器使用顺序存取。下面的查询将强迫对orders表执行顺序操作:
SELECT * FROM orders WHERE (customer_num=104 AND order_num>1001) OR order_num=1008
虽然在customer_num和order_num上建有索引,但是在上面的语句中优化器还是使用顺序存取路径扫描整个表。因为这个语句要检索的是分离的行的集合,所以应该改为如下语句:
SELECT * FROM orders WHERE customer_num=104 AND order_num>1001
UNION
SELECT * FROM orders WHERE order_num=1008
这样就能利用索引路径处理查询。
4.避免相关子查询
一个列的标签同时在主查询和where子句中的查询中出现,那么很可能当主查询中的列值改变之后,子查询必须重新查询一次。查询嵌套层次越多,效率越低,因此应当尽量避免子查询。如果子查询不可避免,那么要在子查询中过滤掉尽可能多的行。
5.避免困难的正规表达式
MATCHES和LIKE关键字支持通配符匹配,技术上叫正规表达式。但这种匹配特别耗费时间。例如:SELECT * FROM customer WHERE zipcode LIKE “98_ _ _”
即使在zipcode字段上建立了索引,在这种情况下也还是采用顺序扫描的方式。如果把语句改为SELECT * FROM customer WHERE zipcode >“98000”,在执行查询时就会利用索引来查询,显然会大大提高速度。
另外,还要避免非开始的子串。例如语句:SELECT * FROM customer WHERE zipcode[2,3] >“80”,在where子句中采用了非开始子串,因而这个语句也不会使用索引。
6.使用临时表加速查询
把表的一个子集进行排序并创建临时表,有时能加速查询。它有助于避免多重排序操作,而且在其他方面还能简化优化器的工作。例如:
SELECT cust.name,rcvbles.balance,……other columns
FROM cust,rcvbles
WHERE cust.customer_id = rcvlbes.customer_id
AND rcvblls.balance>0
AND cust.postcode>“98000”
ORDER BY cust.name
如果这个查询要被执行多次而不止一次,可以把所有未付款的客户找出来放在一个临时文件中,并按客户的名字进行排序:
SELECT cust.name,rcvbles.balance,……other columns
FROM cust,rcvbles
WHERE cust.customer_id = rcvlbes.customer_id
AND rcvblls.balance>0
ORDER BY cust.name
INTO TEMP cust_with_balance
然后以下面的方式在临时表中查询:
SELECT * FROM cust_with_balance
WHERE postcode>“98000”
临时表中的行要比主表中的行少,而且物理顺序就是所要求的顺序,减少了磁盘I/O,所以查询工作量可以得到大幅减少。
注意:临时表创建后不会反映主表的修改。在主表中数据频繁修改的情况下,注意不要丢失数据。
#19
7.用排序来取代非顺序存取
非顺序磁盘存取是最慢的操作,表现在磁盘存取臂的来回移动。SQL语句隐藏了这一情况,使得我们在写应用程序时很容易写出要求存取大量非顺序页的查询。
有些时候,用数据库的排序能力来替代非顺序的存取能改进查询。
实例分析
下面我们举一个制造公司的例子来说明如何进行查询优化。制造公司数据库中包括3个表,模式如下所示:
1.part表
零件号零件描述其他列
(part_num)(part_desc)(other column)
102,032Seageat 30G disk……
500,049Novel 10M network card……
……
2.vendor表
厂商号厂商名其他列
(vendor _num)(vendor_name) (other column)
910,257Seageat Corp……
523,045IBM Corp……
……
3.parven表
零件号厂商号零件数量
(part_num)(vendor_num)(part_amount)
102,032910,2573,450,000
234,423321,0014,000,000
……
下面的查询将在这些表上定期运行,并产生关于所有零件数量的报表:
SELECT part_desc,vendor_name,part_amount
FROM part,vendor,parven
WHERE part.part_num=parven.part_num
AND parven.vendor_num = vendor.vendor_num
ORDER BY part.part_num
如果不建立索引,上述查询代码的开销将十分巨大。为此,我们在零件号和厂商号上建立索引。索引的建立避免了在嵌套中反复扫描。关于表与索引的统计信息如下:
表行尺寸行数量每页行数量数据页数量
(table)(row size)(Row count)(Rows/Pages)(Data Pages)
part15010,00025400
Vendor1501,000 2540
Parven13 15,000300 50
索引键尺寸每页键数量页面数量
(Indexes)(Key Size)(Keys/Page)(Leaf Pages)
part450020
Vendor45002
Parven825060
看起来是个相对简单的3表连接,但是其查询开销是很大的。通过查看系统表可以看到,在part_num上和vendor_num上有簇索引,因此索引是按照物理顺序存放的。parven表没有特定的存放次序。这些表的大小说明从缓冲页中非顺序存取的成功率很小。此语句的优化查询规划是:首先从part中顺序读取400页,然后再对parven表非顺序存取1万次,每次2页(一个索引页、一个数据页),总计2万个磁盘页,最后对vendor表非顺序存取1.5万次,合3万个磁盘页。可以看出在这个索引好的连接上花费的磁盘存取为5.04万次。
实际上,我们可以通过使用临时表分3个步骤来提高查询效率:
1.从parven表中按vendor_num的次序读数据:
SELECT part_num,vendor_num,price
FROM parven
ORDER BY vendor_num
INTO temp pv_by_vn
这个语句顺序读parven(50页),写一个临时表(50页),并排序。假定排序的开销为200页,总共是300页。
2.把临时表和vendor表连接,把结果输出到一个临时表,并按part_num排序:
SELECT pv_by_vn,* vendor.vendor_num
FROM pv_by_vn,vendor
WHERE pv_by_vn.vendor_num=vendor.vendor_num
ORDER BY pv_by_vn.part_num
INTO TMP pvvn_by_pn
DROP TABLE pv_by_vn
这个查询读取pv_by_vn(50页),它通过索引存取vendor表1.5万次,但由于按vendor_num次序排列,实际上只是通过索引顺序地读vendor表(40+2=42页),输出的表每页约95行,共160页。写并存取这些页引发5*160=800次的读写,索引共读写892页。
3.把输出和part连接得到最后的结果:
SELECT pvvn_by_pn.*,part.part_desc
FROM pvvn_by_pn,part
WHERE pvvn_by_pn.part_num=part.part_num
DROP TABLE pvvn_by_pn
这样,查询顺序地读pvvn_by_pn(160页),通过索引读part表1.5万次,由于建有索引,所以实际上进行1772次磁盘读写,优化比例为30∶1。笔者在Informix Dynamic Sever上做同样的实验,发现在时间耗费上的优化比例为5∶1(如果增加数据量,比例可能会更大)。
小结
20%的代码用去了80%的时间,这是程序设计中的一个著名定律,在数据库应用程序中也同样如此。我们的优化要抓住关键问题,对于数据库应用程序来说,重点在于SQL的执行效率。查询优化的重点环节是使得数据库服务器少从磁盘中读数据以及顺序读页而不是非顺序读页。
非顺序磁盘存取是最慢的操作,表现在磁盘存取臂的来回移动。SQL语句隐藏了这一情况,使得我们在写应用程序时很容易写出要求存取大量非顺序页的查询。
有些时候,用数据库的排序能力来替代非顺序的存取能改进查询。
实例分析
下面我们举一个制造公司的例子来说明如何进行查询优化。制造公司数据库中包括3个表,模式如下所示:
1.part表
零件号零件描述其他列
(part_num)(part_desc)(other column)
102,032Seageat 30G disk……
500,049Novel 10M network card……
……
2.vendor表
厂商号厂商名其他列
(vendor _num)(vendor_name) (other column)
910,257Seageat Corp……
523,045IBM Corp……
……
3.parven表
零件号厂商号零件数量
(part_num)(vendor_num)(part_amount)
102,032910,2573,450,000
234,423321,0014,000,000
……
下面的查询将在这些表上定期运行,并产生关于所有零件数量的报表:
SELECT part_desc,vendor_name,part_amount
FROM part,vendor,parven
WHERE part.part_num=parven.part_num
AND parven.vendor_num = vendor.vendor_num
ORDER BY part.part_num
如果不建立索引,上述查询代码的开销将十分巨大。为此,我们在零件号和厂商号上建立索引。索引的建立避免了在嵌套中反复扫描。关于表与索引的统计信息如下:
表行尺寸行数量每页行数量数据页数量
(table)(row size)(Row count)(Rows/Pages)(Data Pages)
part15010,00025400
Vendor1501,000 2540
Parven13 15,000300 50
索引键尺寸每页键数量页面数量
(Indexes)(Key Size)(Keys/Page)(Leaf Pages)
part450020
Vendor45002
Parven825060
看起来是个相对简单的3表连接,但是其查询开销是很大的。通过查看系统表可以看到,在part_num上和vendor_num上有簇索引,因此索引是按照物理顺序存放的。parven表没有特定的存放次序。这些表的大小说明从缓冲页中非顺序存取的成功率很小。此语句的优化查询规划是:首先从part中顺序读取400页,然后再对parven表非顺序存取1万次,每次2页(一个索引页、一个数据页),总计2万个磁盘页,最后对vendor表非顺序存取1.5万次,合3万个磁盘页。可以看出在这个索引好的连接上花费的磁盘存取为5.04万次。
实际上,我们可以通过使用临时表分3个步骤来提高查询效率:
1.从parven表中按vendor_num的次序读数据:
SELECT part_num,vendor_num,price
FROM parven
ORDER BY vendor_num
INTO temp pv_by_vn
这个语句顺序读parven(50页),写一个临时表(50页),并排序。假定排序的开销为200页,总共是300页。
2.把临时表和vendor表连接,把结果输出到一个临时表,并按part_num排序:
SELECT pv_by_vn,* vendor.vendor_num
FROM pv_by_vn,vendor
WHERE pv_by_vn.vendor_num=vendor.vendor_num
ORDER BY pv_by_vn.part_num
INTO TMP pvvn_by_pn
DROP TABLE pv_by_vn
这个查询读取pv_by_vn(50页),它通过索引存取vendor表1.5万次,但由于按vendor_num次序排列,实际上只是通过索引顺序地读vendor表(40+2=42页),输出的表每页约95行,共160页。写并存取这些页引发5*160=800次的读写,索引共读写892页。
3.把输出和part连接得到最后的结果:
SELECT pvvn_by_pn.*,part.part_desc
FROM pvvn_by_pn,part
WHERE pvvn_by_pn.part_num=part.part_num
DROP TABLE pvvn_by_pn
这样,查询顺序地读pvvn_by_pn(160页),通过索引读part表1.5万次,由于建有索引,所以实际上进行1772次磁盘读写,优化比例为30∶1。笔者在Informix Dynamic Sever上做同样的实验,发现在时间耗费上的优化比例为5∶1(如果增加数据量,比例可能会更大)。
小结
20%的代码用去了80%的时间,这是程序设计中的一个著名定律,在数据库应用程序中也同样如此。我们的优化要抓住关键问题,对于数据库应用程序来说,重点在于SQL的执行效率。查询优化的重点环节是使得数据库服务器少从磁盘中读数据以及顺序读页而不是非顺序读页。
#20
解决了,多谢,是一个表没有建索引,谢谢,查了我好久,给分