SQL SERVER中一些常见性能问题的总结
SQL SERVER中一些常见性能问题的总结
作者:pbsql(风云)
日期:2005-11-30
1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。
2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num is null
可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:
select id from t where num=0
3.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。
4.应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num=10 or num=20
可以这样查询:
select id from t where num=10
union all
select id from t where num=20
5.in 和 not in 也要慎用,否则会导致全表扫描,如:
select id from t where num in(1,2,3)
对于连续的数值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3
6.下面的查询也将导致全表扫描:
select id from t where name like '%abc%'
若要提高效率,可以考虑全文检索。
7.如果在 where 子句中使用参数,也会导致全表扫描。因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:
select id from t where num=@num
可以改为强制查询使用索引:
select id from t with(index(索引名)) where num=@num
8.应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where num/2=100
应改为:
select id from t where num=100*2
9.应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where substring(name,1,3)='abc'--name以abc开头的id
select id from t where datediff(day,createdate,'2005-11-30')=0--‘2005-11-30’生成的id
应改为:
select id from t where name like 'abc%'
select id from t where createdate>='2005-11-30' and createdate<'2005-12-1'
10.不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
11.在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。
12.不要写一些没有意义的查询,如需要生成一个空表结构:
select col1,col2 into #t from t where 1=0
这类代码不会返回任何结果集,但是会消耗系统资源的,应改成这样:
create table #t(...)
13.很多时候用 exists 代替 in 是一个好的选择:
select num from a where num in(select num from b)
用下面的语句替换:
select num from a where exists(select 1 from b where num=a.num)
14.并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,如一表中有字段sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。
15.索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。
16.应尽可能的避免更新 clustered 索引数据列,因为 clustered 索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源。若应用系统需要频繁更新 clustered 索引数据列,那么需要考虑是否应将该索引建为 clustered 索引。
17.尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。
18.尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。
19.任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。
20.尽量使用表变量来代替临时表。如果表变量包含大量数据,请注意索引非常有限(只有主键索引)。
21.避免频繁创建和删除临时表,以减少系统表资源的消耗。
22.临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件,最好使用导出表。
23.在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。
24.如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。
25.尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。
26.使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。
27.与临时表一样,游标并不是不可使用。对小型数据集使用 FAST_FORWARD 游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。
28.在所有的存储过程和触发器的开始处设置 SET NOCOUNT ON ,在结束时设置 SET NOCOUNT OFF 。无需在执行存储过程和触发器的每个语句后向客户端发送 DONE_IN_PROC 消息。
29.尽量避免大事务操作,提高系统并发能力。
30.尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。
具体的SQL语句在很多情况下需要结合实际的应用情况来写,这里不作叙述。
问题点数:200、回复次数:188Top
1 楼pbsql(风云)回复于 2005-11-30 15:18:25 得分 0
希望大家多提意见,并作相应补充
^_^Top
2 楼zhangjian01361(★非也非也★)回复于 2005-11-30 15:20:00 得分 2
先来学习一下,楼主能开源共享,值得学习呀!Top
3 楼aw511(点点星灯)回复于 2005-11-30 15:21:03 得分 2
:)Top
4 楼libin_ftsafe(子陌红尘:TS for Banking Card)回复于 2005-11-30 15:26:14 得分 2
UPTop
5 楼zlp321002(Life Is Good,Let's Shine)回复于 2005-11-30 15:27:28 得分 2
先顶,然后看....Top
6 楼zhanwei(@_@,初学.Net)回复于 2005-11-30 15:31:37 得分 2
接分,并收藏之Top
7 楼zhanwei(@_@,初学.Net)回复于 2005-11-30 15:35:09 得分 2
(一)挂起操作
在安装Sql或sp补丁的时候系统提示之前有挂起的安装操作,要求重启,这里往往重启无用,解决办法:
到HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager
删除PendingFileRenameOperations
(二)收缩数据库
--重建索引
DBCC REINDEX
DBCC INDEXDEFRAG
--收缩数据和日志
DBCC SHRINKDB
DBCC SHRINKFILE
(三)压缩数据库
dbcc shrinkdatabase(dbname)
(四)转移数据库给新用户以已存在用户权限
exec sp_change_users_login 'update_one','newname','oldname'
go
(五)检查备份集
RESTORE VERIFYONLY from disk='E:\dvbbs.bak'
(六)修复数据库
ALTER DATABASE [dvbbs] SET SINGLE_USER
GO
DBCC CHECKDB('dvbbs',repair_allow_data_loss) WITH TABLOCK
GO
ALTER DATABASE [dvbbs] SET MULTI_USER
GO
--CHECKDB 有3个参数:
--REPAIR_ALLOW_DATA_LOSS
-- 执行由 REPAIR_REBUILD 完成的所有修复,包括对行和页进行分配和取消分配以改正分配错误、结构行或页的错误,以及删除已损坏的文本对象。这些修复可能会导致一些数据丢失。修复操作可以在用户事务下完成以允许用户回滚所做的更改。如果回滚修复,则数据库仍会含有错误,应该从备份进行恢复。如果由于所提供修复等级的缘故遗漏某个错误的修复,则将遗漏任何取决于该修复的修复。修复完成后,备份数据库。
--REPAIR_FAST 进行小的、不耗时的修复操作,如修复非聚集索引中的附加键。这些修复可以很快完成,并且不会有丢失数据的危险。
--REPAIR_REBUILD 执行由 REPAIR_FAST 完成的所有修复,包括需要较长时间的修复(如重建索引)。执行这些修复时不会有丢失数据的危险。
--DBCC CHECKDB('dvbbs') with NO_INFOMSGS,PHYSICAL_ONLY
SQL SERVER日志清除的两种方法
在使用过程中大家经常碰到数据库日志非常大的情况,在这里介绍了两种处理方法……
方法一
一般情况下,SQL数据库的收缩并不能很大程度上减小数据库大小,其主要作用是收缩日志大小,应当定期进行此操作以免数据库日志过大
1、设置数据库模式为简单模式:打开SQL企业管理器,在控制台根目录中依次点开Microsoft SQL Server-->SQL Server组-->双击打开你的服务器-->双击打开数据库目录-->选择你的数据库名称(如论坛数据库Forum)-->然后点击右键选择属性-->选择选项-->在故障还原的模式中选择“简单”,然后按确定保存。
2、在当前数据库上点右键,看所有任务中的收缩数据库,一般里面的默认设置不用调整,直接点确定。
3、收缩数据库完成后,建议将您的数据库属性重新设置为标准模式,操作方法同第一点,因为日志在一些异常情况下往往是恢复数据库的重要依据
方法二
SET NOCOUNT ON
DECLARE @LogicalFileName sysname,
@MaxMinutes INT,
@NewSize INT
USE tablename -- 要操作的数据库名
SELECT @LogicalFileName = 'tablename_log', -- 日志文件名
@MaxMinutes = 10, -- Limit on time allowed to wrap log.
@NewSize = 1 -- 你想设定的日志文件的大小(M)
-- Setup / initialize
DECLARE @OriginalSize int
SELECT @OriginalSize = size
FROM sysfiles
WHERE name = @LogicalFileName
SELECT 'Original Size of ' + db_name() + ' LOG is ' +
CONVERT(VARCHAR(30),@OriginalSize) + ' 8K pages or ' +
CONVERT(VARCHAR(30),(@OriginalSize*8/1024)) + 'MB'
FROM sysfiles
WHERE name = @LogicalFileName
CREATE TABLE DummyTrans
(DummyColumn char (8000) not null)
DECLARE @Counter INT,
@StartTime DATETIME,
@TruncLog VARCHAR(255)
SELECT @StartTime = GETDATE(),
@TruncLog = 'BACKUP LOG ' + db_name() + ' WITH TRUNCATE_ONLY'
DBCC SHRINKFILE (@LogicalFileName, @NewSize)
EXEC (@TruncLog)
-- Wrap the log if necessary.
WHILE @MaxMinutes > DATEDIFF (mi, @StartTime, GETDATE()) -- time has not expired
AND @OriginalSize = (SELECT size FROM sysfiles WHERE name = @LogicalFileName)
AND (@OriginalSize * 8 /1024) > @NewSize
BEGIN -- Outer loop.
SELECT @Counter = 0
WHILE ((@Counter < @OriginalSize / 16) AND (@Counter < 50000))
BEGIN -- update
INSERT DummyTrans VALUES ('Fill Log')
DELETE DummyTrans
SELECT @Counter = @Counter + 1
END
EXEC (@TruncLog)
END
SELECT 'Final Size of ' + db_name() + ' LOG is ' +
CONVERT(VARCHAR(30),size) + ' 8K pages or ' +
CONVERT(VARCHAR(30),(size*8/1024)) + 'MB'
FROM sysfiles
WHERE name = @LogicalFileName
DROP TABLE DummyTrans
SET NOCOUNT OFF
Top
8 楼lovcal(枫兮)回复于 2005-11-30 15:55:06 得分 2
Good idea……好好总结总结!Top
9 楼yinwun(晴)回复于 2005-11-30 16:45:33 得分 2
好贴收藏。谢谢Top
10 楼jaury(冰飞)回复于 2005-11-30 16:46:20 得分 2
好东西啊Top
11 楼wfliu()回复于 2005-11-30 17:05:06 得分 2
谢谢,收藏Top
12 楼zoubsky(与世隔绝的天空)回复于 2005-11-30 17:22:48 得分 2
UP
Top
13 楼lxzm1001(*~悠悠蓝星梦~*)回复于 2005-11-30 17:24:52 得分 2
好贴Top
14 楼liuxin001(心动就要行动)回复于 2005-11-30 17:41:15 得分 2
好东西,学习
UPTop
15 楼jin2005(小白)回复于 2005-12-01 09:19:08 得分 2
mark..
好贴Top
16 楼singlepine(小山)回复于 2005-12-01 09:25:53 得分 2
不错,支持Top
17 楼sxycgxj(云中客)回复于 2005-12-01 09:53:14 得分 2
好东西,谢谢楼主与大家分享Top
18 楼sxycgxj(云中客)回复于 2005-12-01 09:56:50 得分 2
select id from t where num=10
union all
select id from t where num=20
真得比
select id from t where num=10 or num=20
的效率高吗Top
19 楼rockyljt(江濤)回复于 2005-12-01 09:58:05 得分 2
好Top
20 楼yangbo88(稳步前进)回复于 2005-12-01 10:01:09 得分 2
向楼主学习Top
21 楼pbsql(风云)回复于 2005-12-01 10:15:45 得分 0
select id from t where num=10
union all
select id from t where num=20
真得比
select id from t where num=10 or num=20
的效率高吗
------------------------------------------------
当num上建有索引时,前者可以利用索引查询,而后者不能,只能全表扫描,所以前者效率高
或者这样解释:
从数据库访问的角度看,含有不连续连接词(OR和IN)的WHERE子句一般来说性能不会太好。所以,优化器可能会采用R策略,这种策略会生成1个工作表,其中含有每个可能匹配的执行的标识符,优化器把这些行标志符(页号和行号)看做是指向1个表中匹配的行的“动态索引”。优化器只需扫描工作表,取出每一个行标志符,再从数据表中取得相应的行,所以R策略的代价是生成工作表。Top
22 楼leezo(新丁)回复于 2005-12-01 13:07:37 得分 2
多谢提供Top
23 楼hlq8210(影子)回复于 2005-12-01 15:54:26 得分 2
多谢拉Top
24 楼prcgolf(小鸟)回复于 2005-12-01 16:51:12 得分 2
支持!!!Top
25 楼lsqkeke(可可)回复于 2005-12-01 17:02:43 得分 2
这个帖好啊!
谢谢楼主Top
26 楼wangdehao(找找找(现在很幸福))回复于 2005-12-01 17:32:13 得分 2
只有往死里顶了.......Top
27 楼killxtt(我菜我菜我菜菜菜)回复于 2005-12-01 18:08:54 得分 1
接分。学习Top
28 楼snakebite2008(3DFX)回复于 2005-12-01 21:37:51 得分 1
收藏学习。Top
29 楼chtfyy()回复于 2005-12-01 22:03:57 得分 1
markTop
30 楼s_ants520()回复于 2005-12-01 22:07:19 得分 1
好东东!Top
31 楼javanow(骄子数据库技术网)回复于 2005-12-01 22:11:23 得分 1
in 和 exists 对 sql server 来说是一样的。Top
32 楼aohan(aohan)回复于 2005-12-01 22:21:25 得分 1
in 和 exists 对 sql server 来说是一样的。
不同意
exists比in 优化Top
33 楼javanow(骄子数据库技术网)回复于 2005-12-01 22:43:18 得分 1
sql server 是基于COST的优化模式。
你可以写一条语句,在sql srv 2k 分别用in ,exists.您可以看出,执行计划是完全一样的。
在 oracle rbo优化器模式下,exists 和 in 才是有区别的。Top
34 楼mislrb(上班看看早报,上上CSDN,下班看看电影)回复于 2005-12-01 23:04:40 得分 1
markTop
35 楼ly2005(xiao luo)回复于 2005-12-01 23:05:48 得分 1
up
收藏!
Top
36 楼zlj113(·米老鼠· 学习)回复于 2005-12-01 23:07:00 得分 1
呵呵,难得来技术区的,
UP,收藏~~~~~~~~Top
37 楼wfliu()回复于 2005-12-01 23:32:20 得分 1
收藏Top
38 楼YangYuWeb(飘邈...)回复于 2005-12-02 15:04:12 得分 1
UP,收藏Top
39 楼niulijiang(自由之牛)回复于 2005-12-02 15:13:45 得分 1
好东西啊。。。收藏了Top
40 楼wangtiecheng(不知不为过,不学就是错!)回复于 2005-12-02 15:22:34 得分 1
好东东!!!
收藏!!!Top
41 楼hdhai9451(☆新人类☆)回复于 2005-12-02 15:28:31 得分 1
to:pbsql (风云)
你遇到數據庫用2年了,數據時候正常,有時候運行很慢的現象嗎?
你什麼處理這個問題?
Top
42 楼hdhai9451(☆新人类☆)回复于 2005-12-02 15:31:05 得分 1
不重新啟動計算,在控制面版裡的“服務”停止啟動MSSQLSERVER後,再啟動它,與重新啟動計算有什麼區別?
還有在狀態條上停止啟動圖標退出啟動後,再啟動它,與重新啟動計算有什麼區別?
Top
43 楼nononono(null,null)回复于 2005-12-02 15:39:06 得分 1
不准确,应该逐条检验.Top
44 楼lichangzai(搞没搞定)回复于 2005-12-02 15:46:18 得分 1
upTop
45 楼pbsql(风云)回复于 2005-12-02 16:34:51 得分 0
新新人类:
你遇到數據庫用2年了,數據時候正常,有時候運行很慢的現象嗎?你什麼處理這個問題?
-------------------------
这与当时的具体运行环境有关(内存的使用、其它程序的影响...)
不重新啟動計算,在控制面版裡的“服務”停止啟動MSSQLSERVER後,再啟動它,與重新啟動計算有什麼區別?
-------------------------
对于MSSQL来说应该是一样的,都是重新启动了MSSQL,但对操作系统来说就不一样了,很多时候MSSQL是受当时的操作系统运行状况影响的
還有在狀態條上停止啟動圖標退出啟動後,再啟動它,與重新啟動計算有什麼區別?
-------------------------
状态条上的图标退出不等于停止,且只能管理那里面的几项服务,而重启计算机是把操作系统的所有服务都重启了(如果设置了重启的话)Top
46 楼pbsql(风云)回复于 2005-12-02 16:35:39 得分 0
空空:请举例,以便修改,谢谢Top
47 楼meilian01(meilian)回复于 2005-12-02 16:48:34 得分 1
收藏
谢谢楼主Top
48 楼ls_jingwen(靜雯)回复于 2005-12-02 16:59:35 得分 1
學習啊Top
49 楼NNPeople(春春)回复于 2005-12-02 17:03:33 得分 1
好东西
谢谢
搂主Top
50 楼lichangzai(搞没搞定)回复于 2005-12-02 17:08:45 得分 1
第6中的:
select id from t where name like 'abc%'
与第9中的:
select id from t where name like '%abc%'
有什么不一样,按你说的,不都要进行全文检索吗?Top
51 楼vovo2000(没人要的猫)回复于 2005-12-02 17:55:13 得分 1
不一样,'abc%'只搜索abc开头的字段
'%abc%'则进行全表扫描Top
52 楼huwei2003(凡)回复于 2005-12-02 18:07:55 得分 1
upTop
53 楼xlhl(顽皮兔子)回复于 2005-12-02 18:18:51 得分 1
markTop
54 楼winternet(冬天)回复于 2005-12-02 18:22:07 得分 1
markTop
55 楼lisiyong(小样)回复于 2005-12-02 18:39:18 得分 1
markTop
56 楼netcoder(朱二)回复于 2005-12-02 19:03:31 得分 1
风云,好同志!Top
57 楼nononono(null,null)回复于 2005-12-02 19:53:59 得分 1
2、3的语句好像还是可以使用索引的。
Top
58 楼87607047()回复于 2005-12-02 20:17:30 得分 1
第6条与第9条矛盾呀!
6.下面的查询也将导致全表扫描:
select id from t where name like '%abc%'
若要提高效率,可以考虑全文检索。
9.应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where substring(name,1,3)='abc'--name以abc开头的id
应改为:
select id from t where name like 'abc%'
Top
59 楼buan(不安)回复于 2005-12-02 21:20:26 得分 1
太好了,多谢高手共享Top
60 楼gaojie001(高洁)回复于 2005-12-02 22:54:47 得分 1
收藏,Top
61 楼shiqing8899(米豆)回复于 2005-12-03 01:45:54 得分 1
已经收藏了Top
62 楼sxycgxj(云中客)回复于 2005-12-03 07:52:06 得分 1
不错的帖子,谢谢楼主啦Top
63 楼LAIYANGPJ(小丑)回复于 2005-12-03 09:10:40 得分 1
也来顶下.收藏!!!!!11Top
64 楼zhouhaihe()回复于 2005-12-03 09:26:29 得分 1
好东西
还有吗?Top
65 楼pbsql(风云)回复于 2005-12-03 11:50:03 得分 0
select id from t where name like '%abc%'
这个不能利用索引,不必多说了
将2更改如下:
2.应尽量避免使用 left join 和 null 值判断。left join 比 inner join 消耗更多的资源,因为它们包含与 null(不存在)数据匹配的数据,所以如果可以重新编写查询以使得该查询不使用任何 inner join ,则会得到相应的回报。
例如有两表:
product(product_id int not null,product_type_id int null,...),产品表,product_id为大于0的整数,product_type_id与表product_type关联,但可为空,因为有的产品没有类别
product_type(product_type_id not null,product_type_name null,...),产品类别表
此时要关联两表后查询 product 的内容,马上会想到使用 inner join ,但下面有一种方法可避免使用 inner join :
在 product_type 中增加一条记录:0,'',...,并将 product 的 product_type_id 设置为 not null ,当产品没有类别时将其 product_type_id 设为0,这样查询就可以使用INNER JOIN了。Top
66 楼chinahfl(chinahfl)回复于 2005-12-03 13:53:40 得分 1
UP, 学习中。Top
67 楼xfsfis(可可)回复于 2005-12-03 16:10:53 得分 1
好Top
68 楼MudLib(Alan)回复于 2005-12-03 17:18:26 得分 1
Mark!Top
69 楼wozhuchuanwei(一个组件编写者,就一定是一个更优秀的Delphi开发者)回复于 2005-12-03 20:10:48 得分 1
谢谢
已加入收藏~Top
70 楼cncharles(旺仔)回复于 2005-12-03 20:10:49 得分 1
mark & collectTop
71 楼yangys(杨杨)回复于 2005-12-03 20:37:07 得分 1
thankTop
72 楼Rocky_(洛奇)回复于 2005-12-03 22:01:15 得分 1
markTop
73 楼iwl()回复于 2005-12-03 22:05:53 得分 1
UPTop
74 楼scut_zcm(z寒武纪)回复于 2005-12-03 22:08:12 得分 1
好东东,不客气了~~~
3QUTop
75 楼windbey(北风)回复于 2005-12-04 06:00:26 得分 1
markTop
76 楼lanvode(小小生)回复于 2005-12-04 13:05:12 得分 1
好贴,坚决要顶!Top
77 楼guid6(学无止境)回复于 2005-12-04 17:23:43 得分 1
UP
Top
78 楼pbsql(风云)回复于 2005-12-05 12:54:44 得分 0
4例子不恰当,更改如下:
4.应尽量避免在 where 子句中使用 or 来连接条件,否则将可能导致引擎放弃使用索引而进行全表扫描,如有表t,key1、key2上建有索引,需要下面的存储过程:
create procedure select_proc1 @key1 int=0,@key2 int=0
as
begin
select key3 from t
where (@key1=0 or key1=@key1)
and (@key2=0 or key2=@key2)
end
go
这个存储过程会导致全表扫描,可作如下修改:
create procedure select_proc2 @key1 int=0,@key2 int=0
as
begin
if @key1 <>0 and @key2<>0
select key3 from t
where key1=@key1 and key2=@key2
else
if @key1<>0
select key3 from t where key1=@key1
else
select key3 from t where key2=@key2
end
go
更改后虽然代码增加了,但效率提高了。Top
79 楼rebacca1620(绮罗)回复于 2005-12-05 13:23:26 得分 1
好及时呢:)Top
80 楼lm2883098(懒惰的贝贝有进步,继续努力)回复于 2005-12-05 14:18:15 得分 1
学习~~~~~~~~~``Top
81 楼pbsql(风云)回复于 2005-12-06 11:40:03 得分 0
更新如下:
SQL SERVER中一些常见性能问题的总结
作者:pbsql(风云)
日期:2005-12-06
1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。
2.应尽量避免使用 left join 和 null 值判断。left join 比 inner join 消耗更多的资源,因为它们包含与 null (不存在)数据匹配的数据,所以如果可以重新编写查询以使得该查询不使用任何 inner join ,则会得到相应的回报。
例如有两表:
product(product_id int not null,product_type_id int null,...),产品表, product_id 为大于0的整数, product_type_id 与表 product_type 关联,但可为空,因为有的产品没有类别
product_type(product_type_id not null,product_type_name null,...),产品类别表
此时要关联两表后查询 product 的内容,马上会想到使用 inner join ,但下面有一种方法可避免使用 inner join :
在 product_type 中增加一条记录:0,'',...,并将 product 的 product_type_id 设置为 not null ,当产品没有类别时将其 product_type_id 设为0,这样查询就可以使用 inner join 了。
3.应尽量避免在 where 子句中使用!=或<>操作符,否则引擎可能放弃使用索引而进行全表扫描。
4.应尽量避免在 where 子句中使用 or 来连接条件,否则将可能导致引擎放弃使用索引而进行全表扫描,如有表 t , key1 、 key2 上建有索引,需要下面的存储过程:
create procedure select_proc1 @key1 int=0,@key2 int=0
as
begin
select key3 from t
where (@key1=0 or key1=@key1)
and (@key2=0 or key2=@key2)
end
go
这个存储过程会导致全表扫描,可作如下修改:
create procedure select_proc2 @key1 int=0,@key2 int=0
as
begin
if @key1 <>0 and @key2<>0
select key3 from t
where key1=@key1 and key2=@key2
else
if @key1<>0
select key3 from t where key1=@key1
else
select key3 from t where key2=@key2
end
go
更改后虽然代码增加了,但效率提高了。
5.in 和 not in 也要慎用,如:
select id from t where num in(1,2,3)
对于连续的数值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3
6.下面的查询也将导致全表扫描:
select id from t where name like '%abc%'
若要提高效率,可以考虑全文检索。
7.如果在 where 子句中使用参数,也会导致全表扫描。因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:
select id from t where num=@num
可以改为强制查询使用索引:
select id from t with(index(索引名)) where num=@num
8.应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where num/2=100
应改为:
select id from t where num=100*2
9.应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where substring(name,1,3)='abc'--name以abc开头的id
select id from t where datediff(day,createdate,'2005-11-30')=0--‘2005-11-30’生成的id
应改为:
select id from t where name like 'abc%'
select id from t where createdate>='2005-11-30' and createdate<'2005-12-1'
10.不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
11.在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。
12.不要写一些没有意义的查询,如需要生成一个空表结构:
select col1,col2 into #t from t where 1=0
这类代码不会返回任何结果集,但是会消耗系统资源的,应改成这样:
create table #t(...)
13.很多时候用 exists 代替 in 是一个好的选择:
select num from a where num in(select num from b)
用下面的语句替换:
select num from a where exists(select 1 from b where num=a.num)
14.并不是所有索引对查询都有效, SQL 是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,如一表中有字段 sex , male 、 female 几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。
15.索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 、 update 及 delete 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。
16.应尽可能的避免更新 clustered 索引数据列,因为 clustered 索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源。若应用系统需要频繁更新 clustered 索引数据列,那么需要考虑是否应将该索引建为 clustered 索引。
17.尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。
18.尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间(定长字段即使在数据为null时也需要定长的存储空间(7.0及更高版本)),其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些,而且每页(8KB)可能存储更多的记录数,这样也可以减少I/O的消耗而提高性能。
19.任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。
20.尽量使用表变量来代替临时表。如果表变量包含大量数据,请注意索引非常有限(只有主键索引)。
21.避免频繁创建和删除临时表,以减少系统表资源的消耗。
22.临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件,最好使用导出表。
23.在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果数据量不大,为了缓和系统表的资源,应先 create table ,然后 insert 。
24.如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。
25.尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。
26.使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。
27.与临时表一样,游标并不是不可使用。对小型数据集使用 FAST_FORWARD 游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。
28.在所有的存储过程和触发器的开始处设置 SET NOCOUNT ON ,在结束时设置 SET NOCOUNT OFF 。无需在执行存储过程和触发器的每个语句后向客户端发送 DONE_IN_PROC 消息。
29.尽量避免大事务操作,提高系统并发能力。当使用约束和触发器都能完成同样的功能时,优先考虑使用约束。
30.尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。Top
82 楼funsuzhou(☆【处变不惊】☆)回复于 2005-12-06 11:55:18 得分 1
学习一下,谢谢楼主!
顶!Top
83 楼luxiu1(浪彦)回复于 2005-12-06 12:42:35 得分 1
谢谢楼主,虽然我是初哥,但是都觉得是好贴.Top
84 楼topdogXP(心残)回复于 2005-12-06 13:12:18 得分 1
Mark!Top
85 楼rebacca1620(绮罗)回复于 2005-12-06 13:50:30 得分 1
很好啊!Top
86 楼AliceFeel(飞过西伯利亚的蝴蝶)回复于 2005-12-06 14:00:18 得分 1
收藏并谢谢啦~~~Top
87 楼DTWUJP(建平.net)回复于 2005-12-06 14:02:18 得分 1
markTop
88 楼woxiangfang(小黑黑脸)回复于 2005-12-07 09:20:39 得分 1
好貼﹐不愧是風云Top
89 楼Samensiu(山)回复于 2005-12-07 17:01:34 得分 1
好东西,正在吸收,谢谢!Top
90 楼artoksxb(进取人生)回复于 2005-12-07 17:24:27 得分 1
以前在msdn的T-sql规范中看过些!!收藏了。Top
91 楼wfliu()回复于 2005-12-12 17:36:38 得分 1
收藏Top
92 楼lxw99(十五天少爷(笑看风云))回复于 2005-12-21 17:13:38 得分 1
回复人: pbsql(风云) ( ) 信誉:138 2005-12-1 10:15:46 得分: 0
select id from t where num=10
union all
select id from t where num=20
真得比
select id from t where num=10 or num=20
的效率高吗
------------------------------------------------
当num上建有索引时,前者可以利用索引查询,而后者不能,只能全表扫描,所以前者效率高
或者这样解释:
从数据库访问的角度看,含有不连续连接词(OR和IN)的WHERE子句一般来说性能不会太好。所以,优化器可能会采用R策略,这种策略会生成1个工作表,其中含有每个可能匹配的执行的标识符,优化器把这些行标志符(页号和行号)看做是指向1个表中匹配的行的“动态索引”。优化器只需扫描工作表,取出每一个行标志符,再从数据表中取得相应的行,所以R策略的代价是生成工作表。
你真的这么认为吗 我执行的
select id from t where num=10
union all
select id from t where num=20
成本比
select id from t where num=10 or num=20
高出了4倍啊 不能只看时间和理论吧Top
93 楼lxw99(十五天少爷(笑看风云))回复于 2005-12-21 17:31:55 得分 1
我在clickclass上建立聚集索引
1.SELECT * FROM aa where clickclass=null 还是是使用索引
2.SELECT * FROM aa where clickclass<1 or clickclass>1 还是是使用索引
3.SELECT * FROM aa where clickclass<>1还是是使用索引
4.select * from aa where clickclass in(1,2,3) 还是使用索引
5.select * from aa where clickclass like '%1%' 还是使用索引
6.select * from aa where clickclass+1-1=1 还是使用索引
下面的我就不看了
风云兄请指教啊
aa 表有 clickdate(datetime),clickclass(int)Top
94 楼wangyongli()回复于 2005-12-21 18:14:19 得分 1
收藏Top
95 楼pbsql(风云)回复于 2005-12-21 22:26:39 得分 0
lxw99(十五天少爷(笑看风云)):
请看最后的更新,另请注意一些字眼:可能、尽量避免...
查询应该尽量提供准确的参数,一些不确定性的查询都可能使SQL无法准确使用索引,有个词叫sargable就是这个意思Top
96 楼tmeteor()回复于 2005-12-21 22:40:08 得分 1
我也发一个关于自增列:
SCOPE_IDENTITY 和 @@IDENTITY 的作用都是取得返回在当前会话中的任何表内所生成的最后一个标识值,简单的说就是在执行一条插入语句之后使用@@IDENTITY的全局变量,取得插入记录的ID号但是有个问题就是,@@IDENTITY是全局的,所以在他的功能会体现在所有作用域,一个操作,一个触发器,一个存储过程叫做一个作用域,这时候如果出现多个作用域的情况的时候,@@IDENTITY所取得的ID号就是最后一个作用域产生的结果。这时候我们要使用SCOPE_IDENTITY方法来作了。SCOPE_IDENTITY 只返回插入到当前作用域中的值;@@IDENTITY 不受限于特定的作用域。
使用方法:select SCOPE_IDENTITY() as ID from [table]select @@IDENTITY as ID from [table]
实例:
sql="SET NOCOUNT ON;insert into [Table](Item) values('"&Item&"')"
sql=sql&";select @@IDENTITY as ID from [Table];SET NOCOUNT OFF;"
Top
97 楼tmeteor()回复于 2005-12-21 22:47:39 得分 1
18.尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。
//不同意上述观点!Top
98 楼pbsql(风云)回复于 2005-12-21 23:48:46 得分 0
虽然说在更新时char比varchar可能要快些,但在存储空间和查询上,varchar比char更有优势,见这个帖子讨论:
http://community.csdn.net/Expert/topic/4464/4464546.xml?temp=.6719171Top
99 楼unlme(素鸡)回复于 2005-12-21 23:55:32 得分 1
lxw99(十五天少爷(笑看风云)),
你第一个例子也很模糊,成本可能是高,但在一个巨表中 10 和 20 的记录占了90%,
与 10 和 20 的个只占一条时两种查询方式的结果并非都是高出4倍吧,
后者才体现了索引的真正意义,前者见风云14条。
基本考虑都是实用性的大型数据库,而且id这样的字段是基本不可能有重复的值,14条也说明了这一点。所以你的例子很片面。
你的第二篇驳论就更没必要分析了,大家都知道聚集索引是个会重排表的索引,
对于搜索范围值特别有效,而且这样一个一表只能建一个谨慎玩意,
你认为我们还有必要讨论针对其查询的优化语句么?
我的经验远没有你和风云丰富,更加无法评价他的论点是否正确,但可以肯定的是:
1.他把自己总结的+继承前人的一些可能在某些“高手”眼里是很“基础”的东西展现给大家,
本身就是一种奉献的精神,个人认为无论对新手还是对熟练的开发管理人员,都将获益匪浅。
2.他从实际的开发出发,但世间万象,不同的情况有不同的结果,所以必然会有明显的反例,
然而这并非瑕不掩瑜,而是那些人太精通not exists了。
3.笑看风云笑。Top
100 楼anne6795()回复于 2005-12-22 08:45:41 得分 1
3Q~~~3Q,已经收藏了!!!Top
101 楼lxw99(十五天少爷(笑看风云))回复于 2005-12-22 08:53:10 得分 1
哇哈哈 光是SQlServer的索引就可以引发这么多的问题,
其实不管是聚集还是非聚集都不要建在重复性大的字段上
非聚集是用在不返回大型结果集的查询而聚集正好相反
经常被使用联接或 GROUP BY 子句的查询访问的列使用聚集索引
另外请大家看这个帖子http://community.csdn.net/Expert/topic/4430/4430562.xml?temp=.3321802 讨论的很激烈也是关于索引的问题Top
102 楼lisiyong(小样)回复于 2005-12-22 10:01:02 得分 1
..........Top
103 楼Aden(Aden)回复于 2005-12-22 10:59:38 得分 1
谢谢!收藏!Top
104 楼unlme(素鸡)回复于 2005-12-22 13:16:00 得分 1
其实不管是聚集还是非聚集都不要建在重复性大的字段上
非聚集是用在不返回大型结果集的查询而聚集正好相反
经常被使用联接或 GROUP BY 子句的查询访问的列使用聚集索引
同意~~Top
105 楼pbsql(风云)回复于 2005-12-22 13:45:50 得分 0
char与varchar的问题我已经在这里回复了:
http://community.csdn.net/Expert/topic/4464/4464546.xml?temp=.6719171Top
106 楼friendwei(友威)回复于 2005-12-22 13:53:41 得分 1
收藏起来慢慢看~~
也感谢LZ有这种大公无私的心态!大家一起学习:)Top
107 楼burningfire_wqf(FreshBird)回复于 2005-12-22 13:56:26 得分 1
支持Top
108 楼danisluo(沙砾)回复于 2005-12-22 14:01:16 得分 1
up upTop
109 楼radio_3000(学习中……)回复于 2005-12-26 12:53:13 得分 1
收藏起来慢慢看~~
也感谢LZ有这种大公无私的心态!大家一起学习:)
谢谢谢谢谢谢谢谢谢谢谢谢!
支持楼主!Top
110 楼mib23()(☆☆☆☆☆)回复于 2005-12-26 15:36:59 得分 1
mark,
学习中。。。Top
111 楼18687874(别拿苹果扔我)回复于 2005-12-26 16:55:45 得分 1
收藏收藏...谢谢楼主及众牛人...Top
112 楼jiechifeiniao(飛鳥)回复于 2006-01-09 08:49:07 得分 1
好贴,好贴,谢谢楼上的兄弟
Top
113 楼ShinnyZhang(张大爽)回复于 2006-01-09 09:54:36 得分 1
Mark!Top
114 楼wutao411(了缘)回复于 2006-01-09 10:02:52 得分 1
收Top
115 楼zhang_yzy(六子儿)回复于 2006-01-09 10:37:17 得分 1
学习,收Top
116 楼danisluo(沙砾)回复于 2006-01-10 11:05:31 得分 1
收藏,学习加upTop
117 楼jycjyc(果果)回复于 2006-01-10 13:35:38 得分 1
收Top
118 楼jml2004(jml)回复于 2006-01-10 15:10:15 得分 1
学习
Top
119 楼jixiaojie(太多借口)回复于 2006-01-10 16:20:49 得分 1
收藏Top
120 楼mylover002(靠近您,温暖我!)回复于 2006-01-10 16:43:12 得分 1
hao ding .........Top
121 楼eliee(丫头)回复于 2006-01-10 17:38:59 得分 1
受益非浅!谢谢!Top
122 楼xwmhn(小猫)回复于 2006-01-10 17:51:20 得分 1
接分,并收藏之...Top
123 楼dutguoyi(新鲜鱼排)回复于 2006-01-10 19:53:46 得分 1
收藏Top
124 楼dutguoyi(新鲜鱼排)回复于 2006-01-10 20:43:58 得分 1
參照
http://www.360doc.com/showRelevantArt.aspx?ArticleID=12004&ArticleNum=6
http://blog.csdn.net/dutguoyi/archive/2006/01/10/575617.aspx
我覺得還是有一些需要完善的。Top
125 楼suntt(两条腿的狗)回复于 2006-01-10 21:30:58 得分 1
upTop
126 楼cmHua()回复于 2006-01-13 09:55:14 得分 1
好贴,一定收藏Top
127 楼liujx_1999(Fly)回复于 2006-01-13 10:09:56 得分 1
upTop
128 楼zhangjian01361(★非也非也★)回复于 2006-03-01 09:01:22 得分 1
转贴--共享!
一、适合读者对象:数据库开发程序员,数据库的数据量很多,涉及到对SP(存储过程)的优化的项目开发人员,对数据库有浓厚兴趣的人。
二、介绍:在数据库的开发过程中,经常会遇到复杂的业务逻辑和对数据库的操作,这个时候就会用SP来封装数据库操作。如果项目的SP较多,书写又没有一定的规范,将会影响以后的系统维护困难和大SP逻辑的难以理解,另外如果数据库的数据量大或者项目对SP的性能要求很,就会遇到优化的问题,否则速度有可能很慢,经过亲身经验,一个经过优化过的SP要比一个性能差的SP的效率甚至高几百倍。
三、内容:
1、开发人员如果用到其他库的Table或View,务必在当前库中建立View来实现跨库操作,最好不要直接使用“databse.dbo.table_name”,因为sp_depends不能显示出该SP所使用的跨库table或view,不方便校验。
2、开发人员在提交SP前,必须已经使用set showplan on分析过查询计划,做过自身的查询优化检查。
3、高程序运行效率,优化应用程序,在SP编写过程中应该注意以下几点:
a)SQL的使用规范:
i. 尽量避免大事务操作,慎用holdlock子句,提高系统并发能力。
ii. 尽量避免反复访问同一张或几张表,尤其是数据量较大的表,可以考虑先根据条件提取数据到临时表中,然后再做连接。
iii. 尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该改写;如果使用了游标,就要尽量避免在游标循环中再进行表连接的操作。
iv. 注意where字句写法,必须考虑语句顺序,应该根据索引顺序、范围大小来确定条件子句的前后顺序,尽可能的让字段顺序与索引顺序相一致,范围从大到小。
v. 不要在where子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
vi. 尽量使用exists代替select count(1)来判断是否存在记录,count函数只有在统计表中所有行数时使用,而且count(1)比count(*)更有效率。
vii. 尽量使用“>=”,不要使用“>”。
viii. 注意一些or子句和union子句之间的替换
ix. 注意表之间连接的数据类型,避免不同类型数据之间的连接。
x. 注意存储过程中参数和数据类型的关系。
xi. 注意insert、update操作的数据量,防止与其他应用冲突。如果数据量超过200个数据页面(400k),那么系统将会进行锁升级,页级锁会升级成表级锁。
b)索引的使用规范:
i. 索引的创建要与应用结合考虑,建议大的OLTP表不要超过6个索引。
ii. 尽可能的使用索引字段作为查询条件,尤其是聚簇索引,必要时可以通过index index_name来强制指定索引
iii. 避免对大表查询时进行table scan,必要时考虑新建索引。
iv. 在使用索引字段作为条件时,如果该索引是联合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用。
v. 要注意索引的维护,周期性重建索引,重新编译存储过程。
c)tempdb的使用规范:
i. 尽量避免使用distinct、order by、group by、having、join、cumpute,因为这些语句会加重tempdb的负担。
ii. 避免频繁创建和删除临时表,减少系统表资源的消耗。
iii. 在新建临时表时,如果一次性插入数据量很大,那么可以使用select into代替create table,避免log,提高速度;如果数据量不大,为了缓和系统表的资源,建议先create table,然后insert。
iv. 如果临时表的数据量较大,需要建立索引,那么应该将创建临时表和建立索引的过程放在单独一个子存储过程中,这样才能保证系统能够很好的使用到该临时表的索引。
v. 如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先truncate table,然后drop table,这样可以避免系统表的较长时间锁定。
vi. 慎用大的临时表与其他大表的连接查询和修改,减低系统表负担,因为这种操作会在一条语句中多次使用tempdb的系统表。
d)合理的算法使用:
根据上面已提到的SQL优化技术和ASE Tuning手册中的SQL优化内容,结合实际应用,采用多种算法进行比较,以获得消耗资源最少、效率最高的方法。具体可用ASE调优命令:set statistics io on, set statistics time on , set showplan on 等。Top
129 楼DengXingJie(杰西)回复于 2006-04-04 12:45:32 得分 1
收藏Top
130 楼sxdoujg(无情过客)回复于 2006-04-04 13:00:07 得分 1
up
Top
131 楼chenhu_doc(^0^纯一狼^0^ 看书看到大笑,直到不能自已)回复于 2006-04-04 13:04:30 得分 1
这个版块的人气就是旺Top
132 楼znjgress(四空和尚)回复于 2006-04-04 13:58:23 得分 1
收藏 收藏Top
133 楼marco08(天道酬勤)回复于 2006-04-12 14:13:35 得分 1
收藏Top
134 楼xeqtr1982(Visual C# .NET)回复于 2006-04-12 14:29:57 得分 1
好贴,收藏Top
135 楼darklight2008(其实我是水瓶座)回复于 2006-04-12 15:28:28 得分 1
Good and mark~~~~~~~~~~Top
136 楼Yang_(扬帆破浪)回复于 2006-04-12 15:46:10 得分 1
Mark
Top
137 楼Yang_(扬帆破浪)回复于 2006-04-12 16:17:05 得分 1
不知道风云在不?
18.尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。
确实表述不是很准确
1、首先定长&变长的概念理解问题,我理解定长首先大部分都是差不多长的,而且不是很长,比如部门名字段,很难有公司取的部门名特别长或者特别短;其次定长应该都不是很长的,很难想象有人用char(4000)。变长的概念就是可能很长,也可能很短,比如备注字段。用定长还是变长主要从实际需要考虑。
2、字符串比较效率问题,应该说定长必定长效率高,这里风云对变长字符串的存储的理解好像有点偏差,变长字符串实际存储了两个东西:长度和字符串本身,所以有两方面他们是不同的:
A:存取数据,这个对效率是主要的,定长去出字段值只需要一次操作(读),变长需要两次,一次读长度,一次都数据,这个效率差别很大的。
B:字符串比较,变长和定长的字符串比较方法是不同的,定长的不满部分已经用空格填充,直接比较就可以了,变长的比较则需要把短的先填充到和长的一样长,在做比较。(这里假设不是做等值比较,而是><等不等值比较)
欢迎讨论,说的不一定对
Top
138 楼DengXingJie(杰西)回复于 2006-04-12 16:37:59 得分 1
樓上的有見解
只是這樣下來不知道到底誰的更准確些
似乎聽鄒建說過char比varchar效率要好一些
僅僅是聽說而已Top
139 楼itblog(Just for wife!)回复于 2006-04-12 16:41:15 得分 1
好东西~Top
140 楼itblog(Just for wife!)回复于 2006-04-12 16:41:26 得分 1
好东西~Top
141 楼itblog(Just for wife!)回复于 2006-04-12 16:41:31 得分 1
好东西~Top
142 楼zjdyzwx(十一月猪)回复于 2006-04-12 16:46:16 得分 1
好东西 收藏Top
143 楼utmost100(核动力)回复于 2006-04-12 17:13:33 得分 1
mark一下,以后再看Top
144 楼Batiraul(Bati4Ever)回复于 2006-04-12 17:55:28 得分 1
回味一下Top
145 楼yjlhch(爱拼才会赢)回复于 2006-04-12 19:37:27 得分 1
好贴Top
146 楼skylion()回复于 2006-04-12 22:43:44 得分 1
难道是来送分的?Top
147 楼ahua_liu()回复于 2006-04-12 23:46:00 得分 1
一定要看Top
148 楼yny123()回复于 2006-04-12 23:46:56 得分 1
MARKTop
149 楼superhasty(鸟儿自空中飞过)回复于 2006-04-12 23:47:29 得分 1
goodTop
150 楼pbsql(风云)回复于 2006-04-13 00:26:31 得分 0
Yang_(扬帆破浪):
1、首先定长&变长的概念理解问题,我理解定长首先大部分都是差不多长的,而且不是很长,比如部门名字段,很难有公司取的部门名特别长或者特别短;其次定长应该都不是很长的,很难想象有人用char(4000)。变长的概念就是可能很长,也可能很短,比如备注字段。用定长还是变长主要从实际需要考虑。
******
这个是要从实际需要考虑,但如果实际上差不多长的而又不是很长的话,那讨论似乎就没必要了
******
2、字符串比较效率问题,应该说定长必定长效率高,这里风云对变长字符串的存储的理解好像有点偏差,变长字符串实际存储了两个东西:长度和字符串本身,所以有两方面他们是不同的:
A:存取数据,这个对效率是主要的,定长去出字段值只需要一次操作(读),变长需要两次,一次读长度,一次都数据,这个效率差别很大的。
******
这个值得商榷,为什么定长就不读长度呢?不读长度又怎么知道要读多长?
另外,即便如此,我觉得效率的差别也不在于是否要多读长度(顶多用2字节来存长度吧),而是字符串本身的长度决定了I/O的消耗
******
B:字符串比较,变长和定长的字符串比较方法是不同的,定长的不满部分已经用空格填充,直接比较就可以了,变长的比较则需要把短的先填充到和长的一样长,在做比较。(这里假设不是做等值比较,而是><等不等值比较)
******
这个似乎是一样吧,应该都是填充到和长的一样长后再做比较,不管是“=”还是“<>”比较,可以简单做个试验:
declare @a varchar(10),@b varchar(10)
select @a='a ',@b='a'
不管定义成char还是varchar,然后比较就可以看出
正因为“都是填充到和长的一样长后再做比较”,我才觉得如果是varchar,实际上比较时填充的字符数可能会更少些
欢迎继续讨论Top
151 楼MaiCle(原来小日本连畜生都不如)回复于 2006-04-13 00:40:59 得分 1
如果where子句是组合查询,楼主以上归纳的东东就不好使了。
因为 where xxx and yyy, 这时数据库会根据现有的索引进行优化匹配,而且还会根据实际的数据量进行优化。 关于索引的东东,最好的办法就是大家自己练习一下,实践了才知道怎么回事。
还有这个例子举得太牵强了。
4.应尽量避免在 where 子句中使用 or 来连接条件,否则将可能导致引擎放弃使用索引而进行全表扫描,如有表 t , key1 、 key2 上建有索引,需要下面的存储过程:
create procedure select_proc1 @key1 int=0,@key2 int=0
as
begin
select key3 from t
where (@key1=0 or key1=@key1)
and (@key2=0 or key2=@key2)
end
go
这个存储过程会导致全表扫描,可作如下修改:
create procedure select_proc2 @key1 int=0,@key2 int=0
as
begin
if @key1 <>0 and @key2<>0
select key3 from t
where key1=@key1 and key2=@key2
else
if @key1<>0
select key3 from t where key1=@key1
else
select key3 from t where key2=@key2
end
go
Top
152 楼MaiCle(原来小日本连畜生都不如)回复于 2006-04-13 00:41:56 得分 1
如果where子句是组合查询,楼主以上归纳的东东就不好使了。
因为 where xxx and yyy, 这时数据库会根据现有的索引进行优化匹配,而且还会根据实际的数据量进行优化。 关于索引的东东,最好的办法就是大家自己练习一下,实践了才知道怎么回事。
还有这个例子举得太牵强了。
4.应尽量避免在 where 子句中使用 or 来连接条件,否则将可能导致引擎放弃使用索引而进行全表扫描,如有表 t , key1 、 key2 上建有索引,需要下面的存储过程:
create procedure select_proc1 @key1 int=0,@key2 int=0
as
begin
select key3 from t
where (@key1=0 or key1=@key1)
and (@key2=0 or key2=@key2)
end
go
这个存储过程会导致全表扫描,可作如下修改:
create procedure select_proc2 @key1 int=0,@key2 int=0
as
begin
if @key1 <>0 and @key2<>0
select key3 from t
where key1=@key1 and key2=@key2
else
if @key1<>0
select key3 from t where key1=@key1
else
select key3 from t where key2=@key2
end
go
Top
153 楼MaiCle(原来小日本连畜生都不如)回复于 2006-04-13 00:48:14 得分 1
24.如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。
-- 是不是在每个return之前都要执行删除命令啊? 系统表较长时间锁定是什么概念,有多长时间?为了什么事而锁定。 如果临时表就几个(少于3),且临时表数据也很少,也需要显示的删除吗?Top
154 楼billywolf(康康)回复于 2006-04-13 21:18:16 得分 1
学习Top
155 楼Yang_(扬帆破浪)回复于 2006-04-13 22:43:47 得分 1
这样说看来只有做试验才能说明了
可惜我现在的电脑没有环境
我的经验是char不设置60以上长度的,varchar不设置30以下的
Top
156 楼jiaojian843(莱莱斯基)回复于 2006-04-14 09:15:06 得分 1
学习Top
157 楼chenyu112(晨雨)回复于 2006-04-14 10:45:56 得分 1
学习Top
158 楼yzujjcb()回复于 2006-04-14 16:26:40 得分 1
学习一把,顺便做个记号!Top
159 楼jackeychen0920(诸神寒冰)回复于 2006-04-14 16:37:27 得分 1
好东西
谢谢
搂主
顶一下~~~~~~Top
160 楼sxdoujg(无情过客)回复于 2006-04-14 16:49:37 得分 1
up
Top
161 楼edp08(王二)回复于 2006-04-14 16:56:31 得分 1
between and 和 >= <= 哪个快?
Top
162 楼bflovesnow()回复于 2006-04-14 17:10:46 得分 1
hehe.Top
163 楼JustNoOtherWay(e阿亮)回复于 2006-04-14 17:12:27 得分 1
学习,mark 下Top
164 楼sam_summer(Sam)回复于 2006-04-14 17:33:22 得分 1
先学习,看完了再讨论!
谢谢楼主Top
165 楼jakexue31(anni)回复于 2006-04-14 17:51:29 得分 1
作个记号Top
166 楼hanjoe109()回复于 2006-04-14 21:01:24 得分 1
非常非常感谢!Top
167 楼47522341(睡到8:30)回复于 2006-04-15 10:48:42 得分 1
我对第四句关于or的使用有点看法:
楼主说:-------------
4.应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num=10 or num=20
可以这样查询:
select id from t where num=10
union all
select id from t where num=20
----------------------------
但我们查看sql后台执行计划可以看到;
假如有两个查询
1,
select id from sysobjects where id =10 or id=20
2,
select id from sysobjects where id =10
union all
select id from sysobjects where id =20
从查询计划中得到,
1的预计执行成本为0.00640,其中I/O成本0.00632,CPU成本0.000081;
2的预计执行成本则是0.01280 其中id=10的I/O成本0.00632,CPU成本0.000080;
id=20的I/O成本0.00632,CPU成本0.000080;
这样,虽然1中的CPU成本稍高于后面两句单独执行的结果;
但其总体成本要少于union执行的情况。
Top
168 楼wei123456(onedotone)回复于 2006-04-15 11:42:35 得分 1
谢谢,谢谢Top
169 楼ypnet(高原)回复于 2006-04-15 14:55:49 得分 1
学习Top
170 楼Dark13(我不知道!我什么都不知道!)回复于 2006-04-15 15:26:24 得分 1
MARKTop
171 楼lhl123hk(木木)回复于 2006-04-27 10:31:22 得分 1
学习Top
172 楼yui()回复于 2006-04-27 10:57:16 得分 1
4.应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num=10 or num=20
可以这样查询:
select id from t where num=10
union all
select id from t where num=20
关于这个,我也认为没必要,查询优化器一般情况下会自行优化,不会用不了索引而做全表扫描的,但不排除特殊情况下优化器发傻,认不了索引,但这情况少之又少。
Top
173 楼holym(小雨)回复于 2006-05-21 14:52:20 得分 1
studyTop
174 楼steelmqb1(小N)回复于 2006-05-21 20:32:57 得分 1
upTop
175 楼koposo(不知不是错,不问就是过)回复于 2006-05-21 21:00:38 得分 1
markTop
176 楼j__jake(人称大侠,但偶不是)回复于 2006-05-21 21:36:47 得分 1
good thing
learningTop
177 楼xietnt(雷管)回复于 2006-05-21 22:06:59 得分 1
upTop

