.NET

一、数据库基础知识(通用)篇

1.说说主键、外键、超键、候选键

超键:在关系中能唯一标识元组的属性集称为关系模式的超键。一个属性可以为作为一个超键,多个属

性组合在一起也可以作为一个超键。超键包含候选键和主键。

候选键:是最小超键,即没有冗余元素的超键。

主键:数据库表中对储存数据对象予以唯一和完整标识的数据列或属性的组合。一个数据列只能有一个

主键,且主键的取值不能缺失,即不能为空值(Null)。

外键:在一个表中存在的另一个表的主键称此表的外键。

2.为什么用自增列作为主键?

如果我们定义了主键(PRIMARY KEY),那么InnoDB会选择主键作为聚集索引、

如果没有显式定义主键,则InnoDB会选择第一个不包含有NULL值的唯一索引作为主键索引、

如果也没有这样的唯一索引,则InnoDB会选择内置6字节长的ROWID作为隐含的聚集索引(ROWID随着行记录的写入而主键递增,这个ROWID不像ORACLE的ROWID那样可引用,是隐含的)。

数据记录本身被存于主索引(一颗B+Tree)的叶子节点上。这就要求同一个叶子节点内(大小为一个内存页或磁盘页)的各条数据记录按主键顺序存放,因此每当有一条新的记录插入时,MySQL会根据其主键将其插入适当的节点和位置,如果页面达到装载因子(InnoDB默认为15/16),则开辟一个新的页(节点)

如果表使用自增主键,那么每次插入新的记录,记录就会顺序添加到当前索引节点的后续位置,当一页写满,就会自动开辟一个新的页

如果使用非自增主键(如果身份证号或学号等),由于每次插入主键的值近似于随机,因此每次新记录都要被插到现有索引页的中间某个位置,此时MySQL不得不为了将新记录插到合适位置而移动数据,甚至目标页面可能已经被回写到磁盘上而从缓存中清掉,此时又要从磁盘上读回来,这增加了很多开销,同时频繁的移动、分页操作造成了大量的碎片,得到了不够紧凑的索引结构,后续不得不通过 OPTIMIZE TABLE来重建表并优化填充页面。

3.触发器的作用是什么?

触发器是一种特殊的存储过程,主要是通过事件来触发而被执行的。它可以强化约束,来维护数据的完整性和一致性,可以跟踪数据库内的操作从而不允许未经许可的更新和变化。可以联级运算。如,某表上的触发器上包含对另一个表的数据操作,而该操作又会导致该表触发器被触发。

4.什么是存储过程?用什么来调用?

存储过程是一个预编译的SQL语句,优点是允许模块化的设计,就是说只需创建一次,以后在该程序中就可以调用多次。如果某次操作需要执行多次SQL,使用存储过程比单纯SQL语句执行要快。

调用:

1)可以用一个命令对象来调用存储过程。

2)可以供外部程序调用,比如:java程序。

5.说说存储过程的优缺点?

优点:

1)存储过程是预编译过的,执行效率高。

2)存储过程的代码直接存放于数据库中,通过存储过程名直接调用,减少网络通讯。

3)安全性高,执行存储过程需要有一定权限的用户。

4)存储过程可以重复使用,可减少数据库开发人员的工作量。

缺点:

移植性差

6.说说存储过程与函数的区别

(1)存储过程用户在数据库中完成特定操作或者任务(如插入,删除等),函数用于返回特定的数

据。

(2)存储过程声明用procedure,函数用function。

(3)存储过程不需要返回类型,函数必须要返回类型。

(4)存储过程可作为独立的pl-sql执行,函数不能作为独立的plsql执行,必须作为表达式的一部分。

(5)存储过程只能通过out和in/out来返回值,函数除了可以使用out,in/out以外,还可以使用return

返回值。

(6)sql语句(DML或SELECT)中不可用调用存储过程,而函数可以。

7.什么叫视图?游标是什么?

视图:

是一种虚拟的表,具有和物理表相同的功能。可以对视图进行增,改,查,操作,试图通常是有一个表或者多个表的行或列的子集。对视图的修改会影响基本表。它使得我们获取数据更容易,相比多表查询。

游标:

是对查询出来的结果集作为一个单元来有效的处理。游标可以定在该单元中的特定行,从结果集的当前行检索一行或多行。可以对结果集当前行做修改。一般不使用游标,但是需要逐条处理数据的时候,游标显得十分重要。

8.视图的优缺点有哪些?

优点:

1对数据库的访问,因为视图可以有选择性的选取数据库里的一部分。

2)用户通过简单的查询可以从复杂查询中得到结果。

3)维护数据的独立性,试图可从多个表检索数据。

4)对于相同的数据可产生不同的视图。

缺点:

性能:查询视图时,必须把视图的查询转化成对基本表的查询,如果这个视图是由一个复杂的多表查询所定义,那么,那么就无法更改数据

9.说说droptruncate delete区别

最基本:

1) drop直接删掉表。

2) truncate删除表中数据,再插入时自增长id又从1开始。

3) delete删除表中数据,可以加where字句。

(1) DELETE语句执行删除的过程是每次从表中删除一行,并且同时将该行的删除操作作为事务记录在

日志中保存以便进行进行回滚操作。TRUNCATE TABLE 则一次性地从表中删除所有的数据并不把单独的删除操作记录记入日志保存,删除行是不能恢复的。并且在删除的过程中不会激活与表有关的删除触发器。执行速度快。

(2) 表和索引所占空间。当表被TRUNCATE 后,这个表和索引所占用的空间会恢复到初始大小,而DELETE操作不会减少表或索引所占用的空间。drop语句将表所占用的空间全释放掉。

(3) 一般而言,drop > truncate > delete

(4) 应用范围。TRUNCATE 只能对TABLE;DELETE可以是table和view

(5) TRUNCATE 和DELETE只删除数据,而DROP则删除整个表(结构和数据)。

(6) truncate与不带where的delete :只删除数据,而不删除表的结构(定义)drop语句将删除表的结构被依赖的约束(constrain),触发器(trigger)索引(index);依赖于该表的存储过程/函数将被保留,

但其状态会变为:invalid。

(7) delete语句为DML(data maintain Language),这个操作会被放到 rollback segment中,事务提交

后才生效。如果有相应的 tigger,执行的时候将被触发。

(8) truncate、drop是DLL(data define language),操作立即生效,原数据不放到 rollback

segment中,不能回滚。

(9) 在没有备份情况下,谨慎使用 drop 与 truncate。要删除部分数据行采用delete且注意结合

where来约束影响范围。回滚段要足够大。要删除表用drop;若想保留表而将表中数据删除,如果与事

务无关,用truncate即可实现。如果和事务有关,或老师想触发trigger,还是用delete。

(10) Truncate table 表名 速度快,而且效率高,因为:?truncate table 在功能上与不带 WHERE 子句的

DELETE 语句相同:二者均删除表中的全部行。但 TRUNCATE TABLE 比 DELETE 速度快,且使用的系统和事务日志资源少。DELETE 语句每次删除一行,并在事务日志中为所删除的每行记录一项。TRUNCATE TABLE 通过释放存储表数据所用的数据页来删除数据,并且只在事务日志中记录页的释

放。

(11) TRUNCATE TABLE 删除表中的所有行,但表结构及其列、约束、索引等保持不变。新行标识所

用的计数值重置为该列的种子。如果想保留标识计数值,请改用 DELETE。如果要删除表定义及其数

据,请使用 DROP TABLE 语句。

(12) 对于由 FOREIGN KEY 约束引用的表,不能使用 TRUNCATE TABLE,而应使用不带 WHERE 子

句的 DELETE 语句。由于 TRUNCATE TABLE 不记录在日志中,所以它不能激活触发器。

10.什么是临时表,临时表什么时候删除?

临时表可以手动删除:

DROP TEMPORARY TABLE IF EXISTS temp_tb;

临时表只在当前连接可见,当关闭连接时,MySQL会自动删除表并释放所有空间。因此在不同的连接中可以创建同名的临时表,并且操作属于本连接的临时表。

创建临时表的语法与创建表语法类似,不同之处是增加关键字TEMPORARY,

如:

CREATE TEMPORARY TABLE tmp_table (

NAME VARCHAR (10) NOT NULL,

time date NOT NULL

);

select * from tmp_table;

11.说说非关系型数据库和关系型数据库区别,优势比较?

非关系型数据库的优势:

性能:NOSQL是基于键值对的,可以想象成表中的主键和值的对应关系,而且不需要经过SQL层的解

析,所以性能非常高。

可扩展性:同样也是因为基于键值对,数据之间没有耦合性,所以非常容易水平扩展。

关系型数据库的优势:

复杂查询:可以用SQL语句方便的在一个表以及多个表之间做非常复杂的数据查询。

CREATE TEMPORARY TABLE tmp_table (

NAME VARCHAR (10) NOT NULL,

time date NOT NULL

);

select * from tmp_table;事务支持:使得对于安全性能很高的数据访问要求得以实现。

其他:

1.对于这两类数据库,对方的优势就是自己的弱势,反之亦然。

2.NOSQL数据库慢慢开始具备SQL数据库的一些复杂查询功能,比如MongoDB。

3.对于事务的支持也可以用一些系统级的原子操作来实现例如乐观锁之类的方法来曲线救国,比如

Redis set nx。

12.什么是数据库范式,根据某个场景设计数据表?

第一范式:(确保每列保持原子性)所有字段值都是不可分解的原子值。

第一范式是最基本的范式。如果数据库表中的所有字段值都是不可分解的原子值,就说明该数据库表满 足了第一范式。

第一范式的合理遵循需要根据系统的实际需求来定。比如某些数据库系统中需要用到“地址”这个属性, 本来直接将“地址”属性设计成一个数据库表的字段就行。但是如果系统经常会访问“地址”属性中的“城市” 部分,那么就非要将“地址”这个属性重新拆分为省份、城市、详细地址等多个部分进行存储,这样在对地址中某一部分操作的时候将非常方便。这样设计才算满足了数据库的第一范式,如下表所示。

上表所示的用户信息遵循了第一范式的要求,这样在对用户使用城市进行分类的时候就非常方便,也提高了数据库的性能。

第二范式:(确保表中的每列都和主键相关)在一个数据库表中,一个表中只能保存一种数据,不可以把多种数据保存在同一张数据库表中。

第二范式在第一范式的基础之上更进一层。第二范式需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言)。也就是说在一个数据库表中,一个表中只能保存一种数据,不可以把多种数据保存在同一张数据库表中。

比如要设计一个订单信息表,因为订单中可能会有多种商品,所以要将订单编号和商品编号作为数据库表的联合主键。

第三范式:(确保每列都和主键列直接相关,而不是间接相关) 数据表中的每一列数据都和主键直接相关,而不能间接相关。

第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关。

比如在设计一个订单数据表的时候,可以将客户编号作为一个外键和订单表建立相应的关系。而不可以在订单表中添加关于客户其它信息(比如姓名、所属公司等)的字段。

BCNF:符合3NF,并且,主属性不依赖于主属性。

若关系模式属于第二范式,且每个属性都不传递依赖于键码,则R属于BC范式。

通常BC范式的条件有多种等价的表述:每个非平凡依赖的左边必须包含键码;每个决定因素必须包含键码。

BC范式既检查非主属性,又检查主属性。当只检查非主属性时,就成了第三范式。满足BC范式的关系都必然满足第三范式。

还可以这么说:若一个关系达到了第三范式,并且它只有一个候选码,或者它的每个候选码都是单属

性,则该关系自然达到BC范式。

一般,一个数据库设计符合3NF或BCNF就可以了。

第四范式:要求把同一表内的多对多关系删除。

第五范式:从最终结构重新建立原始结构。

13.什么是 内连接、外连接、交叉连接、笛卡尔积等?

内连接: 只连接匹配的行

左外连接: 包含左边表的全部行(不管右边的表中是否存在与它们匹配的行),以及右边表中全部匹配的行

右外连接: 包含右边表的全部行(不管左边的表中是否存在与它们匹配的行),以及左边表中全部匹配的行

例如1:

SELECT a.,b. FROM luntan LEFT JOIN usertable as b ON a.username=b.username

例如2:

SELECT a.,b. FROM city as a FULL OUTER JOIN user as b ON a.username=b.username

全外连接: 包含左、右两个表的全部行,不管另外一边的表中是否存在与它们匹配的行。

交叉连接: 生成笛卡尔积-它不使用任何匹配或者选取条件,而是直接将一个数据源中的每个行与另一个数据源的每个行都一一匹配

例如:

SELECT type,pub_name FROM titles CROSS JOIN publishers ORDER BY type

14.varchar和char的使用场景?

1.char的长度是不可变的,而varchar的长度是可变的。

定义一个char[10]和varchar[10]。

如果存进去的是‘csdn’,那么char所占的长度依然为10,除了字符‘csdn’外,后面跟六个空格,varchar就

立马把长度变为4了,取数据的时候,char类型的要用trim()去掉多余的空格,而varchar是不需要的。

2.char的存取速度还是要比varchar要快得多,因为其长度固定,方便程序的存储与查找。

char也为此付出的是空间的代价,因为其长度固定,所以难免会有多余的空格占位符占据空间,可谓是以空间换取时间效率。

varchar是以空间效率为首位。

3.char的存储方式是:对英文字符(ASCII)占用1个字节,对一个汉字占用两个字节。

varchar的存储方式是:对每个英文字符占用2个字节,汉字也占用2个字节。

4.两者的存储数据都非unicode的字符数据。

15.SQL语言分类

SQL语言共分为四大类:

一、数据查询语言DQL

二、数据操纵语言DML

三、数据定义语言DDL

四、数据控制语言DCL。

1. 数据查询语言DQL

数据查询语言DQL基本结构是由SELECT子句,FROM子句,WHERE子句组成的查询块:

SELECT

FROM

WHERE

2. 数据操纵语言DML

数据操纵语言DML主要有三种形式:

1) 插入:INSERT

2) 更新:UPDATE

3) 删除:DELETE

SELECT a.,b. FROM luntan LEFT JOIN usertable as b ON a.username=b.username

SELECT a.,b. FROM city as a FULL OUTER JOIN user as b ON a.username=b.username

SELECT type,pub_name FROM titles CROSS JOIN publishers ORDER BY type3. 数据定义语言DDL

数据定义语言DDL用来创建数据库中的各种对象-----表、视图、索引、同义词、聚簇等如:

CREATE TABLE/VIEW/INDEX/SYN/CLUSTER

表 视图 索引 同义词 簇

DDL操作是隐性提交的!不能rollback

4. 数据控制语言DCL

数据控制语言DCL用来授予或回收访问数据库的某种特权,并控制数据库操纵事务发生的时间及效

果,对数据库实行监视等。如:

1) GRANT:授权。

2) ROLLBACK [WORK] TO [SAVEPOINT]:回退到某一点。回滚---ROLLBACK;回滚命令使数据库

状态回到上次最后提交的状态。其格式为:

SQL>ROLLBACK;

3) COMMIT [WORK]:提交。

在数据库的插入、删除和修改操作时,只有当事务在提交到数据库时才算完成。在事务提交前,只

有操作数据库的这个人才能有权看到所做的事情,别人只有在最后提交完成后才可以看到。

提交数据有三种类型:显式提交、隐式提交及自动提交。下面分别说明这三种类型。

(1) 显式提交

用COMMIT命令直接完成的提交为显式提交。其格式为:SQL>COMMIT;

(2) 隐式提交

用SQL命令间接完成的提交为隐式提交。这些命令是:

ALTER,AUDIT,COMMENT,CONNECT,CREATE,DISCONNECT,DROP,

EXIT,GRANT,NOAUDIT,QUIT,REVOKE,RENAME。

(3) 自动提交

若把AUTOCOMMIT设置为ON,则在插入、修改、删除语句执行后,

系统将自动进行提交,这就是自动提交。

其格式为:SQL>SET AUTOCOMMIT ON;

16.说说like %和-的区别

通配符的分类

%百分号通配符:表示任何字符出现任意次数(可以是0次).

下划线通配符:表示只能匹配单个字符,不能多也不能少,就是一个字符.

like操作符: LIKE作用是指示mysql后面的搜索模式是利用通配符而不是直接相等匹配进行比较.

注意如果在使用like操作符时,后面的没有使用通用匹配符效果是和=一致的,SELECT * FROM products

WHERE products.prod_name like '1000';只能匹配的结果为1000,而不能匹配像JetPack 1000这样的结果.

%通配符使用匹配以"yves"开头的记录:(包括记录"yves") SELECT FROM products WHERE

products.prod_name like 'yves%';

匹配包含"yves"的记录(包括记录"yves") SELECT FROM products WHERE products.prod_name like

'%yves%';

匹配以"yves"结尾的记录(包括记录"yves",不包括记录"yves ",也就是yves后面有空格的记录,这里需要注意)

SELECT * FROM products WHERE products.prod_name like '%yves';

通配符使用: SELECT FROM products WHERE products.prod_name like 'yves'; 匹配结果为: 像"yyves"这样记录. SELECT FROM products WHERE products.prodname like 'yves'; 匹配结果为: 像"yvesHe"这样的记录.(一个下划线只能匹配一个字符,不能多也不能少)

注意事项:

注意大小写,在使用模糊匹配时,也就是匹配文本时,mysql是可能区分大小的,也可能是不区分大小写的,这个结果是取决于用户对MySQL的配置方式.如果是区分大小写,那么像YvesHe这样记录是不能

被"yves__"这样的匹配条件匹配的.

注意尾部空格,"%yves"是不能匹配"heyves "这样的记录的.

注意NULL,%通配符可以匹配任意字符,但是不能匹配NULL,也就是说SELECT * FROM products WHERE

products.prod_name like '%;是匹配不到products.prod_name为NULL的的记录.技巧与建议:

正如所见, MySQL的通配符很有用。但这种功能是有代价的:通配符搜索的处理一般要比前面讨论的其他搜索所花时间更长。这里给出一些使用通配符要记住的技巧。

不要过度使用通配符。如果其他操作符能达到相同的目的,应该 使用其他操作符。

在确实需要使用通配符时,除非绝对有必要,否则不要把它们用 在搜索模式的开始处。把通配符置于搜索模式的开始处,搜索起 来是最慢的。

仔细注意通配符的位置。如果放错地方,可能不会返回想要的数.

17.说说count(*)、count(1)、count(column)的区别

count()对行的数目进行计算,包含NULL

count(column)对特定的列的值具有的行数进行计算,不包含NULL值。

count()还有一种使用方式,count(1)这个用法和count()的结果是一样的。

性能问题:

1.任何情况下SELECT COUNT() FROM tablename是最优选择;

2.尽量减少SELECT COUNT() FROM tablename WHERE COL = ‘value’ 这种查询;

3.杜绝SELECT COUNT(COL) FROM tablename WHERE COL2 = ‘value’ 的出现。

如果表没有主键,那么count(1)比count()快。

如果有主键,那么count(主键,联合主键)count()快。

如果表只有一个字段,count()最快。

count(1)count(主键)一样,只扫描主键。count()跟count(非主键)一样,扫描整个表。明显前者更快一些。

18.什么是最左前缀原则?

多列索引:

ALTER TABLE people ADD INDEX lname_fname_age (lame,fname,age);

为了提高搜索效率,我们需要考虑运用多列索引,由于索引文件以B-Tree格式保存,所以我们不用扫描任何记录,即可得到最终结果。

注:在mysql中执行查询时,只能使用一个索引,如果我们在lname,fname,age上分别建索引,执行查询时,只能使用一个索引,mysql会选择一个最严格(获得结果集记录数最少)的索引。

最左前缀原则:顾名思义,就是最左优先,上例中我们创建了lname_fname_age多列索引,相当于创建了(lname)单列索引,(lname,fname)组合索引以及(lname,fname,age)组合索引。

19.什么是索引?

何为索引:

数据库索引,是数据库管理系统中一个排序的数据结构,索引的实现通常使用B树及其变种B+树。

在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指

向)数据,这样就可以在这些数据结构上实现高级查找算法。这种数据结构,就是索引。

20.索引的作用?它的优点缺点是什么?

索引作用:

协助快速查询、更新数据库表中数据。

为表设置索引要付出代价的:

一是增加了数据库的存储空间

二是在插入和修改数据时要花费较多的时间(因为索引也要随之变动)。

ALTER TABLE people ADD INDEX lname_fname_age (lame,fname,age);21.索引的优缺点有哪些?

创建索引可以大大提高系统的性能(优点):

(1)通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。

(2)可以大大加快数据的检索速度,这也是创建索引的最主要的原因。

(3)可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。

(4)在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。

(5)通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。

增加索引也有许多不利的方面(缺点):

(1).创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。

(2).索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要

建立聚簇索引,那么需要的空间就会更大。

(3).当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护

速度。

(4).哪些列适合建立索引、哪些不适合建索引?

索引是建立在数据库表中的某些列的上面。在创建索引的时候,应该考虑在哪些列上可以创建索引,在

哪些列上不能创建索引。

一般来说,应该在这些列上创建索引:

(1)在经常需要搜索的列上,可以加快搜索的速度;

(2)在作为主键的列上,强制该列的唯一性和组织表中数据的排列结构;

(3)在经常用在连接的列上,这些列主要是一些外键,可以加快连接的速度;

(4)在经常需要根据范围进行搜索的列上创建索引,因为索引已经排序,其指定的范围是连续的;

(5)在经常需要排序的列上创建索引,因为索引已经排序,这样查询可以利用索引的排序,加快排序

查询时间;

(6)在经常使用在WHERE子句中的列上面创建索引,加快条件的判断速度。

对于有些列不应该创建索引:

(1)对于那些在查询中很少使用或者参考的列不应该创建索引。

这是因为,既然这些列很少使用到,因此有索引或者无索引,并不能提高查询速度。相反,由于增加了

索引,反而降低了系统的维护速度和增大了空间需求。

(2)对于那些只有很少数据值的列也不应该增加索引。

这是因为,由于这些列的取值很少,例如人事表的性别列,在查询的结果中,结果集的数据行占了表中

数据行的很大比例,即需要在表中搜索的数据行的比例很大。增加索引,并不能明显加快检索速度。

(3)对于那些定义为text, image和bit数据类型的列不应该增加索引。

这是因为,这些列的数据量要么相当大,要么取值很少。

(4)当修改性能远远大于检索性能时,不应该创建索引。

这是因为,修改性能和检索性能是互相矛盾的。当增加索引时,会提高检索性能,但是会降低修改性

能。当减少索引时,会提高修改性能,降低检索性能。因此,当修改性能远远大于检索性能时,不应该

创建索引。

索引详解:带你从头到尾捋一遍MySQL索引结构!

22.什么样的字段适合建索引?

唯一、不为空、经常被查询的字段

23.说说聚集索引和非聚集索引区别?

聚合索引(clustered index):

聚集索引表记录的排列顺序和索引的排列顺序一致,所以查询效率快,只要找到第一个索引值记录,其余就连续性的记录在物理也一样连续存放。聚集索引对应的缺点就是修改慢,因为为了保证表中记录的物理和索引顺序一致,在记录插入的时候,会对数据页重新排序。聚集索引类似于新华字典中用拼音去查找汉字,拼音检索表于书记顺序都是按照a~z排列的,就像相同

的逻辑顺序于物理顺序一样,当你需要查找a,ai两个读音的字,或是想一次寻找多个傻(sha)的同音字时,也许向后翻几页,或紧接着下一行就得到结果了。

非聚合索引(nonclustered index):

非聚集索引指定了表中记录的逻辑顺序,但是记录的物理和索引不一定一致,两种索引都采用B+树结构,非聚集索引的叶子层并不和实际数据页相重叠,而采用叶子层包含一个指向表中的记录在数据页中的指针方式。非聚集索引层次多,不会造成数据重排。

非聚集索引类似在新华字典上通过偏旁部首来查询汉字,检索表也许是按照横、竖、撇来排列的,但是由于正文中是a~z的拼音顺序,所以就类似于逻辑地址于物理地址的不对应。同时适用的情况就在于分组,大数目的不同值,频繁更新的列中,这些情况即不适合聚集索引。

根本区别:

聚集索引和非聚集索引的根本区别是表记录的排列顺序和与索引的排列顺序是否一致。

二、SqlServer笔试基础篇

试用SQL查询语句表达下列对教学数据库中三个基本表 S、SC 、C 的查询:

S(sno,sname,SAGE,SSEX) 各字段表示学号,姓名,年龄,性别

Sc(sno,cno,grade) 各字段表示学号,课程号,成绩、

C(cno,cname, TEACHER) 各字段表示课程号,课程名和教师名 其 中 SAGE, grade 是数值型,其他均

为字符型。

1.求年龄大于所有女同学年龄的男学生姓名和年龄。

SELECTSNAME,SAGE FROM S AS X

WHERE X.SSEX=' 男'AND X.SAGE >ALL (SELECT SAGE FROMS AS Y WHERE

Y.SSEX=' 女')

2.求年龄大于女同学平均年龄的男学生姓名和年龄。

SELECT SNAME,SAGE

FROM S

WHERE SSEX=' 男'

AND SAGE>(SELECTAVG(SAGE) FROM S WHERE SSEX='女')

3.在 SC 中检索成绩为空值的学生学号和课程号。

SELECT Sno,CnoFROM SC WHERE GRADE IS NULL

4.检索姓名以 WANG 打头的所有学生的姓名和年龄。

SELECT SNAME,SAGE FROM S

WHERE SNAME LIKE 'WANG%'

5.检索学号比 WANG 同学大,而年龄比他小的学生姓名。

SELECTX.SNAME FROM S AS X, S AS Y

WHERE Y .SNAME='WANG' AND X.Sno>Y.Sno AND X.SAGE

 

SELECT SNAME

from s

where sno>(select sno from s where SNAME='WANG') andSAGE<(select sAGE from s

where SNAME='WANG')

6.统计每门课程的学生选修人数 (超过 2 人的课程才统计)。要求输出课程号和选修人数,查询结果按人数降序排列,若人数相同,按课程号升序排列。

SELECTDISTINCT Cno,COUNT(Sno) FROM SC

GROUPBY Cno HAVING COUNT(Sno)>2

ORDER BY 2 DESC, Cno ASC

 

SELECT DISTINCT Cno,COUNT(Sno) as 人数

FROMSC GROUP BY Cno

HAVING COUNT(Sno)>2

ORDER BY 人 数 DESC, Cno ASC

 

7.求 LIU 老师所授课程的每门课程的学生平均成绩。

SELECT AVG(GRADE)

FROM SC join C on SC.Cno=C.Cno WHERE TEACHER='liu'

GROUP BY c.Cno

 

SELECTCNAME,AVG(GRADE) FROM SC ,C WHERE SC.Cno=C.Cno AND TEACHER='liu'

GROUP BY c.Cno,cname

 

8.求选修 C4 课程的学生的平均年龄。

SELECT AVG(SAGE )

FROMS WHERE Sno

IN(SELECT Sno FROM SC WHERE Cno='4')

 

SELECT AVG(SAGE)

FROM S,SC WHERES.Sno=SC.Sno AND Cno='4'

 

9.统计有学生选修的课程门数。

SELECT COUNT(DISTINCT Cno) FROM SC

试用 SQL 更新语句表达对教学数据库中三个基本表 S、

SC 、C的各个更新操作:

10.在基本表 SC 中修改 4 号课程的成绩,若成绩小于等于 75 分时提高 5% , 若成绩大于 75 分时提高 4% (用两个 UPDATE 语句实现)。

UPDATE SC SETGRADE=GRADE*1.05 WHERE Cno='4' AND GRADE<=75

UPDATE SC SET GRADE=GRADE*1.04 WHERE Cno='4' AND GRADE>75

11 .把低于总平均成绩的女同学成绩提高 5% 。

UPDATE SC SETGRADE=GRADE*1.05 WHERE GRADE<(SELECT AVG(GRADE) FROM SC)

AND Sno IN (SELECT Sno FROM SWHERE SSEX=' 女')

12 .把选修数据库原理课不及格的成绩全改为空值。

UPDATE SC SET GRADE=NULL

WHERE GRADE<60 AND Cno IN(SELECT Cno FROM C

WHERE CNAME=' 数据库原理 ')

13.把WANG 同学的学习选课和成绩全部删去。

DELETEFROM SC WHERE Sno IN(SELECT Sno FROM S

WHERE SNAME='WANG')

14 .在基本表 SC 中删除尚无成绩的选课元组。

DELETE FROM SCWHERE GRADE IS NULL

15 .往基本表 S 中插入一个学生元组( ‘ S9’,‘ WU ’,18 )。。

INSERT INTO S(Sno,SNAME,SAGE) VALUES('59','WU',18)

16.什么是SQL注入式攻击?

所谓SQL注入式攻击,就是攻击者把SQL命令插入到Web表单的输入域或页面请求的查询字符串,欺骗服务器执行恶意的SQL命令。在某些表单中,用户输入的内容直接用来构造(或者影响)动态SQL命令,或作为存储过程的输入参数,这类表单特别容易受到SQL注入式攻击。

三、SqlServer笔试高级篇

1.什么是内存泄漏?

一般我们所说的内存泄漏指的是堆内存的泄漏。堆内存是程序从堆中为其分配的,大小任意的,使用完后要显示释放内存。当应用程序用关键字new 等创建对象时,就从堆中为它分配一块内存,使用完后程序调用free 或者delete 释放该内存,否则就说该内存就不能被使用,我们就说该内存被泄漏了。

2.维护数据库的完整性和一致性,你喜欢用触发器还是自写业务逻

辑?为什么?

是这样做的,尽可能使用约束,如check, 主键,外键,非空字段等来约束,这样做效率最高,也最方便。其次是使用触发器,这种方法可以保证,无论什么业务系统访问数据库都可以保证数据的完整新和一致性。最后考虑的是自写业务逻辑,但这样做麻烦,编程复杂,效率低下。

UPDATE SC SETGRADE=GRADE*1.05 WHERE Cno='4' AND GRADE<=75

UPDATE SC SET GRADE=GRADE*1.04 WHERE Cno='4' AND GRADE>75

UPDATE SC SETGRADE=GRADE*1.05 WHERE GRADE<(SELECT AVG(GRADE) FROM SC)

AND Sno IN (SELECT Sno FROM SWHERE SSEX=' 女')

UPDATE SC SET GRADE=NULL

WHERE GRADE<60 AND Cno IN(SELECT Cno FROM C

WHERE CNAME=' 数据库原理 ')

DELETEFROM SC WHERE Sno IN(SELECT Sno FROM S

WHERE SNAME='WANG')

DELETE FROM SCWHERE GRADE IS NULL

INSERT INTO S(Sno,SNAME,SAGE) VALUES('59','WU',18)

3.什么是事务?什么是锁?

事务就是被绑定在一起作为一个逻辑工作单元的SQL 语句分组,如果任何一个语句操作失败那么整个操作就被失败,以后操作就会回滚到操作前状态,或者是上有个节点。为了确保要么执行,要么不执行,

就可以使用事务。要将有组语句作为事务考虑,就需要通过ACID 测试,即原子性,一致性,隔离性和持久性。

锁:在所以的 DBMS中,锁是实现事务的关键,锁可以保证事务的完整性和并发性。与现实生活中锁一 样,它可以使某些数据的拥有者,在某段时间内不能使用某些数据或数据结构。当然锁还分级别的。

4.对一个投入使用的在线事务处理表格有过多索引需要有什么样的性能考虑?

对一个表格的索引越多,数据库引擎用来更新、插入或者删除数据所需要的时间就越多,因为在数据操控发生的时候索引也必须要维护。

5.什么是相关子查询?如何使用这些查询?

相关子查询是一种包含子查询的特殊类型的查询。查询里包含的子查询会真正请求外部查询的值,从而形成一个类似于循环的状况。

6.什么是SQL注入式攻击?

就是攻击者把SQL命令插入到Web表单的输入域或页面请求的查询字符串,欺骗服务器执行恶意的SQL命令。在某些表单中,用户输入的内容直接用来构造(或者影响)动态SQL命令,或作为存储过程的输入参数,这类表单特别容易受到SQL注入式攻击

7.如何防范SQL注入式攻击?

好在要防止ASP.NET应用被SQL注入式攻击闯入并不是一件特别困难的事情,只要在利用表单输入的内容构造SQL命令之前,把所有输入内容过滤一番就可以了。过滤输入内容可以按多种方式进行。

⑴ 对于动态构造SQL查询的场合,可以使用下面的技术:

第一:替换单引号,即把所有单独出现的单引号改成两个单引号,防止攻击者修改SQL命令的含义。

再来看前面的例子,“SELECT * from Users WHERE login = ’’’ or ’’1’’=’’1’ AND password = ’’’ or ’’1’’=’’1’”

显然会得到与“SELECT * from Users WHERE login = ’’ or ’1’=’1’ AND password = ’’ or ’1’=’1’”不同的结

果。

第二:删除用户输入内容中的所有连字符,防止攻击者构造出类如“SELECT * from Users WHERE

login = ’mas’ —— AND password =’’”之类的查询,因为这类查询的后半部分已经被注释掉,不再有效,攻击者只要知道一个合法的用户登录名称,根本不需要知道用户的密码就可以顺利获得访问权限。

第三:对于用来执行查询的数据库帐户,限制其权限。用不同的用户帐户执行查询、插入、更新、删

除操作。由于隔离了不同帐户可执行的操作,因而也就防止了原本用于执行SELECT命令的地方却被用于执行INSERT、UPDATE或DELETE命令。

⑵ 用存储过程来执行所有的查询。SQL参数的传递方式将防止攻击者利用单引号和连字符实施攻击。

此外,它还使得数据库权限可以限制到只允许特定的存储过程执行,所有的用户输入必须遵从被调用的存储过程的安全上下文,这样就很难再发生注入式攻击了。

⑶ 限制表单或查询字符串输入的长度。如果用户的登录名字最多只有10个字符,那么不要认可表单中输入的10个以上的字符,这将大大增加攻击者在SQL命令中插入有害代码的难度。

⑷ 检查用户输入的合法性,确信输入的内容只包含合法的数据。数据检查应当在客户端和服务器端都执行——之所以要执行服务器端验证,是为了弥补客户端验证机制脆弱的安全性。

在客户端,攻击者完全有可能获得网页的源代码,修改验证合法性的脚本(或者直接删除脚本),然后将非法内容通过修改后的表单提交给服务器。因此,要保证验证操作确实已经执行,唯一的办法就是在服务器端也执行验证。你可以使用许多内建的验证对象,例如RegularExpressionValidator,它们能够自动生成验证用的客户端脚本,当然你也可以插入服务器端的方法调用。如果找不到现成的验证对象,你可以通过CustomValidator自己创建一个。

⑸ 将用户登录名称、密码等数据加密保存。加密用户输入的数据,然后再将它与数据库中保存的数据比较,这相当于对用户输入

的数据进行了“消毒”处理,用户输入的数据不再对数据库有任何特殊的意义,从而也就防止了攻击者注入SQL命令。System.Web.Security.FormsAuthentication类有一个

HashPasswordForStoringInConfigFile,非常适合于对输入数据进行消毒处理。

⑹ 检查提取数据的查询所返回的记录数量。如果程序只要求返回一个记录,但实际返回的记录却超过一行,那就当作出错处理。

(7)使用预处理语句

8.你可以用什么来确保表格里的字段只接受特定范围里的值?

master 主要保存系统级的信息,比如本数据库实例都有哪些数据库 ,都有哪些账号等,需备份;

model 模板,每创建一个数据库 ,都会根据这个库的结构来创建,如果改过此库,建议备份;

msdb 保存计划任务,作业之类的信息,需备份,否则会丢失作业和备份计划;

tempdb 用户对sqlserver操作时产生的临时数据依赖于此库,最常见的是临时表,不许备份;

9.有哪些操作会使用到TempDB;如果TempDB异常变大,可能的原

因是什么,该如何处理;

每个sqlserver运行时所产生的临时数据都会用到tempdb,最常见的是执行sql脚本需要返回的记录集;异常变大的原因是执行的操作返回的记录集过大造成,找出该语句优化,减少数据范围,或者分批操作这些数据

10.Index有哪些类型,它们的区别和实现原理是什么,索引有啥优点和缺点;如何为SQL语句创建合适的索引,索引创建时有哪些需要,注意的项,如何查看你创建的索引是否被使用;如何维护索引;索引损坏如何检查,怎么修复;T-SQL有更好的索引存在,但是运行,时并没有使用该索引,原因可能是什么;

聚集索引,非聚集索引;聚集索引只能有一个,非聚集可有多个,数据依赖于聚集索引来保存,如果没有聚集索引,数据是一个乱序的堆;

优点:合适的索引可有效提高查询效率;缺点:过多的索引,在insert、update 和 delete 的时候增加索引的维护成本,降低并发量;

一般索引的创建要依赖于 where 和 order by 这两个关键字,执行计划可以看出是否用到了索引;

还没遇到过索引损坏的情况,如果损坏,重建之;

用不到索引可能是索引碎片过多 ,可进行碎片整理,若不行可加强制索引with(index( 索引名 ))

11.Job信息我们可以通过哪些表获取;系统正在运行的语句可以通过哪些视图获取;如何获取某个T-SQL语句的IO、Time等信息;

sql2000下是通过 master.dbo.sysjobs 来查看作业信息;系统正在运行的语句可通过

master.dbo.sysprocesses 结合 dbcc inputbuffer 来查看,IO,在sql2000下我本人都是通过profiler

看reads,duration,sql2005下有了动态视图(dmv);

 

 

SQL Server优化的方法<一>

查询速度慢的原因很多,常见如下几种:

  1、没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷)

  2、I/O吞吐量小,形成了瓶颈效应。

  3、没有创建计算列导致查询不优化。

  4、内存不足

  5、网络速度慢

  6、查询出的数据量过大(可以采用多次查询,其他的方法降低数据量)

  7、锁或者死锁(这也是查询慢最常见的问题,是程序设计的缺陷)

  8、sp_lock,sp_who,活动的用户查看,原因是读写竞争资源。

  9、返回了不必要的行和列

  10、查询语句不好,没有优化

可以通过如下方法来优化查询 :

  1、把数据、日志、索引放到不同的I/O设备上,增加读取速度,以前可以将Tempdb应放在RAID0上,SQL2000不在支持。数据量(尺寸)越大,提高I/O越重要.

  2、纵向、横向分割表,减少表的尺寸(sp_spaceuse)

  3、升级硬件

  4、根据查询条件,建立索引,优化索引、优化访问方式,限制结果集的数据量。注意填充因子要适当(最好是使用默认值0)。索引应该尽量小,使用字节数小的列建索引好(参照索引的创建),不要对有限的几个值的字段建单一索引如性别字段

  5、提高网速;

  6、扩大服务器的内存,Windows 2000和SQL server 2000能支持4-8G的内存。配置虚拟内存:虚拟内存大小应基于计算机上并发运行的服务进行配置。运行 Microsoft SQL Server? 2000 时,可考虑将虚拟内存大小设置为计算机中安装的物理内存的 1.5 倍。如果另外安装了全文检索功能,并打算运行 Microsoft 搜索服务以便执行全文索引和查询,可考虑:将虚拟内存大小配置为至少是计算机中安装的物理内存的 3 倍。将 SQL Server max server memory 服务器配置选项配置为物理内存的 1.5 倍(虚拟内存大小设置的一半)。

  7、增加服务器CPU个数;但是必须明白并行处理串行处理更需要资源例如内存。使用并行还是串行程是MsSQL自动评估选择的。单个任务分解成多个任务,就可以在处理器上运行。例如耽搁查询的排序、连接、扫描和GROUP BY字句同时执行,SQL SERVER根据系统的负载情况决定最优的并行等级,复杂的需要消耗大量的CPU的查询最适合并行处理。但是更新操作UPDATE,INSERT,DELETE还不能并行处理。

  8、如果是使用like进行查询的话,简单的使用index是不行的,但是全文索引耗空间。 like ‘a%’ 使用索引 like ‘%a’ 不使用索引用 like ‘%a%’ 查询时,查询耗时和字段值总长度成正比,所以不能用CHAR类型,而是VARCHAR。对于字段的值很长的建全文索引。

  9、DB Server 和APPLication Server 分离;OLTP和OLAP分离;

  10、分布式分区视图可用于实现数据库服务器联合体。联合体是一组分开管理的服务器,但它们相互协作分担系统的处理负荷。这种通过分区数据形成数据库服务器联合体的机制能够扩大一组服务器,以支持大型的多层 Web 站点的处理需要。

    a、在实现分区视图之前,必须先水平分区表

    b、在创建成员表后,在每个成员服务器上定义一个分布式分区视图,并且每个视图具有相同的名称。这样,引用分布式分区视图名的查询可以在任何一个成员服务器上运行。系统操作如同每个成员服务器上都有一个原始表的复本一样,但其实每个服务器上只有一个成员表和一个分布式分区视图。数据的位置对应用程序是透明的。

  11、重建索引 DBCC REINDEX ,DBCC INDEXDEFRAG     收缩数据和日志 DBCC SHRINKDB,DBCC SHRINKFILE.     设置自动收缩日志.     对于大的数据库不要设置数据库自动增长,它会降低服务器的性能。     在T-sql的写法上有很大的讲究,下面列出常见的要点:首先,DBMS处理查询计划的过程是这样的:

1、 查询语句的词法、语法检查

2、 将语句提交给DBMS的查询优化器

3、 优化器做代数优化和存取路径的优化

4、 由预编译模块生成查询规划

5、 然后在合适的时间提交给系统处理执行

6、 最后将执行结果返回给用户.
其次看一下SQL SERVER的数据存放的结构:一个页面的大小为8K(8060)字节,8个页面为一个盘区,按照B树存放。
复制

  12、Commit和rollback的区别:     Rollback:回滚所有的事务。     Commit:提交当前的事务。     没有必要在动态SQL里写事务,如果要写请写在外面如: begin tran exec(@s) commit trans 或者将动态SQL 写成函数或者存储过程。

  13、在查询Select语句中用Where字句限制返回的行数,避免表扫描,如果返回不必要的数据,浪费了服务器的I/O资源,加重了网络的负担降低性能。如果表很大,在表扫描的期间将表锁住,禁止其他的联接访问表,后果严重。

  14、SQL的注释申明对执行没有任何影响

  15、尽可能不使用光标,它占用大量的资源。如果需要row-by-row地执行,尽量采用非光标技术,如:在客户端循环,用临时表,Table变量,用子查询,用Case语句等等。

  16、用Profiler来跟踪查询,得到查询所需的时间,找出SQL的问题所在;用索引优化器优化索引

  17、注意UNion和UNion all 的区别,UNION all好

  18、注意使用DISTINCT,在没有必要时不要用,它同UNION一样会使查询变慢,重复的记录在查询里是没有问题的;

  19、查询时不要返回不需要的行、列;

  20、用sp_configure 'query governor cost limit’或者SET QUERY_GOVERNOR_COST_LIMIT来限制查询消耗的资源。当评估查询消耗的资源超出限制时,服务器自动取消查询,在查询之前就扼杀掉。SET LOCKTIME设置锁的时间;

  21、用select top 100 / 10 Percent 来限制用户返回的行数或者SET ROWCOUNT来限制操作的行;

  22、在SQL2000以前,一般不要用如下的字句: “IS NULL”, “<>”, “!=”, “!>”, “!<”, “NOT”, “NOT EXISTS”, “NOT IN”, “NOT LIKE”, and “LIKE ‘%500’”,因为他们不走索引全是表扫描。也不要在WHere字句中的列名加函数,如Convert,substring等,如果必须用函数的时候,创建计算列再创建索引来替代.还可以变通写法:WHERE SUBSTRING(firstname,1,1) = 'm’改为WHERE firstname like ‘m%’(索引扫描),一定要将函数和列名分开。并且索引不能建得太多和太大。NOT IN会多次扫描表,使用EXISTS、NOT EXISTS ,IN , LEFT OUTER JOIN 来替代,特别是左连接,而Exists比IN更快,最慢的是NOT操作.如果列的值含有空,以前它的索引不起作用,现在2000的优化器能够处理了。相同的是IS NULL,“NOT”, “NOT EXISTS”, “NOT IN"能优化她,而”<>"等还是不能优化,用不到索引。

  23、使用Query Analyzer,查看SQL语句的查询计划和评估分析是否是优化的SQL。一般的20%的代码占据了80%的资源,我们优化的重点是这些慢的地方。

  24、如果使用了IN或者OR等时发现查询没有走索引,使用显示申明指定索引: SELECT * FROM PersonMember (INDEX = IX_Title) WHERE processid IN (‘男’,‘女’)

  25、将需要查询的结果预先计算好放在表中,查询的时候再SELECT。这在SQL7.0以前是最重要的手段。例如医院的住院费计算。

  26、MIN() 和 MAX()能使用到合适的索引。

  27、数据库有一个原则是代码离数据越近越好,所以优先选择Default,依次为Rules,Triggers, Constraint(约束如外健主健CheckUNIQUE……,数据类型的最大长度等等都是约束),Procedure.这样不仅维护工作小,编写程序质量高,并且执行的速度快。

  28、如果要插入大的二进制值到Image列,使用存储过程,千万不要用内嵌INsert来插入(不知JAVA是否)。因为这样应用程序首先将二进制值转换成字符串(尺寸是它的两倍),服务器受到字符后又将他转换成二进制值.存储过程就没有这些动作: 方法:Create procedure p_insert as insert into table(Fimage) values (@image), 在前台调用这个存储过程传入二进制参数,这样处理速度明显改善。

  29、Between在某些时候比IN速度更快,Between能够更快地根据索引找到范围。用查询优化器可见到差别。 select * from chineseresume where title in (‘男’,‘女’) Select * from chineseresume where between ‘男’ and ‘女’ 是一样的。由于in会在比较多次,所以有时会慢些。

  30、在必要是对全局或者局部临时表创建索引,有时能够提高速度,但不是一定会这样,因为索引也耗费大量的资源。他的创建同是实际表一样。

  31、不要建没有作用的事务,例如产生报表时会浪费资源,只有在必要使用事物时使用它。

  32、用OR的字句可以分解成多个查询,并且通过UNION 连接多个查询。他们的速度只同是否使用索引有关,如果查询需要用到联合索引,用UNION all执行的效率更高.多个OR的字句没有用到索引,改写成UNION的形式再试图与索引匹配。一个关键的问题是否用到索引。

  33、尽量少用视图,它的效率低。对视图操作比直接对表操作慢,可以用stored procedure来代替她。特别的是不要用视图嵌套,嵌套视图增加了寻找原始资料的难度。我们看视图的本质:它是存放在服务器上的被优化好了的已经产生了查询规划的SQL。对单个表检索数据时,不要使用指向多个表的视图,直接从表检索或者仅仅包含这个表的视图上读,否则增加了不必要的开销,查询受到干扰.为了加快视图的查询,MsSQL增加了视图索引的功能。

  34、没有必要时不要用DISTINCT和ORDER BY,这些动作可以改在客户端执行。它们增加了额外的开销。这同UNION 和UNION ALL一样的道理。

  35、在IN后面值的列表中,将出现最频繁的值放在最前面,出现得最少的放在最后面,减少判断的次数。

  36、当用SELECT INTO时,它会锁住系统表(sysobjects,sysindexes等等),阻塞其他的连接的存取。创建临时表时用显示申明语句,而不是

-- select INTO. drop table t_lxh begin tran select * into t_lxh from chineseresume where name = 'XYZ'
-- commit
复制

SELECT INTO 会锁住系统表,所以千万不要在事物内使用它!!!这样的话如果是经常要用的临时表请使用实表或者临时表变量。

  37、一般在GROUP BY 和HAVING子句之前就能剔除多余的行,所以尽量不要用它们来做剔除行的工作。他们的执行顺序应该如下最优:select 的Where字句选择所有合适的行,Group By用来分组和统计行,Having字句用来剔除多余的分组。这样Group By 个Having的开销小,查询快.对于大的数据行进行分组和Having十分消耗资源。如果Group BY的目的不包括计算,只是分组,那么用Distinct更快.

  38、一次更新多条记录比分多次更新每次一条快,就是说批处理好

  39、少用临时表,尽量用结果集和Table类性的变量来代替它,Table 类型的变量比临时表好;

  40、在SQL2000下,计算字段是可以索引的,需要满足的条件如下:

a、计算字段的表达是确定的

b、不能用在TEXT,Ntext,Image数据类型

c、必须配制如下选项 ANSI_NULLS = ON, ANSI_PADDINGS = ON, …….
复制

  41、尽量将数据的处理工作放在服务器上,减少网络的开销,如使用存储过程。存储过程是编译好、优化过、并且被组织到一个执行规划里、且存储在数据库中的SQL语句,是控制流语言的集合,速度当然快。反复执行的动态SQL,可以使用临时存储过程,该过程(临时表)被放在Tempdb中。以前由于SQL SERVER对复杂的数学计算不支持,所以不得不将这个工作放在其他的层上而增加网络的开销。SQL2000支持UDFs,现在支持复杂的数学计算,函数的返回值不要太大,这样的开销很大。用户自定义函数象光标一样执行的消耗大量的资源,如果返回大的结果采用存储过程

  42、不要在一句话里再三的使用相同的函数,浪费资源,将结果放在变量里再调用更快

  43、SELECT COUNT(*)的效率教低,尽量变通他的写法,而EXISTS快.同时请注意区别: select count(Field of null) from Table 和 select count(Field of NOT null) from Table 的返回值是不同的!!!

  44、当服务器的内存够多时,配制线程数量 = 最大连接数+5,这样能发挥最大的效率;否则使用 配制线程数量<最大连接数启用SQL SERVER的线程池来解决,如果还是数量 = 最大连接数+5,严重的损害服务器的性能。

  45、按照一定的次序来访问你的表。如果你先锁住表A,再锁住表B,那么在所有的存储过程中都要按照这个顺序来锁定它们。如果你(不经意的)某个存储过程中先锁定表B,再锁定表A,这可能就会导致一个死锁。如果锁定顺序没有被预先详细的设计好,死锁很难被发现

  46、通过SQL Server Performance Monitor监视相应硬件的负载 Memory: Page Faults / sec计数器如果该值偶尔走高,表明当时有线程竞争内存。如果持续很高,则内存可能是瓶颈。

  47、分析select emp_name form employee where salary > 3000 在此语句中若salary是Float类型的,则优化器对其进行优化为Convert(float,3000),因为3000是个整数,我们应在编程时使用3000.0而不要等运行时让DBMS进行转化。同样字符和整型数据的转换。

  48、关于JOBCN现在查询分页的新方法(如下),用性能优化器分析性能的瓶颈,如果在I/O或者网络的速度上,如下的方法优化切实有效,如果在CPU或者内存上,用现在的方法更好。请区分如下的方法,说明索引越小越好。

begin

DECLARE @local_variable table (FID int identity(1,1),ReferenceID varchar(20))

insert into @local_variable (ReferenceID)

select top 100000 ReferenceID from chineseresume order by ReferenceID

select * from @local_variable where Fid > 40 and fid <= 60

end 和

begin

DECLARE @local_variable table (FID int identity(1,1),ReferenceID varchar(20))

insert into @local_variable (ReferenceID)

select top 100000 ReferenceID from chineseresume order by updatedate

select * from @local_variable where Fid > 40 and fid <= 60

end 的不同

begin

create table #temp (FID int identity(1,1),ReferenceID varchar(20))

insert into #temp (ReferenceID)

select top 100000 ReferenceID from chineseresume order by updatedate

select * from #temp where Fid > 40 and fid <= 60 drop table #temp

end

复制

 

SQL Server优化的方法<二>

  一个系统的性能的提高,不单单是试运行或者维护阶段的性能调优的任务,也不单单是开发阶段的事情,而是在整个软件生命周期都需要注意,进行有效工作才能达到的。所以我希望按照软件生命周期的不同阶段来总结数据库性能优化相关的注意事项。

一、 分析阶段

  一般来说,在系统分析阶段往往有太多需要关注的地方,系统各种功能性、可用性、可靠性、安全性需求往往吸引了我们大部分的注意力,但是,我们必须注意,性能是很重要的非功能性需求,必须根据系统的特点确定其实时性需求、响应时间的需求、硬件的配置等。最好能有各种需求的量化的指标。另一方面,在分析阶段应该根据各种需求区分出系统的类型,大的方面,区分是OLTP(联机事务处理系统)和OLAP(联机分析处理系统)。

二、 设计阶段

  设计阶段可以说是以后系统性能的关键阶段,在这个阶段,有一个关系到以后几乎所有性能调优的过程—数据库设计。

  在数据库设计完成后,可以进行初步的索引设计,好的索引设计可以指导编码阶段写出高效率的代码,为整个系统的性能打下良好的基础。

以下是性能要求设计阶段需要注意的:

1、 数据库逻辑设计的规范化

  数据库逻辑设计的规范化就是我们一般所说的范式,我们可以这样来简单理解范式:

    第1规范:没有重复的组或多值的列,这是数据库设计的最低要求。

    第2规范: 每个非关键字段必须依赖于主关键字,不能依赖于一个组合式主关键字的某些组成部分。消除部分依赖,大部分情况下,数据库设计都应该达到第二范式。

    第3规范: 一个非关键字段不能依赖于另一个非关键字段。消除传递依赖,达到第三范式应该是系统中大部分表的要求,除非一些特殊作用的表。

    更高的范式要求这里就不再作介绍了,个人认为,如果全部达到第二范式,大部分达到第三范式,系统会产生较少的列和较多的表,因而减少了数据冗余,也利于性能的提高。

2、 合理的冗余

  完全按照规范化设计的系统几乎是不可能的,除非系统特别的小,在规范化设计后,有计划地加入冗余是必要的。

  冗余可以是冗余数据库、冗余表或者冗余字段,不同粒度的冗余可以起到不同的作用。

  冗余可以是为了编程方便而增加,也可以是为了性能的提高而增加。从性能角度来说,冗余数据库可以分散数据库压力,冗余表可以分散数据量大的表的并发压力,也可以加快特殊查询的速度,冗余字段可以有效减少数据库表的连接,提高效率。

3、 主键的设计

  主键是必要的,SQL SERVER的主键同时是一个唯一索引,而且在实际应用中,我们往往选择最小的键组合作为主键,所以主键往往适合作为表的聚集索引。聚集索引对查询的影响是比较大的,这个在下面索引的叙述。

  在有多个键的表,主键的选择也比较重要,一般选择总的长度小的键,小的键的比较速度快,同时小的键可以使主键的B树结构的层次更少。

  主键的选择还要注意组合主键的字段次序,对于组合主键来说,不同的字段次序的主键的性能差别可能会很大,一般应该选择重复率低、单独或者组合查询可能性大的字段放在前面。

4、 外键的设计

  外键作为数据库对象,很多人认为麻烦而不用,实际上,外键在大部分情况下是很有用的,理由是:

  外键是最高效的一致性维护方法,数据库的一致性要求,依次可以用外键、CHECK约束、规则约束、触发器、客户端程序,一般认为,离数据越近的方法效率越高。

  谨慎使用级联删除和级联更新,级联删除和级联更新作为SQL SERVER 2000当年的新功能,在2005作了保留,应该有其可用之处。我这里说的谨慎,是因为级联删除和级联更新有些突破了传统的关于外键的定义,功能有点太过强大,使用前必须确定自己已经把握好其功能范围,否则,级联删除和级联更新可能让你的数据莫名其妙的被修改或者丢失。从性能看级联删除和级联更新是比其他方法更高效的方法。

5、 字段的设计

  字段是数据库最基本的单位,其设计对性能的影响是很大的。需要注意如下:

    A、数据类型尽量用数字型,数字型的比较比字符型的快很多。

    B、 数据类型尽量小,这里的尽量小是指在满足可以预见的未来需求的前提下的。

    C、 尽量不要允许NULL,除非必要,可以用NOT NULL+DEFAULT代替。

    D、少用TEXT和IMAGE,二进制字段的读写是比较慢的,而且,读取的方法也不多,大部分情况下最好不用。

    E、 自增字段要慎用,不利于数据迁移。

6、 数据库物理存储和环境的设计

  在设计阶段,可以对数据库的物理存储、操作系统环境、网络环境进行必要的设计,使得我们的系统在将来能适应比较多的用户并发和比较大的数据量。

  这里需要注意文件组的作用,适用文件组可以有效把I/O操作分散到不同的物理硬盘,提高并发能力。

7、 系统设计

  整个系统的设计特别是系统结构设计对性能是有很大影响的,对于一般的OLTP系统,可以选择C/S结构、三层的C/S结构等,不同的系统结构其性能的关键也有所不同。

  系统设计阶段应该归纳一些业务逻辑放在数据库编程实现,数据库编程包括数据库存储过程、触发器和函数。用数据库编程实现业务逻辑的好处是减少网络流量并可更充分利用数据库的预编译和缓存功能。

8、 索引的设计

  在设计阶段,可以根据功能和性能的需求进行初步的索引设计,这里需要根据预计的数据量和查询来设计索引,可能与将来实际使用的时候会有所区别。

  关于索引的选择,应改主意:

    A、 根据数据量决定哪些表需要增加索引,数据量小的可以只有主键。

    B、 根据使用频率决定哪些字段需要建立索引,选择经常作为连接条件、筛选条件、聚合查询、排序的字段作为索引的候选字段。

  C、 把经常一起出现的字段组合在一起,组成组合索引,组合索引的字段顺序与主键一样,也需要把最常用的字段放在前面,把重复率低的字段放在前面。

  D、 一个表不要加太多索引,因为索引影响插入和更新的速度。

三、 编码阶段

  编码阶段是本文的重点,因为在设计确定的情况下,编码的质量几乎决定了整个系统的质量。

  编码阶段首先是需要所有程序员有性能意识,也就是在实现功能同时有考虑性能的思想,数据库是能进行集合运算的工具,我们应该尽量的利用这个工具,所谓集合运算实际是批量运算,就是尽量减少在客户端进行大数据量的循环操作,而用SQL语句或者存储过程代替。关于思想和意识,很难说得很清楚,需要在编程过程中来体会。

  下面罗列一些编程阶段需要注意的事项:

1、 只返回需要的数据

  返回数据到客户端至少需要数据库提取数据、网络传输数据、客户端接收数据以及客户端处理数据等环节,如果返回不需要的数据,就会增加服务器、网络和客户端的无效劳动,其害处是显而易见的,避免这类事件需要注意:

    A、横向来看,不要写SELECT *的语句,而是选择你需要的字段。

    B、 纵向来看,合理写WHERE子句,不要写没有WHERE的SQL语句。

    C、 注意SELECT INTO后的WHERE子句,因为SELECT INTO把数据插入到临时表,这个过程会锁定一些系统表,如果这个WHERE子句返回的数据过多或者速度太慢,会造成系统表长期锁定,诸塞其他进程。

    D、对于聚合查询,可以用HAVING子句进一步限定返回的行。

2、 尽量少做重复的工作

  这一点和上一点的目的是一样的,就是尽量减少无效工作,但是这一点的侧重点在客户端程序,需要注意的如下:

    A、 控制同一语句的多次执行,特别是一些基础数据的多次执行是很多程序员很少注意的。

    B、 减少多次的数据转换,也许需要数据转换是设计的问题,但是减少次数是程序员可以做到的。

    C、 杜绝不必要的子查询和连接表,子查询在执行计划一般解释成外连接,多余的连接表带来额外的开销。

    D、 合并对同一表同一条件的多次UPDATE,比如

UPDATE EMPLOYEE SET FNAME=’HAIWER’ WHERE EMP_ID=’ VPA30890F’

UPDATE EMPLOYEE SET LNAME=’YANG’ WHERE EMP_ID=’ VPA30890F’
复制

这两个语句应该合并成以下一个语句

UPDATE EMPLOYEE SET FNAME=’HAIWER’,LNAME=’YANG’

WHERE EMP_ID=’ VPA30890F’
复制

    E、 UPDATE操作不要拆成DELETE操作+INSERT操作的形式,虽然功能相同,但是性能差别是很大的。

    F、 不要写一些没有意义的查询,比如

SELECT * FROM EMPLOYEE WHERE 1=2
复制

3、 注意事务和锁

  事务是数据库应用中和重要的工具,它有原子性、一致性、隔离性、持久性这四个属性,很多操作我们都需要利用事务来保证数据的正确性。在使用事务中我们需要做到尽量避免死锁、尽量减少阻塞。具体以下方面需要特别注意:

    A、事务操作过程要尽量小,能拆分的事务要拆分开来。

    B、 事务操作过程不应该有交互,因为交互等待的时候,事务并未结束,可能锁定了很多资源。

    C、 事务操作过程要按同一顺序访问对象。

    D、提高事务中每个语句的效率,利用索引和其他方法提高每个语句的效率可以有效地减少整个事务的执行时间。

    E、 尽量不要指定锁类型和索引,SQL SERVER允许我们自己指定语句使用的锁类型和索引,但是一般情况下,SQL SERVER优化器选择的锁类型和索引是在当前数据量和查询条件下是最优的,我们指定的可能只是在目前情况下更有,但是数据量和数据分布在将来是会变化的。

    F、 查询时可以用较低的隔离级别,特别是报表查询的时候,可以选择最低的隔离级别(未提交读)。

4、 注意临时表和表变量的用法

  在复杂系统中,临时表和表变量很难避免,关于临时表和表变量的用法,需要注意:

    A、如果语句很复杂,连接太多,可以考虑用临时表和表变量分步完成。

    B、 如果需要多次用到一个大表的同一部分数据,考虑用临时表和表变量暂存这部分数据。

    C、 如果需要综合多个表的数据,形成一个结果,可以考虑用临时表和表变量分步汇总这多个表的数据。

    D、其他情况下,应该控制临时表和表变量的使用。

    E、 关于临时表和表变量的选择,很多说法是表变量在内存,速度快,应该首选表变量,但是在实际使用中发现,这个选择主要考虑需要放在临时表的数据量,在数据量较多的情况下,临时表的速度反而更快。

    F、 关于临时表产生使用SELECT INTO和CREATE TABLE + INSERT INTO的选择,我们做过测试,一般情况下,SELECT INTO会比CREATE TABLE + INSERT INTO的方法快很多,但是SELECT INTO会锁定TEMPDB的系统表SYSOBJECTS、SYSINDEXES、SYSCOLUMNS,在多用户并发环境下,容易阻塞其他进程,所以我的建议是,在并发系统中,尽量使用CREATE TABLE + INSERT INTO,而大数据量的单个语句使用中,使用SELECT INTO。

    G、 注意排序规则,用CREATE TABLE建立的临时表,如果不指定字段的排序规则,会选择TEMPDB的默认排序规则,而不是当前数据库的排序规则。如果当前数据库的排序规则和TEMPDB的排序规则不同,连接的时候就会出现排序规则的冲突错误。一般可以在CREATE TABLE建立临时表时指定字段的排序规则为DATABASE_DEFAULT来避免上述问题。

5、 子查询的用法

    子查询是一个 SELECT 查询,它嵌套在 SELECT、INSERT、UPDATE、DELETE 语句或其它子查询中。任何允许使用表达式的地方都可以使用子查询。

    子查询可以使我们的编程灵活多样,可以用来实现一些特殊的功能。但是在性能上,往往一个不合适的子查询用法会形成一个性能瓶颈。

    如果子查询的条件中使用了其外层的表的字段,这种子查询就叫作相关子查询。相关子查询可以用IN、NOT IN、EXISTS、NOT EXISTS引入。

  关于相关子查询,应该注意:

  A、NOT IN、NOT EXISTS的相关子查询可以改用LEFT JOIN代替写法。比如:

SELECT PUB_NAME

FROM PUBLISHERS

WHERE PUB_ID NOT IN

(SELECT PUB_ID

FROM TITLES

WHERE TYPE = 'BUSINESS')
复制

可以改写成:

SELECT A.PUB_NAME

FROM PUBLISHERS A LEFT JOIN TITLES B

ON B.TYPE = 'BUSINESS' AND

A.PUB_ID=B. PUB_ID

WHERE B.PUB_ID IS NULL
复制

.

SELECT TITLE

FROM TITLES

WHERE NOT EXISTS

(SELECT TITLE_ID

FROM SALES

WHERE TITLE_ID = TITLES.TITLE_ID)
复制

可以改写成:

SELECT TITLE

FROM TITLES LEFT JOIN SALES

ON SALES.TITLE_ID = TITLES.TITLE_ID

WHERE SALES.TITLE_ID IS NULL
复制

  B、 如果保证子查询没有重复 ,IN、EXISTS的相关子查询可以用INNER JOIN 代替。比如:

SELECT PUB_NAME

FROM PUBLISHERS

WHERE PUB_ID IN

(SELECT PUB_ID

FROM TITLES

WHERE TYPE = 'BUSINESS')
复制

可以改写成:

SELECT DISTINCT A.PUB_NAME

FROM PUBLISHERS A INNER JOIN TITLES B

ON B.TYPE = 'BUSINESS' AND

A.PUB_ID=B. PUB_ID
复制

  C、 IN的相关子查询用EXISTS代替,比如

SELECT PUB_NAME

FROM PUBLISHERS

WHERE PUB_ID IN

(SELECT PUB_ID

FROM TITLES

WHERE TYPE = 'BUSINESS')
复制

可以用下面语句代替:

SELECT PUB_NAME

FROM PUBLISHERS

WHERE EXISTS

(SELECT 1

FROM TITLES

WHERE TYPE = 'BUSINESS' AND

PUB_ID= PUBLISHERS.PUB_ID)
复制

  D、不要用COUNT(*)的子查询判断是否存在记录,最好用LEFT JOIN或者EXISTS,比如有人写这样的语句:

SELECT JOB_DESC FROM JOBS

WHERE (SELECT COUNT(*) FROM EMPLOYEE WHERE JOB_ID=JOBS.JOB_ID)=0
复制

应该改成:

SELECT JOBS.JOB_DESC FROM JOBS LEFT JOIN EMPLOYEE

ON EMPLOYEE.JOB_ID=JOBS.JOB_ID

WHERE EMPLOYEE.EMP_ID IS NULL
复制

.

SELECT JOB_DESC FROM JOBS

WHERE (SELECT COUNT(*) FROM EMPLOYEE WHERE JOB_ID=JOBS.JOB_ID)<>0
复制

应该改成:

SELECT JOB_DESC FROM JOBS

WHERE EXISTS (SELECT 1 FROM EMPLOYEE WHERE JOB_ID=JOBS.JOB_ID)
复制

6、 慎用游标

  数据库一般的操作是集合操作,也就是对由WHERE子句和选择列确定的结果集作集合操作,游标是提供的一个非集合操作的途径。一般情况下,游标实现的功能往往相当于客户端的一个循环实现的功能,所以,大部分情况下,我们把游标功能搬到客户端。

  游标是把结果集放在服务器内存,并通过循环一条一条处理记录,对数据库资源(特别是内存和锁资源)的消耗是非常大的,所以,我们应该只有在没有其他方法的情况下才使用游标。

  另外,我们可以用SQL SERVER的一些特性来代替游标,达到提高速度的目的。

  A、字符串连接的例子

  这是论坛经常有的例子,就是把一个表符合条件的记录的某个字符串字段连接成一个变量。比如需要把JOB_ID=10的EMPLOYEE的FNAME连接在一起,用逗号连接,可能最容易想到的是用游标:

DECLARE @NAME VARCHAR(20)

DECLARE @NAME VARCHAR(1000)

DECLARE NAME_CURSOR CURSOR FOR

SELECT FNAME FROM EMPLOYEE WHERE JOB_ID=10 ORDER BY EMP_ID

OPEN NAME_CURSOR

FETCH NEXT FROM RNAME_CURSOR INTO @NAME

WHILE @@FETCH_STATUS = 0

BEGIN

SET @NAMES = ISNULL(@NAMES+’,’,’’)+@NAME

FETCH NEXT FROM NAME_CURSOR INTO @NAME

END

CLOSE NAME_CURSOR

DEALLOCATE NAME_CURSOR

复制

可以如下修改,功能相同:

DECLARE @NAME VARCHAR(1000)

SELECT @NAMES = ISNULL(@NAMES+’,’,’’)+FNAME

FROM EMPLOYEE WHERE JOB_ID=10 ORDER BY EMP_ID
复制

  B、 用CASE WHEN 实现转换的例子

  很多使用游标的原因是因为有些处理需要根据记录的各种情况需要作不同的处理,实际上这种情况,我们可以用CASE WHEN语句进行必要的判断处理,而且CASE WHEN是可以嵌套的。比如:

表结构:

CREATE TABLE 料件表(

料号 VARCHAR(30),

名称 VARCHAR(100),

主单位 VARCHAR(20),

单位1 VARCHAR(20),

单位1参数 NUMERIC(18,4),

单位2 VARCHAR(20),

单位2参数 NUMERIC(18,4)

)

GO

CREATE TABLE 入库表(

时间 DATETIME,

料号 VARCHAR(30),

单位 INT,

入库数量 NUMERIC(18,4),

损坏数量 NUMERIC(18,4)

)

GO

复制

  其中,单位字段可以是0,1,2,分别代表主单位、单位1、单位2,很多计算需要统一单位,统一单位可以用游标实现:

DECLARE @料号 VARCHAR(30),

@单位 INT,

@参数 NUMERIC(18,4),

DECLARE CUR CURSOR FOR

SELECT 料号,单位 FROM 入库表 WHERE 单位 <>0

OPEN CUR

FETCH NEXT FROM CUR INTO @料号,@单位

WHILE @@FETCH_STATUS<>-1

BEGIN

IF @单位=1

BEGIN

SET @参数=(SELECT 单位1参数 FROM 料件表 WHERE 料号 =@料号)

UPDATE 入库表 SET 数量=数量*@参数,损坏数量=损坏数量*@参数,单位=1 WHERE CURRENT OF CUR

END

IF @单位=2

BEGIN

SET @参数=(SELECT 单位1参数 FROM 料件表 WHERE 料号 =@料号)

UPDATE 入库表 SET 数量=数量*@参数,损坏数量=损坏数量*@参数,单位=1 WHERE CURRENT OF CUR

END

FETCH NEXT FROM CUR INTO @料号,@单位

END

CLOSE CUR

DEALLOCATE CUR

复制

可以改写成:

UPDATE A SET

数量=CASE A.单位 WHEN 1 THEN A.数量*B. 单位1参数
WHEN 2 THEN A.数量*B. 单位2参数

ELSE A.数量

END,

损坏数量= CASE A.单位 WHEN 1 THEN A. 损坏数量*B. 单位1参数

WHEN 2 THEN A. 损坏数量*B. 单位2参数

ELSE A. 损坏数量

END,

单位=1

FROM入库表 A, 料件表 B

WHERE A.单位<>1 AND

A.料号=B.料号

复制

  C、 变量参与的UPDATE语句的例子

  SQL ERVER的语句比较灵活,变量参与的UPDATE语句可以实现一些游标一样的功能,比如:

SELECT A,B,C,CAST(NULL AS INT) AS 序号

INTO #T

FROM 表

ORDER BY A ,NEWID()

产生临时表后,已经按照A字段排序,但是在A相同的情况下是乱序的,这时如果需要更改序号字段为按照A字段分组的记录序号,就只有游标和变量参与的UPDATE语句可以实现了,这个变量参与的UPDATE语句如下:

DECLARE @A INT

DECLARE @序号 INT

UPDATE #T SET

@序号=CASE WHEN A=@A THEN @序号+1 ELSE 1 END,

@A=A,

序号=@序号

D、如果必须使用游标,注意选择游标的类型,如果只是循环取数据,那就应该用只进游标(选项FAST_FORWARD),一般只需要静态游标(选项STATIC)。

E、 注意动态游标的不确定性,动态游标查询的记录集数据如果被修改,会自动刷新游标,这样使得动态游标有了不确定性,因为在多用户环境下,如果其他进程或者本身更改了纪录,就可能刷新游标的记录集。

7、 尽量使用索引

建立索引后,并不是每个查询都会使用索引,在使用索引的情况下,索引的使用效率也会有很大的差别。只要我们在查询语句中没有强制指定索引,索引的选择和使用方法是SQLSERVER的优化器自动作的选择,而它选择的根据是查询语句的条件以及相关表的统计信息,这就要求我们在写SQL语句的时候尽量使得优化器可以使用索引。

为了使得优化器能高效使用索引,写语句的时候应该注意:

A、不要对索引字段进行运算,而要想办法做变换,比如

SELECT ID FROM T WHERE NUM/2=100

应改为:

SELECT ID FROM T WHERE NUM=100*2

SELECT ID FROM T WHERE NUM/2=NUM1

如果NUM有索引应改为:

SELECT ID FROM T WHERE NUM=NUM1*2

如果NUM1有索引则不应该改。

发现过这样的语句:

SELECT 年,月,金额 FROM 结余表

WHERE 100年+月=2007100+10

应该改为:

SELECT 年,月,金额 FROM 结余表

WHERE 年=2007 AND

月=10

B、 不要对索引字段进行格式转换

日期字段的例子:

WHERE CONVERT(VARCHAR(10), 日期字段,120)=’2008-08-15’

应该改为

WHERE日期字段〉=’2008-08-15’ AND 日期字段<’2008-08-16’

ISNULL转换的例子:

WHERE ISNULL(字段,’’)<>’’应改为:WHERE字段<>’’

WHERE ISNULL(字段,’’)=’’不应修改

WHERE ISNULL(字段,’F’) =’T’应改为: WHERE字段=’T’

WHERE ISNULL(字段,’F’)<>’T’不应修改

C、 不要对索引字段使用函数

WHERE LEFT(NAME, 3)=‘ABC’ 或者WHERE SUBSTRING(NAME,1, 3)=‘ABC’

应改为:

WHERE NAME LIKE ‘ABC%’

日期查询的例子:

WHERE DATEDIFF(DAY, 日期,‘2005-11-30’)=0应改为:WHERE 日期 >=‘2005-11-30’ AND 日期 <'2005-12-1‘

WHERE DATEDIFF(DAY, 日期,‘2005-11-30’)>0应改为:WHERE 日期 <'2005-11-30‘

WHERE DATEDIFF(DAY, 日期,‘2005-11-30’)>=0应改为:WHERE 日期 <'2005-12-01‘

WHERE DATEDIFF(DAY, 日期,‘2005-11-30’)<0应改为:WHERE 日期>='2005-12-01‘

WHERE DATEDIFF(DAY, 日期,‘2005-11-30’)<=0应改为:WHERE 日期>='2005-11-30‘

D、不要对索引字段进行多字段连接

比如:

WHERE FAME+ ’.’+LNAME=‘HAIWEI.YANG’

应改为:

WHERE FNAME=‘HAIWEI’ AND LNAME=‘YANG’

8、 注意连接条件的写法

  多表连接的连接条件对索引的选择有着重要的意义,所以我们在写连接条件条件的时候需要特别的注意。

  A、多表连接的时候,连接条件必须写全,宁可重复,不要缺漏。

  B、 连接条件尽量使用聚集索引

  C、 注意ON部分条件和WHERE部分条件的区别

9、 其他需要注意的地方

  经验表明,问题发现的越早解决的成本越低,很多性能问题可以在编码阶段就发现,为了提早发现性能问题,需要注意:

  A、程序员注意、关心各表的数据量。

  B、 编码过程和单元测试过程尽量用数据量较大的数据库测试,最好能用实际数据测试。

  C、 每个SQL语句尽量简单

  D、不要频繁更新有触发器的表的数据

  E、 注意数据库函数的限制以及其性能

10、 学会分辩SQL语句的优劣

  自己分辨SQL语句的优劣非常重要,只有自己能分辨优劣才能写出高效的语句。

  A、 查看SQL语句的执行计划,可以在查询分析其使用CTRL+L图形化的显示执行计划,一般应该注意百分比最大的几个图形的属性,把鼠标移动到其上面会显示这个图形的属性,需要注意预计成本的数据,也要注意其标题,一般都是CLUSTERED INDEX SEEK 、INDEX SEEK 、CLUSTERED INDEX SCAN 、INDEX SCAN 、TABLE SCAN等,其中出现SCAN说明语句有优化的余地。也可以用语句

SET SHOWPLAN_ALL ON
复制

要执行的语句

SET SHOWPLAN_ALL OFF
复制

查看执行计划的文本详细信息。

  B、 用事件探查器跟踪系统的运行,可疑跟踪到执行的语句,以及所用的时间,CPU用量以及I/O数据,从而分析语句的效率。

  C、 可以用WINDOWS的系统性能检测器,关注CPU、I/O参数

四、 测试、试运行、维护阶段

  测试的主要任务是发现并修改系统的问题,其中性能问题也是一个重要的方面。重点应该放在发现有性能问题的地方,并进行必要的优化。主要进行语句优化、索引优化等。

  试运行和维护阶段是在实际的环境下运行系统,发现的问题范围更广,可能涉及操作系统、网络以及多用户并发环境出现的问题,其优化也扩展到操作系统、网络以及数据库物理存储的优化。

  这个阶段的优花方法在这里不再展开,只说明下索引维护的方法:

  A、 可以用DBCC DBREINDEX语句或者SQL SERVER维护计划设定定时进行索引重建,索引重建的目的是提高索引的效能。

  B、 可以用语句UPDATE STATISTICS或者SQL SERVER维护计划设定定时进行索引统计信息的更新,其目的是使得统计信息更能反映实际情况,从而使得优化器选择更合适的索引。

  C、 可以用DBCC CHECKDB或者DBCC CHECKTABLE语句检查数据库表和索引是否有问题,这两个语句也能修复一般的问题。

五、 网上资料中一些说法的个人不同意见

  1、 “应尽量避免在 WHERE 子句中对字段进行 NULL 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:

SELECT ID FROM T WHERE NUM IS NULL
复制

  可以在NUM上设置默认值0,确保表中NUM列没有NULL值,然后这样查询:

SELECT ID FROM T WHERE NUM=0”
复制

  个人意见:经过测试,IS NULL也是可以用INDEX SEEK查找的,0和NULL是不同概念的,以上说法的两个查询的意义和记录数是不同的。

  2、 “应尽量避免在 WHERE 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。”

  个人意见:经过测试,<>也是可以用INDEX SEEK查找的。

  3、 “应尽量避免在 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”
复制

  4、 “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”
复制

  5、 “如果在 WHERE 子句中使用参数,也会导致全表扫描。因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:

SELECT ID FROM T WHERE NUM=@NUM
复制

可以改为强制查询使用索引:

SELECT ID FROM T WITH(INDEX(索引名)) WHERE NUM=@NUM”
复制

  6、 “尽可能的使用 VARCHAR/NVARCHAR 代替 CHAR/NCHAR ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。”

  7、 关于连接表的顺序或者条件的顺序的说法,经过测试,在SQL SERVER,这些顺序都是不影响性能的,这些说法可能是对ORACLE有效。

 

SQL Server优化的方法<三>

  关于索引的常识:影响到数据库性能的最大因素就是索引。由于该问题的复杂性,我只可能简单的谈谈这个问题,不过关于这方面的问题,目前有好几本不错的书籍可供你参阅。我在这里只讨论两种SQL Server索引,即clustered索引和nonclustered索引。当考察建立什么类型的索引时,你应当考虑数据类型和保存这些数据的column。同样,你也必须考虑数据库可能用到的查询类型以及使用的最为频繁的查询类型。

索引的类型

  如果column保存了高度相关的数据,并且常常被顺序访问时,最好使用clustered索引,这是因为如果使用clustered索引,SQL Server会在物理上按升序(默认)或者降序重排数据列,这样就可以迅速的找到被查询的数据。同样,在搜寻控制在一定范围内的情况下,对这些column也最好使用clustered索引。这是因为由于物理上重排数据,每个表格上只有一个clustered索引。

  与上面情况相反,如果columns包含的数据相关性较差,你可以使用nonculstered索引。你可以在一个表格中使用高达249个nonclustered索引——尽管我想象不出实际应用场合会用的上这么多索引。

  当表格使用主关键字(primary keys),默认情况下SQL Server会自动对包含该关键字的column(s)建立一个独有的cluster索引。很显然,对这些column(s)建立独有索引意味着主关键字的唯一性。当建立外关键字(foreign key)关系时,如果你打算频繁使用它,那么在外关键字cloumn上建立nonclustered索引不失为一个好的方法。如果表格有clustered索引,那么它用一个链表来维护数据页之间的关系。相反,如果表格没有clustered索引,SQL Server将在一个堆栈中保存数据页。

数据页

  当索引建立起来的时候,SQLServer就建立数据页(datapage),数据页是用以加速搜索的指针。当索引建立起来的时候,其对应的填充因子也即被设置。设置填充因子的目的是为了指示该索引中数据页的百分比。随着时间的推移,数据库的更新会消耗掉已有的空闲空间,这就会导致页被拆分。页拆分的后果是降低了索引的性能,因而使用该索引的查询会导致数据存储的支离破碎。当建立一个索引时,该索引的填充因子即被设置好了,因此填充因子不能动态维护。

  为了更新数据页中的填充因子,我们可以停止旧有索引并重建索引,并重新设置填充因子(注意:这将影响到当前数据库的运行,在重要场合请谨慎使用)。DBCC INDEXDEFRAG和DBCC DBREINDEX是清除clustered和nonculstered索引碎片的两个命令。INDEXDEFRAG是一种在线操作(也就是说,它不会阻塞其它表格动作,如查询),而DBREINDEX则在物理上重建索引。在绝大多数情况下,重建索引可以更好的消除碎片,但是这个优点是以阻塞当前发生在该索引所在表格上其它动作为代价换取来得。当出现较大的碎片索引时,INDEXDEFRAG会花上一段比较长的时间,这是因为该命令的运行是基于小的交互块(transactional block)。

填充因子

  当你执行上述措施中的任何一个,数据库引擎可以更有效的返回编入索引的数据。关于填充因子(fillfactor)话题已经超出了本文的范畴,不过我还是提醒你需要注意那些打算使用填充因子建立索引的表格。

  在执行查询时,SQL Server动态选择使用哪个索引。为此,SQL Server根据每个索引上分布在该关键字上的统计量来决定使用哪个索引。值得注意的是,经过日常的数据库活动(如插入、删除和更新表格),SQL Server用到的这些统计量可能已经“过期”了,需要更新。你可以通过执行DBCC SHOWCONTIG来查看统计量的状态。当你认为统计量已经“过期”时,你可以执行该表格的UPDATE STATISTICS命令,这样SQL Server就刷新了关于该索引的信息了。

建立数据库维护计划

  SQL Server提供了一种简化并自动维护数据库的工具。这个称之为数据库维护计划向导(Database Maintenance Plan Wizard ,DMPW)的工具也包括了对索引的优化。如果你运行这个向导,你会看到关于数据库中关于索引的统计量,这些统计量作为日志工作并定时更新,这样就减轻了手工重建索引所带来的工作量。如果你不想自动定期刷新索引统计量,你还可以在DMPW中选择重新组织数据和数据页,这将停止旧有索引并按特定的填充因子重建索引。

posted on 2024-02-01 00:47  drewwestlhq2  阅读(17)  评论(0)    收藏  举报

导航