The NoteBook of EricKong

  BlogJava :: 首页 :: 联系 :: 聚合  :: 管理
  611 Posts :: 1 Stories :: 190 Comments :: 0 Trackbacks

#


1.Hashtable和HashMap有什么区别?
  a.Hashtable是继承自陈旧的Dictionary类的,HashMap继承自AbstractMap类同时是Java 1.2引进的Map接口的一个实现。
  b.也许最重要的不同是Hashtable的方法是同步的,而HashMap的方法不是。这就意味着,然你可以不用采取任何特殊的行为就可以在一个 多线程的应用程序中用一个Hashtable,但你必须同样地为一个HashMap提供外同步。一个方便的方法就是利用Collections类的静态的synchronizedMap()方法,它创建一个线程安全的Map对象,并把它作为一个封装的对象来返回。这个对象的方法可以让你同步访问潜在的HashMap。这么做的结果就是当你不需要同步时,你不能切断Hashtable中的同步(比如在一个单线程的应用程序中),而且同步增加了很多处理费用。
  c.第三点不同是,只有HashMap可以让你将空值作为一个表的条目的key或value。HashMap中只有一条记录可以是一个空的key,但任意数量的条目可以是空的value。这就是说,如果在表中没有发现搜索键,或者如果发现了搜索键,但它是一个空的值,那么get()将返回null。如果有必要,用containKey()方法来区别这两种情况。
  d.HashMap去掉了Hashtable的contains方法,保留了containsValue和containsKey方法
  e.Hashtable中hash数组默认大小是11,增加的方式是 old*2+1。HashMap中hash数组的默认大小是16,而且一定是2的指数
HashTable的应用非常广泛,HashMap是新框架中用来代替HashTable的类,也就是说建议使用HashMap,不要使用HashTable。可能你觉得HashTable很好用,为什么不用呢?这里简单分析他们的区别。

1.HashTable的方法是同步的,HashMap未经同步,所以在多线程场合要手动同步HashMap这个区别就像Vector和ArrayList一样。

2.HashTable不允许null值(key和value都不可以),HashMap允许null值(key和value都可以)。

3.HashTable有一个contains(Object value),功能和containsValue(Object value)功能一样。

4.HashTable使用Enumeration,HashMap使用Iterator。 以上只是表面的不同,它们的实现也有很大的不同。

5.HashTable中hash数组默认大小是11,增加的方式是 old*2+1。HashMap中hash数组的默认大小是16,而且一定是2的指数。

6.哈希值的使用不同,HashTable直接使用对象的hashCode,代码是这样的:

int hash = key.hashCode();

int index = (hash & 0x7FFFFFFF) % tab.length;

而HashMap重新计算hash值,而且用与代替求模:

int hash = hash(k);
int i = indexFor(hash, table.length);

static int hash(Object x) {
   int h = x.hashCode();
   h += ~(h << 9);
   h ^= (h >>> 14);
   h += (h << 4);
   h ^= (h >>> 10);
   return h;
}


static int indexFor(int h, int length) {
   return h & (length-1);
}
以上只是一些比较突出的区别,当然他们的实现上还是有很多不同的,比如HashMap对null的操作。
Hashtable和HashMap的区别:
1.Hashtable是Dictionary的子类,HashMap是Map接口的一个实现类;
2.Hashtable中的方法是同步的,而HashMap中的方法在缺省情况下是非同步的。即是说,在多线程应用程序中,不用专门的操作就安全地可以使用Hashtable了;而对于HashMap,则需要额外的同步机制。但HashMap的同步问题可通过Collections的一个静态方法得到解决:
Map Collections.synchronizedMap(Map m)
这个方法返回一个同步的Map,这个Map封装了底层的HashMap的所有方法,使得底层的HashMap即使是在多线程的环境中也是安全的。
3.在HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。当get()方法返回null值时,即可以表示HashMap中没有该键,也可以表示该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键,而应该用containsKey()方法来判断。

Hashtable继承自Dictionary类,而HashMap是Java1.2引进的Map interface的一个实现

HashMap允许将null作为一个entry的key或者value,而Hashtable不允许

还有就是,HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因为contains方法容易让人引起误解。

最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在
多个线程访问Hashtable时,不需要自己为它的方法实现同步,而HashMap
就必须为之提供外同步。

Hashtable和HashMap采用的hash/rehash算法都大概一样,所以性能不会有很大的差异。

posted @ 2010-08-26 21:32 Eric_jiang 阅读(2106) | 评论 (0)编辑 收藏

    如何让你的SQL运行得更快 [推荐!!!]
人们在使用SQL时往往会陷入一个误区,即太关注于所得的结果是否正确,而忽略了不同的实现方法之间可能存在的性能差异,这种性能差异在大型的或是复杂的数据库
环境中(如联机事务处理OLTP或决策支持系统DSS)中表现得尤为明显。笔者在工作实践中发现,不良的SQL往往来自于不恰当的索引设计、不充份的连接条件和不可优化的where子句。在对它们进行适当的优化后,其运行速度有了明显地提高!下面我将从这三个

方面分别进行总结:

---- 为了更直观地说明问题,所有实例中的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,302)。

三、不可优化的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次数,尽量避免表搜索的发生。其实SQL的性能优化是一个复杂的过程,上述这些只是在应用层次的一种体现,深入研究还会

涉及数据库层的资源配置、网络层的流量控制以及操作系统层的总体设计。

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,所以查询工作量可以得到大幅减少。

注意:临时表创建后不会反映主表的修改。在主表中数据频繁修改的情况下,注意不要丢失数据。



7.用排序来取代非顺序存取

   非顺序磁盘存取是最慢的操作,表现在磁盘存取臂的来回移动。SQL语句隐藏了这一情况,使得我们在写应用程序时很容易写出要求存取大量非顺序页的查询。

有些时候,用数据库的排序能力来替代非顺序的存取能改进查询。



3.优化 tempdb 性能

对 tempdb 数据库的物理位置和数据库选项设置的一般建议包括:

使 tempdb 数据库得以按需自动扩展。这确保在执行完成前不终止查询,该查询所生成的存储在 tempdb 数据库内的中间结果集比预期大得多。将 tempdb 数据库文件的初始大小设置为合理的大小,以避免当需要更多空间时文件自动扩展。如果 tempdb 数据库扩展得过于频繁,性能会受不良影响。将文件增长增量百分比设置为合理的大小,以避免 tempdb 数据库文件按太小的值增长。如果文件增长幅度与写入 tempdb 数据库的数据量相比太小,则 tempdb 数据库可能需要始终扩展,因而将妨害性能。将 tempdb 数据库放在快速 I/O 子系统上以确保好的性能。在多个磁盘上条带化 tempdb 数据库以获得更好的性能。将 tempdb 数据库放在除用户数据库所使用的磁盘之外的磁盘上。有关更多信息,请参见扩充数据库。


4.优化服务器:



使用内存配置选项优化服务器性能

    Microsoft&reg; SQL Server™ 2000 的内存管理组件消除了对 SQL Server 可用的内存进行手工管理的需要。SQL Server 在启动时根据操作系统和其它应用程序当前正在使用的内存量,动态确定应分配的内存量。当计算机和SQL Server 上的负荷更改时,分配的内存也随之更改。有关更多信息,请参见内存构架。



下列服务器配置选项可用于配置内存使用并影响服务器性能:

min server memory

max server memory

max worker threads

index create memory



min memory per query

    min server memory 服务器配置选项可用于确保 SQL Server 在达到该值后不会释放内存。可以基于 SQL Server 的大小及活动将该配置选项设置为特定的值。如果选择设置此选项,必须为操作系统和其他程序留出足够的内存。如果操作系统没有足够的内存,会向 SQL Server 请求内存,从而导致影响 SQL Server 性能。
    max server memory 服务器配置选项可用于:在 SQL Server 启动及运行时,指定 SQL Server 可以分配的最大内存量。如果知道有多个应用程序与 SQL Server 同时运行,而且想保障这些应用程序有足够的内存运行,可以将该配置选项设置为特定的值。如果这些其它应用程序(如 Web 服务器或电子邮件服务器)只根据需要请求内存,则 SQL Server 将根据需要给它们释放内存,因此不要设置 max server memory 服务器配置选项。然而,应用程序通常在启动时不假选择地使用可用内存,而如果需要更多内存也不请求。如果有这种行为方式的应用程序与 SQL Server 同时运行在相同的计算机上,则将 max server memory 服务器配置选项设置为特定的值,以保障应用程序所需的内存不由 SQL Server 分配出。
   不要将 min server memory 和 max server memory 服务器配置选项设置为相同的值,这样做会使分配给 SQL Server 的内存量固定。动态内存分配可以随时间提供最佳的总体性能。有关更多信息,请参见服务器内存选项。
    max worker threads 服务器配置选项可用于指定为用户连接到 SQL Server 提供支持的线程数。255 这一默认设置对一些配置可能稍微偏高,这要具体取决于并发用户数。由于每个工作线程都已分配,因此即使线程没有正在使用(因为并发连接比分配的工作线程少),可由其它操作(如高速缓冲存储器)更好地利用的内存资源也可能是未使用的。一般情况下,应将该配置值设置为并发连接数,但不能超过 32727。并发连接与用户登录连接不同。SQL Server 实例的工作线程池只需要足够大,以便为同时正在该实例中执行批处理的用户连接提供服务。如果增加工作线程的数量超过默认值,会降低服务器性能。有关更多信息,请参见max worker threads 选项。

说明 当 SQL Server 运行在 Microsoft Windows98 上时,最大工作线程服务器配置选项不起作用。
    index create memory 服务器配置选项控制创建索引时排序操作所使用的内存量。在生产系统上创建索引通常是不常执行的任务,通常调度为在非峰值时间执行的作业。因此,不常创建索引且在非峰值时间时,增加该值可提高索引创建的性能。不过,最好将 min memory per query 配置选项保持在一个较低的值,这样即使所有请求的内存都不可用,索引创建作业仍能开始。有关更多信息,请参见 index create memory 选项。
    min memory per query 服务器配置选项可用于指定分配给查询执行的最小内存量。当系统内有许多查询并发执行时,增大 min memory per query 的值有助于提高消耗大量内存的查询(如大型排序和哈希操作)的性能。不过,不要将 min memory per query 服务器配置选项设置得太高,尤其是在很忙的系统上,因为查询将不得不等到能确保占有请求的最小内存、或等到超过 query wait 服务器配置选项内所指定的值。如果可用内存比执行查询所需的指定最小内存多,则只要查询能对多出的内存加以有效的利用,就可以使用多出的内存。有关更多信息,请参见 min memory per query 选项和 query wait 选项。



使用 I/O 配置选项优化服务器性能

下列服务器配置选项可用于配置 I/O 的使用并影响服务器性能:



recovery interval
    recovery interval 服务器配置选项控制 Microsoft&reg; SQL Server™ 2000 在每个数据库内发出检查点的时间。默认情况下,SQL Server 确定执行检查点操作的最佳时间。然而,若要确定这是否为适当的设置,需要使用 Windows NT 性能监视器监视数据库文件上的磁盘写入活动。导致磁盘利用率达到 100% 的活动尖峰值会妨害性能。若更改该参数以使检查点进程较少出现,通常可以提高这种情况下的总体性能。但仍须继续监视性能以确定新值是否已对性能产生正面影响。有关更多信息,请参见recovery interval 选项。
5.优化数据库文件

分区
    将数据库分区可提高其性能并易于维护。通过将一个大表拆分成更小的单个表,只访问一小部分数据的查询可以执行得更快,因为需要扫描的数据较少。而且可以更快地执行维护任务(如重建索引或备份表)。
    实现分区操作时可以不拆分表,而将表物理地放置在个别的磁盘驱动器上。例如,将表放在某个物理驱动器上并将相关的表放在与之分离的驱动器上可提高查询性能,因为当执行涉及表之间联接的查询时,多个磁头同时读取数据。可以使用 Microsoft&reg; SQL Server™ 2000 文件组指定将表放置在哪些磁盘上。

硬件分区
    硬件分区将数据库设计为利用可用的硬件构架。硬件分区的示例包括:
    允许多线程执行的多处理器,使得可以同时执行许多查询。换句话说,在多处理器上可以同时执行查询的各个组件,因此使单个查询的速度更快。例如,查询内引用的每个表可同时由不同的线程扫描。
    RAID(独立磁盘冗余阵列)设备允许数据在多个磁盘驱动器中条带化,使更多的读/写磁头同时读取数据,因此可以更快地访问数据。在多个驱动器中条带化的表一般比存储在一个驱动器上的相同的表扫描速度要快。换句话说,将表与相关的表分开存储在不同的驱动器上可以显著提高联接那些表的查询的性能。

水平分区
    水平分区将一个表分段为多个表,每个表包含相同数目的列和较少的行。例如,可以将一个包含十亿行的表水平分区成 12 个表,每个小表代表特定年份内一个月的数据。任何需要特定月份数据的查询只引用相应月份的表。
    具体如何将表进行水平分区取决于如何分析数据。将表进行分区是为了使查询引用尽可能少的表。否则,查询时须使用过多的 UNION 查询来逻辑合并表,而这会削弱查询性能。有关查询水平分区的表的更多信息,请参见视图使用方案。
    常用的方法是根据时期/使用对数据进行水平分区。例如,一个表可能包含最近五年的数据,但是只定期访问本年度的数据。在这种情况下,可考虑将数据分区成五个表,每个表只包含一年的数据。

垂直分区
    垂直分区将一个表分段为多个表,每个表包含较少的列。垂直分区的两种类型是规范化和行拆分。
规范化是个标准数据库进程,该进程从表中删除冗余列并将其放到次表中,次表按主键与外键的关系链接到主表。
    行拆分将原始表垂直分成多个只包含较少列的表。拆分的表内的每个逻辑行与其它表内的相同逻辑行匹配。例如,联接每个拆分的表内的第十行将重新创建原始行。
    与水平分区一样,垂直分区使查询得以扫描较少的数据,因此提高查询性能。例如有一个包含七列的表,通常只引用该表的前四列,那么将该表的后三列拆分到一个单独的表中可获得性能收益。
   应谨慎考虑垂直分区操作,因为分析多个分区内的数据需要有联接表的查询,而如果分区非常大将可能影响性能。



(一)深入浅出理解索引结构
     实际上,您可以把索引理解为一种特殊的目录。微软的SQL SERVER提供了两种索引:聚集索引(clustered index,也称聚类索引、簇集索引)和非聚集索引(nonclustered index,也称非聚类索引、非簇集索引)。下面,我们举例来说明一下聚集索引和非聚集索引的区别:
    其实,我们的汉语字典的正文本身就是一个聚集索引。比如,我们要查“安”字,就会很自然地翻开字典的前几页,因为“安”的拼音是“an”,而按照拼音排序汉字的字典是以英文字母“a”开头并以“z”结尾的,那么“安”字就自然地排在字典的前部。如果您翻完了所有以“a”开头的部分仍然找不到这个字,那么就说明您的字典中没有这个字;同样的,如果查“张”字,那您也会将您的字典翻到最后部分,因为“张”的拼音是“zhang”。也就是说,字典的正文部分本身就是一个目录,您不需要再去查其他目录来找到您需要找的内容。
我们把这种正文内容本身就是一种按照一定规则排列的目录称为“聚集索引”。
    如果您认识某个字,您可以快速地从自动中查到这个字。但您也可能会遇到您不认识的字,不知道它的发音,这时候,您就不能按照刚才的方法找到您要查的字,而需要去根据“偏旁部首”查到您要找的字,然后根据这个字后的页码直接翻到某页来找到您要找的字。但您结合“部首目录”和“检字表”而查到的字的排序并不是真正的正文的排序方法,比如您查“张”字,我们可以看到在查部首之后的检字表中“张”的页码是672页,检字表中“张”的上面是“驰”字,但页码却是63页,“张”的下面是“弩”字,页面是390页。很显然,这些字并不是真正的分别位于“张”字的上下方,现在您看到的连续的“驰、张、弩”三字实际上就是他们在非聚集索引中的排序,是字典正文中的字在非聚集索引中的映射。我们可以通过这种方式来找到您所需要的字,但它需要两个过程,先找到目录中的结果,然后再翻到您所需要的页码。



我们把这种目录纯粹是目录,正文纯粹是正文的排序方式称为“非聚集索引”。
通过以上例子,我们可以理解到什么是“聚集索引”和“非聚集索引”。



进一步引申一下,我们可以很容易的理解:每个表只能有一个聚集索引,因为目录只能按照一种方法进行排序。



(二)何时使用聚集索引或非聚集索引

  事实上,我们可以通过前面聚集索引和非聚集索引的定义的例子来理解上表。如:返回某范围内的数据一项。比如您的某个表有一个时间列,恰好您把聚合索引建立在了该列,这时您查询2004年1月1日至2004年10月1日之间的全部数据时,这个速度就将是很快的,因为您的这本字典正文是按日期进行排序的,聚类索引只需要找到要检索的所有数据中的开头和结尾数据即可;而不像非聚集索引,必须先查到目录中查到每一项数据对应的页码,然后再根据页码查到具体内容。



(三)结合实际,谈索引使用的误区
    理论的目的是应用。虽然我们刚才列出了何时应使用聚集索引或非聚集索引,但在实践中以上规则却很容易被忽视或不能根据实际情况进行综合分析。下面我们将根据在实践中遇到的实际问题来谈一下索引使用的误区,以便于大家掌握索引建立的方法。
1、主键就是聚集索引
    这种想法笔者认为是极端错误的,是对聚集索引的一种浪费。虽然SQL SERVER默认是在主键上建立聚集索引的。
    通常,我们会在每个表中都建立一个ID列,以区分每条数据,并且这个ID列是自动增大的,步长一般为1。我们的这个办公自动化的实例中的列Gid就是如此。此时,如果我们将这个列设为主键,SQL SERVER会将此列默认为聚集索引。这样做有好处,就是可以让您的数据在数据库中按照ID进行物理排序,但笔者认为这样做意义不大。
    显而易见,聚集索引的优势是很明显的,而每个表中只能有一个聚集索引的规则,这使得聚集索引变得更加珍贵。
    从我们前面谈到的聚集索引的定义我们可以看出,使用聚集索引的最大好处就是能够根据查询要求,迅速缩小查询范围,避免全表扫描。在实际应用中,因为ID号是自动生成的,我们并不知道每条记录的ID号,所以我们很难在实践中用ID号来进行查询。这就使让ID号这个主键作为聚集索引成为一种资源浪费。其次,让每个ID号都不同的字段作为聚集索引也不符合“大数目的不同值情况下不应建立聚合索引”规则;当然,这种情况只是针对用户经常修改记录内容,特别是索引项的时候会负作用,但对于查询速度并没有影响。
    在办公自动化系统中,无论是系统首页显示的需要用户签收的文件、会议还是用户进行文件查询等任何情况下进行数据查询都离不开字段的是“日期”还有用户本身的“用户名”。
    通常,办公自动化的首页会显示每个用户尚未签收的文件或会议。虽然我们的where语句可以仅仅限制当前用户尚未签收的情况,但如果您的系统已建立了很长时间,并且数据量很大,那么,每次每个用户打开首页的时候都进行一次全表扫描,这样做意义是不大的,绝大多数的用户1个月前的文件都已经浏览过了,这样做只能徒增数据库的开销而已。事实上,我们完全可以让用户打开系统首页时,数据库仅仅查询这个用户近3个月来未阅览的文件,通过“日期”这个字段来限制表扫描,提高查询速度。如果您的办公自动化系统已经建立的2年,那么您的首页显示速度理论上将是原来速度8倍,甚至更快。
    在这里之所以提到“理论上”三字,是因为如果您的聚集索引还是盲目地建在ID这个主键上时,您的查询速度是没有这么高的,即使您在“日期”这个字段上建立的索引(非聚合索引)。下面我们就来看一下在1000万条数据量的情况下各种查询的速度表现(3个月内的数据为25万条):



(1)仅在主键上建立聚集索引,并且不划分时间段:

Select gid,fariqi,neibuyonghu,title from tgongwen
用时:128470毫秒(即:128秒)

(2)在主键上建立聚集索引,在fariq上建立非聚集索引:
select gid,fariqi,neibuyonghu,title from Tgongwen
where fariqi> dateadd(day,-90,getdate())
用时:53763毫秒(54秒)

(3)将聚合索引建立在日期列(fariqi)上:
select gid,fariqi,neibuyonghu,title from Tgongwen
where fariqi> dateadd(day,-90,getdate())
用时:2423毫秒(2秒)

    虽然每条语句提取出来的都是25万条数据,各种情况的差异却是巨大的,特别是将聚集索引建立在日期列时的差异。事实上,如果您的数据库真的有1000万容量的话,把主键建立在ID列上,就像以上的第1、2种情况,在网页上的表现就是超时,根本就无法显示。这也是我摒弃ID列作为聚集索引的一个最重要的因素。

得出以上速度的方法是:在各个select语句前加:declare @d datetime
set @d=getdate()
并在select语句后加:
select [语句执行花费时间(毫秒)]=datediff(ms,@d,getdate())



2、只要建立索引就能显著提高查询速度
    事实上,我们可以发现上面的例子中,第2、3条语句完全相同,且建立索引的字段也相同;不同的仅是前者在fariqi字段上建立的是非聚合索引,后者在此字段上建立的是聚合索引,但查询速度却有着天壤之别。所以,并非是在任何字段上简单地建立索引就能提高查询速度。
    从建表的语句中,我们可以看到这个有着1000万数据的表中fariqi字段有5003个不同记录。在此字段上建立聚合索引是再合适不过了。在现实中,我们每天都会发几个文件,这几个文件的发文日期就相同,这完全符合建立聚集索引要求的:“既不能绝大多数都相同,又不能只有极少数相同”的规则。由此看来,我们建立“适当”的聚合索引对于我们提高查询速度是非常重要的。



3、把所有需要提高查询速度的字段都加进聚集索引,以提高查询速度
    上面已经谈到:在进行数据查询时都离不开字段的是“日期”还有用户本身的“用户名”。既然这两个字段都是如此的重要,我们可以把他们合并起来,建立一个复合索引(compound index)。
    很多人认为只要把任何字段加进聚集索引,就能提高查询速度,也有人感到迷惑:如果把复合的聚集索引字段分开查询,那么查询速度会减慢吗?带着这个问题,我们来看一下以下的查询速度(结果集都是25万条数据):(日期列fariqi首先排在复合聚集索引的起始列,用户名neibuyonghu排在后列)

(1)select gid,fariqi,neibuyonghu,title from Tgongwen where fariqi>'2004-5-5'

查询速度:2513毫秒

(2)select gid,fariqi,neibuyonghu,title from Tgongwen where fariqi>'2004-5-5' and neibuyonghu='办公室'

查询速度:2516毫秒

(3)select gid,fariqi,neibuyonghu,title from Tgongwen where neibuyonghu='办公室'

查询速度:60280毫秒

    从以上试验中,我们可以看到如果仅用聚集索引的起始列作为查询条件和同时用到复合聚集索引的全部列的查询速度是几乎一样的,甚至比用上全部的复合索引列还要略快(在查询结果集数目一样的情况下);而如果仅用复合聚集索引的非起始列作为查询条件的话,这个索引是不起任何作用的。当然,语句1、2的查询速度一样是因为查询的条目数一样,如果复合索引的所有列都用上,而且查询结果少的话,这样就会形成“索引覆盖”,因而性能可以达到最优。同时,请记住:无论您是否经常使用聚合索引的其他列,但其前导列一定要是使用最频繁的列
posted @ 2010-08-19 17:03 Eric_jiang 阅读(815) | 评论 (0)编辑 收藏

# 基数:度量在数据集中可以存在多少个唯一值。
# 密度:度量在数据集中唯一值的个数。密度通过如下方法得到:给定键值的行数除以数据表的总行数。优化器将忽略高密度的索引。
# 选择率:度量对于一个特定的查询将返回查询结果的行数。选择率通过如下方法得到:查询关键字的个数除以查询得到的行数。要计算查询规划的相对成本,优化器需要一个有效的选择率来度量。

    随着列中数据的变化,索引和列统计信息就变得没有用处了,这样将导致优化器在决定如何处理查询时达不到最优性能。因此,根据数据表中数据的变化,SQL服务器系统周期的自动更新这些统计信息。通过对这些数据的采样,这种统计信息的自动更新将使得成本降到最低,而且不需要对全部数据进行分析。

最佳性能
    在一个复杂的数据库表中设计并指定索引是一件非常棘手的任务。幸运的是,SQL服务器系统有一个内置的调节向导来帮助你建立最优的统计和索引集合。要提高数据库的查询性能,可以通过运行向导来提供一个基于脚本的建议列表。

    对于SQL服务器查询优化器如何工作这一部分懂得越多,你就会知道对于特定的情形为什么只能用向导的建议来实现。但是,对于动态系统来说,最佳的数据库性能分析部分将需要进行周期性地更新。理解查询索引性能中的每个统计度量的真正含义将有助于你在管理决策方面有一个良好的知识基础。

特别说明:
    在微软的SQL服务器系统中,对数据库查询功能进行适当的优化需要懂得一些基本的查询索引和性能统计方面的知识。熟悉该系统的优化工作是如何实现的将有助于提高决策的正确性。

    随着你对微软的SQL服务器数据库实现的逐渐熟悉,性能优化的需求也将进一步增加。建立一个真正实现最优查询功能的数据库环境的第一步是要懂得SQL服务器系统的优化器是如何工作的。

索引
    虽然对于特定的查询来说,进行查询规划和性能优化可能只需要少量的成本估算与比较,也可以没有成本估算与比较,但是大多数的查询将从实现完全优化的工作中受益。提高查询性能的最有效的方法之一就是创建一个高效率的索引。一个构架良好的索引在执行查询工作的时候可以避免出现扫描整个数据表的情况

    在创建索引的时候,SQL服务器系统将自动度量和存储那些与索引列相关的分布状态值相对应的统计信息。这些统计信息常常被优化器用来评估查询的优化策略是否合理。

有两种类型的索引:clustered索引和non-clustered索引,根据数据集合的不同,每种类型的索引都有各自独特的优点。

    clustered索引要求数据表中数据按照顺序存储。因为数据已经排序,所以对于查找一定范围的索引值时clustered 索引是非常有效的。对于查找具有唯一索引值的行信息来说,这种类型的索引性能也优于其他类型的索引

     non- clustered索引和教科书中的索引非常相似,索引在一个位置而其数据值却在另外一个位置。对于一个数据值的查询搜索来说,首先搜索non- clustered的索引,找到数据值在数据表中的位置,然后直接从这个位置得到数据。non-clustered 索引对于精确匹配查询是非常有用的

统计学
    作为一种常用的规则,和大多数商业使用需求一样,索引的数量应该尽可能少,以减少与每个查询相关的处理过程。如果要分析和优化查询的性能,首先应该度量和收集数据的统计特性。

SQL服务器系统能够维护索引值的数据统计特性。如果对其进行适当的配置,对于非索引值也能够进行统计度量。

对于性能优化,数据库管理员应该懂得几个基本的统计概念,这些概念的定义如下:

更新索引统计
   分布页面并不是每次一个记录更新时都要进行更新.在大型数据库中,这会导致巨大的性能损失.因此,当用户初始创建一个空表时,分布页面仍是空的.它仅在发生如下情况时才被更新:
1.用户在一个已存在数据表上创建一个索引.
2.用户进行了update satatic语句
从系统管理员角度来看,用户应该创建一个工具来自动地更新分布页面.自动更新应该至少每周一次,如果数据量每天增加10%以上则应每天一次.
因为不可能每天都添加索引,用户需要使用update statistics语句更新分布页面,用以优化SQLserver.

UPDATE STATISTICS
在指定的表或索引视图中,对一个或多个统计组(集合)有关键值分发的信息进行更新。若要基于列生成统计,请参见 CREATE STATISTICS。

语法
UPDATE STATISTICS table | view
    [
        index
        | ( statistics_name [ ,...n ] )
    ]
    [    WITH
        [
            [ FULLSCAN ]
            | SAMPLE number { PERCENT | ROWS } ]
            | RESAMPLE
        ]
        [ [ , ] [ ALL | COLUMNS | INDEX ]
        [ [ , ] NORECOMPUTE ]
    ]

参数
table | view

要更新统计的表或索引视图的名称。表名和视图名必须符合标识符的规则。有关更多信息,请参见使用标识符。由于索引名在每个数据库中不唯一,所以必须指定 table 或 view。可选择指定数据库、表或视图所有者。只有在 Microsoft? SQL Server? 2000 企业版中才支持索引视图。

index

要更新统计的索引。索引名必须符合标识符的规则。如果未指定 index,则更新指定表或索引视图中的所有索引的分发统计。若要查看索引名和描述的列表,请带表名或视图名执行 sp_helpindex。

statistics_name

要更新的统计组(集合)的名称。统计名称必须符合标识符规则。有关生成统计组的更多信息,请参见 CREATE STATISTICS。

n

是表示可以指定多个 statistic_name 组的占位符。

FULLSCAN

指定应读取 table 或 view 中的所有行以收集统计。FULLSCAN 提供与 SAMPLE 100 PERCENT 相同的行为。FULLSCAN 不能与 SAMPLE 选项一起使用。

SAMPLE number { PERCENT | ROWS }

当为较大的表或视图收集统计时,指定要采样的表或索引视图的百分比或行数。number 只允许使用整数,无论它是 PERCENT 还是 ROWS。若要对较大的表或视图使用默认采样行为,请将 SAMPLE number 和 PERCENT 或 ROWS 一起使用。Microsoft SQL Server 将确保值的采样数不低于某一数目,以保证统计有用。如果 PERCENT、ROWS 或 number 选项导致要采样的行数过小,SQL Server 则自动根据表或视图中的现有行数改正采样。

说明  默认行为是在目标表或索引视图上进行采样扫描。SQL Server 自动计算所需的样本大小。


RESAMPLE

指定使用从所有现有统计(包括索引)继承的采样速率来收集统计。如果采样速率导致要采样的行过少,SQL Server 则自动根据表或视图中的现有行数改正采样。

ALL | COLUMNS | INDEX

指定 UPDATE STATISTICS 语句是否影响列统计、索引统计或所有现有统计。如果未指定选项,则 UPDATE STATISTICS 语句影响所有的统计。每个 UPDATE STATISTICS 语句只能指定一种类型(ALL、COLUMNS 或 INDEX)。

NORECOMPUTE

指定过期统计不自动重新计算。统计过期与否取决于在索引列上进行的 INSERT、UPDATE 和 DELETE 操作的数量。指定该选项时,将导致 SQL Server 禁用自动统计重建功能。若要还原自动统计重新计算,请重新执行 UPDATE STATISTICS(不要 NORECOMPUTE 选项),或者执行 sp_autostats。

重要  禁用自动统计重新计算会导致 SQL Server 查询优化器对于涉及指定表的查询选择非最佳的策略。


注释
SQL Server 保留每个索引中关于键值分发的统计,并且使用这些统计来决定查询处理中使用哪个(或哪些)索引。用户可以通过使用 CREATE STATISTICS 语句生成基于非索引列的统计。查询优化依赖于分发步骤的准确性:

如果索引中的键值有显著变化,请对此索引重新运行 UPDATE STATISTICS。


如果索引列中添加、更改或删除大量数据(即如果键值分发更改),或者用 TRUNCATE TABLE 语句将表截断然后重新填充,请使用 UPDATE STATISTICS。
若要查看统计最近一次更新的时间,请使用 STATS_DATE 函数。

只有当能够在计算列上创建索引时,才可以在包含这些计算列的表上创建或更新统计。有关在计算列上创建索引的要求和限制的更多信息,请参见 CREATE INDEX。

权限
UPDATE STATISTICS 权限默认授予表或视图的所有者,并且该权限不可转让。

示例
A. 更新单个表的所有统计
本示例更新表 authors 上的所有索引分发统计。

UPDATE STATISTICS authors

B. 仅更新单一索引的统计
本示例仅更新表 authors 的索引 au_id_ind 的分发信息。

UPDATE STATISTICS authors au_id_ind

C. 使用 50% 采样更新特定统计组(集合)的统计
本示例首先创建表 authors 中 au_lname 列和 au_fname 列的统计组,然后对其进行更新。

CREATE STATISTICS anames
   ON authors (au_lname, au_fname)
   WITH SAMPLE 50 PERCENT
GO
-- Time passes. The UPDATE STATISTICS statement is then executed.
UPDATE STATISTICS authors(anames)
   WITH SAMPLE 50 PERCENT
GO

D. 使用 FULLSCAN 和 NORECOMPUTE 更新特定统计组(集合)的统计
本示例更新表 authors 中的 anames 统计组(集合),强制对表 authors 中的所有行进行完全扫描,并且关闭该统计组(集合)的自动统计更新。

UPDATE STATISTICS authors(anames)
   WITH FULLSCAN, NORECOMPUTE

sp_updatestats对当前数据库中所有用户定义的表运行 UPDATE STATISTICS。

语法
sp_updatestats [[@resample =] ''resample'']

返回代码值
0(成功)或 1(失败)

参数
[@resample =] ''resample''

指定 sp_updatestats 将使用 UPDATE STATISTICS 命令的 RESAMPLE 选项。新统计表将继承旧统计表的采样比率。如果未指定 ''resample'',则 sp_updatestats 使用默认采样更新统计表。该参数的数据类型为 varchar(8),默认值为 ''NO''。

注释
sp_updatestats 会显示表示其进度的消息。完成更新之后,该存储过程将报告已为所有的表更新了统计信息。

权限
只有 DBO 和 sysadmin 固定服务器角色的成员才能执行该过程。

示例
下例为数据库 pubs 中的表更新统计信息。

USE pubs
EXEC sp_updatestats


Sqlserver7 编程技术内幕提供的方法.

drop proc pr_updateindex
create proc pr_updateindex
as
 set nocount on
 declare get_index_curs cursor
  for select name--tablename
  from sysobjects --systemtable
 where type=''u'' -usertable

 declare @holdtable varchar(30)
 declare @message varchar(40)
 declare @dynamic varchar(51)

 open getindex_curs
 fetch next from getindex_curs into @holdtable
 while @@fetch_status=0
 begin
   select @dynamic=''update statistics ''+@holdtable
   select @message=''updating''+@holdtable
   exec(@dynamic)
  print @message
  fetch next from getindex_curs into @holdtable
end
  close getindex_curs




Copyright (C) 2003 Cameron Michelis copying and redistribution of this file is permitted provided
this notice and the above comments are preserved.
*/

Set quoted_identifier off
use master
DECLARE @fillfactor varchar(2)
DECLARE @tablename varchar(30)
DECLARE @tablename_header varchar(75)
DECLARE @dataname varchar(30)
DECLARE @dataname_header varchar(75)
DECLARE datanames_cursor CURSOR FOR SELECT name FROM sysdatabases
        WHERE name not in ('master', 'pubs', 'tempdb', 'model', 'northwind')
/* Variable Initialization */
 select @fillfactor = "0" -- Set Fill factor here
     -- Note "0" will use original fillfactor.
/* End Variable Initialization */
OPEN datanames_cursor

  FETCH NEXT FROM datanames_cursor INTO @dataname

  WHILE (@@fetch_status <> -1)
    BEGIN
      IF (@@fetch_status = -2)
        BEGIN
  FETCH NEXT FROM datanames_cursor INTO @dataname
           CONTINUE
        END
 SELECT @dataname_header = "Database " + RTRIM(UPPER(@dataname))
       PRINT " "
 PRINT @dataname_header
       PRINT " "
 EXEC ("USE " + @dataname + " DECLARE tnames_cursor CURSOR FOR SELECT name from sysobjects where type = 'U'")
 Select @dataname_header = RTRIM(UPPER(@dataname))
 Exec ("Use " + @dataname)
 OPEN tnames_cursor
  FETCH NEXT FROM tnames_cursor INTO @tablename
  WHILE (@@fetch_status <> -1)
          BEGIN
             IF (@@fetch_status = -2)            
    BEGIN
                  FETCH NEXT FROM tnames_cursor INTO @tablename
                  CONTINUE
               END
        SELECT @tablename_header = "  Updating " + RTRIM(UPPER(@tablename))
    PRINT ""
             PRINT @tablename_header
    EXEC ("USE " + @dataname + " DBCC DBREINDEX (" + @tablename + "," + "''" + "," + @fillfactor + ")")
    EXEC ("USE " + @dataname + " UPDATE STATISTICS " + @tablename)
    FETCH NEXT FROM tnames_cursor INTO @tablename
          END
 DEALLOCATE tnames_cursor
       FETCH NEXT FROM datanames_cursor INTO @dataname
      END
DEALLOCATE datanames_cursor
PRINT ""
PRINT " "
PRINT "Indexing complete for All User Databases"

SET QUOTED_IDENTIFIER OFF
/* Start with master DB */
USE master
/* Create Variables */
DECLARE     @DBName     CHAR(64)
DECLARE     @TableName     CHAR(64)
DECLARE        @FQTableName    CHAR(64)
DECLARE        @TempVar    CHAR(256)
/* Create DB List */
DECLARE     DBCursor     CURSOR FOR
SELECT        name
FROM    master..sysdatabases
OPEN        DBCursor
FETCH NEXT
FROM    DBCursor
INTO    @DBName
/* Create Database Loop */
WHILE @@FETCH_STATUS = 0
BEGIN
/* Retrieve Table List */
PRINT 'Retrieving Table List for DB ' + @DBName
EXEC ('SELECT name AS TableName INTO ##TableNames FROM [' + @DBName + ']..sysobjects WHERE type = ''U''')
/* Open Table List */
DECLARE        TableCursor     CURSOR FOR
SELECT        TableName
FROM    ##TableNames
OPEN TableCursor
FETCH NEXT
FROM    TableCursor
INTO    @TableName
/* Create Table Loop */
WHILE @@FETCH_STATUS = 0
BEGIN
/* Add DB Name to Table Name */
SELECT @FQTableName = QUOTENAME(RTRIM(@DBName)) + '..' + QUOTENAME(RTRIM(@TableName))
SELECT @TableName = RTRIM(@DBName) + '..' + RTRIM(@TableName)
/* ReIndex Table */
PRINT 'ReIndexing Table ' + @TableName
DBCC DBREINDEX(@TableName)
/* Update Statics on Table */
PRINT 'Updating Statistics on Table ' + @TableName
EXEC ('UPDATE STATISTICS ' + @FQTableName)
/* Get Next Table Name */
FETCH NEXT
FROM TableCursor
INTO @TableName
END
/* Close Table Cursor */
CLOSE        TableCursor
DEALLOCATE    TableCursor
/* Remove Tempory Table */
DROP TABLE ##TableNames
/* Preform DB Checks */
PRINT 'Preforming DB Checks on ' + @DBName
DBCC CHECKDB (@DBName)
/* Get Next Table Name */
FETCH NEXT
FROM DBCursor
INTO @DBName
END
/* Close DB Curosor */
CLOSE        DBCursor
DEALLOCATE    DBCursor
/* Finished */

posted @ 2010-08-18 19:26 Eric_jiang 阅读(790) | 评论 (0)编辑 收藏

     扫描与查找操作均是SQL Server从表或索引中读取数据采用的迭代器,这些也是SQL Server支持的最基本的运算.几乎在每一个查询计划中都可以找到,因此理解它们的不同是很重要的,扫描是在整张表上进行处理,而索引是在整个页级上进行处理,而查找则返回特定谓词上一个或多个范围内的数据行
      下面让我们看一个扫描的例子(这里使用Northwind数据库)
        SELECT [OrderId] FROM [Orders] WHERE [RequiredDate] = '1998-03-26'
        在Orders表中,并不存在对RequiredDate列的索引,因此,SQL Server必须读取Orders表的每一行来估计每一行的RequiredDate谓词,如果满足该谓词条件(即找到包含’1998-03-26’的记录),则返回该行数据.
        为了最大化提升性能,SQL Server尽可能地使用扫描迭代器来估计该谓词,然而,如果该谓词过于复杂或开销过大,SQL Server或许使用别的筛选迭代器来估计.以下是WHERE关键字中的文本计划的过程:
|--Clustered Index Scan(OBJECT:([Orders].[PK_Orders]),
  WHERE:([Orders].[RequiredDate]='1998-03-26'))
下图描述了该操作的流程图:

    由于扫描表的每一行数据,不论满足与否,因此,其查询开销对表中的总记录数是均衡的,当表中的数据很少或满足谓词的行比较多时,采用扫描操作有效,如果表中数据量比较大或满足谓词的行较少时,使用扫描将读取更多的页面或执行更多的I/O操作来获取数据,这显而不是最有效的方法.   
     下面让我们看一个关于索引查找的例子,下面的例子在OrderdDate列上创建了索引:
   SELECT [OrderId] FROM [Orders] WHERE [OrderDate] = '1998-02-26'

         这次SQL Server能够使用索引查找来直接找到满足谓词的那些记录行,这里称该谓词为"查找"谓词.大多数情况下,SQL Server并不显式地估计"查找"谓词,而索引确保了"查找"操作仅返回满足的数据行,以下是"查找"谓词的文本计划:

|--Index Seek(OBJECT:([Orders].[OrderDate]),

  SEEK:([Orders].[OrderDate]=CONVERT_IMPLICIT(datetime,[@1],0)) ORDERED FORWARD)

注意:SQL Server自动使用@1参数替换查询文本中的参数

    由此看来,查找仅扫描满足该谓词的数据页,其查询开销显然要比表中总记录数的开销低,因此,对于高选择度的查询谓词操作,查找通常是最有效的策略.也就是说,对于估计大表中的数据时,使用查找谓词是比较有效率的.

    SQL Server将扫描与查找进行区分,如同将在堆(无聚集索引的对象)上扫描,聚集索引上的扫描,非聚集索引上的扫描进行分区.下表说明了这些出现在的查询计划中的扫描与查找运算.

 

扫描

查找

表扫描

 

聚集索引

聚集索引找描

聚集索引查找

非聚集索引

索引扫描

索引查找

可查找的谓词与覆盖列

    SQL Server在执行索引查找之前,它需要确定索引的键是否满足查询中的谓词,我们称该谓词为"可查找的谓词",SQL Server必须确定该索引是否包含或"覆盖"查询中引用的列集合.下面描述了如何确定哪个谓词是可查找的,哪个谓词不是可查找的,哪些列需要索引覆盖.

单列索引

   在单列索引上判断谓词是否是可查找的是很容易的,SQL Server使用单列索引来响应多数简单的比较(包括相等和不等(大于,小于等))或者更复杂的表达式,如在列上运算的函数和LIKE %谓词,这些运算符将阻止SQL Server使用索引查找.

例如,假设我们在Col1列上创建了单列索引,可以在以下谓词上进行索引查找:

Ø [Col1] = 3.14

Ø [Col1] > 100

Ø [Col1] BETWEEN 0 AND 99

Ø [Col1] LIKE 'abc%'

Ø [Col1] IN (2, 3, 5, 7)

然页,在以下谓词上将不能使用索引查找:

Ø ABS([Col1]) = 1

Ø [Col1] + 1 = 9

Ø [Col1] LIKE '%abc'

下面我通过一些例子来介绍单列索引:

首先创建一些架构对象:
create table person
(id int, last_name varchar(30), first_name varchar(30))

create unique clustered index person_id
on person (id)
create index person_name
on person (last_name, first_name)

    以下是三个查询及其各自的文本查询计划,第一个查询在person_name索引上进行查找,第二个查询首先在第一个键列上进行索引查找,然后使用residual谓词来估计first_name,第三个查询不能使用索引查找,而是使用了索引扫描来处理residual谓词.

select id from person where last_name = 'Doe' and first_name = 'John'

 |--Index Seek(OBJECT:([person].[person_name]), SEEK:([person].[last_name]='Doe' AND [person].[first_name]='John'))

 

select id from person where last_name > 'Doe' and first_name = 'John'

 |--Index Seek(OBJECT:([person].[person_name]), SEEK:([person].[last_name] > 'Doe'), WHERE:([person].[first_name]='John'))

 

select id from person where last_name like '%oe' and first_name = 'John'

 |--Index Scan(OBJECT:([person].[person_name]), WHERE:([person].[first_name]='John' AND [person].[last_name] like '%oe'))

 

上面三条查询的图形查询计划:

posted @ 2010-08-18 16:16 Eric_jiang 阅读(2360) | 评论 (0)编辑 收藏

要对资料库管理系统进行操作,最基本的就是使用SQL(Standard Query Language)语句,大部份的资料库都支援标准的SQL语句,然而也有一些特定于资料库的SQL语句,应用程式配合SQL语句进行资料库查询时,若使用到特定于资料库的SQL语句,程式本身会有相依于特定资料库的问题。

使用Hibernate时,即使您不了解SQL的使用与撰写,也可以使用它所提供的API来进行SQL语句查询,org.hibernate.Criteria对SQL进行封装,您可以从Java物件的观点来组合各种查询条件,由Hibernate自动为您产生SQL语句,而不用特别管理SQL与资料库相依的问题。

以最基本的查询来说,如果您想要查询某个物件所对应的资料表中所有的内容,您可以如下进行查询:

Criteria criteria = session.createCriteria(User.class);
List users = criteria.list();

for(Iterator it = users.iterator(); it.hasNext(); ) {
User user = (User) it.next();
System.out.println(user.getId() +
" "t " + user.getName() +
"/" + user.getAge());
}


Criteria建立后,若不给予任何的条件,预设是查询物件所对应表格之所有资料,如果您执行以上的程式片段,并于设定档中设定了了Hibernate的”show_sql”属性,则可以在主控下看到以下的SQL语句之产生:

Hibernate: select this_.id as id0_0_, this_.name as name0_0_, this_.age as age0_0_ from T_USER this_

Criteria基本查询条件设定

org.hibernate.Criteria实际上是个条件附加的容器,如果想要设定查询条件,则要使用org.hibernate.criterion.Restrictions的各种静态方法传回org.hibernate.criterion.Criteria实例,传回的每个org.hibernate.criterion.Criteria实例代表着一个条件,您要使用org.hibernate.Criteria的add()方法加入这些条件实例,例如查询”age”大于20且小于40的资料:

Criteria criteria = session.createCriteria(User.class);
criteria.add(Restrictions.gt("age", new Integer(20)));
criteria.add(Restrictions.lt("age", new Integer(40)));
List users = criteria.list();

for(Iterator it = users.iterator(); it.hasNext(); ) {
User user = (User) it.next();
System.out.println(user.getId() +
" "t " + user.getName() +
"/" + user.getAge());
}

Restrictions的gt()方法表示大于(great than)的条件,而lt表示小于(less than)的条件,执行以上程式片段,观察所产生的SQL语句,将使用where与and子句产来完成SQL的条件查询:

Hibernate: select this_.id as id0_0_, this_.name as name0_0_, this_.age as age0_0_ from T_USER this_ where this_.age>? and this_.age

使用add()方法加入条件时,预设是使用and来组合条件,如果要用or的方式来组合条件,则可以使用Restrictions.or()方法,例如结合age等于(eq)20或(or)age为空(isNull)的条件:

Criteria criteria = session.createCriteria(User.class);
criteria.add(Restrictions.or(
Restrictions.eq("age", new Integer(20)),
Restrictions.isNull("age")
));
List users = criteria.list();

观察所产生的SQL语句,将使用where与or子句完成SQL的条件查询:

Hibernate: select this_.id as id0_0_, this_.name as name0_0_, this_.age as age0_0_ from T_USER this_ where (this_.age=? or this_.age is null)

您也可以使用Restrictions.like()方法来进行SQL中like子句的功能,例如查询”name”中名称为”just”开头的资料:

Criteria criteria = session.createCriteria(User.class);
criteria.add(Restrictions.like("name", "just%"));
List users = criteria.list();

观察所产生的SQL语句如下:

Hibernate: select this_.id as id0_0_, this_.name as name0_0_, this_.age as age0_0_ from T_USER this_ where this_.name like ?

Restrictions的几个常用限定查询方法如下表所示:

方法 说明
Restrictions.eq 等于
Restrictions.allEq 使用Map,使用key/value进行多个等于的比对
Restrictions.gt 大于 >
Restrictions.ge 大于等于 >=
Restrictions.lt 小于 <
Restrictions.le 小于等于 <=
Restrictions.between 对应SQL的BETWEEN子句
Restrictions.like 对应SQL的LIKE子句
Restrictions.in 对应SQL的in子句
Restrictions.and and关係
Restrictions.or or关係

Criteria进阶查询条件设定

使用Criteria进行查询时,不仅仅能组合出SQL中where子句的功能,还可以组合出如排序、统计、分组等的查询功能。

排序

您可以使用Criteria进行查询,并使用org.hibernate.criterion.Order对结果进行排序,例如使用Oder.asc(),指定根据”age”由小到大排序(反之则使用desc()):

Criteria criteria = session.createCriteria(User.class);
criteria.addOrder(Order.asc("age"));
List users = criteria.list();

注意在加入Order条件时,使用的是addOrder()方法,而不是add()方法,在产生SQL语句时,会使用order by与asc(desc)来进行排序指定:

Hibernate: select this_.id as id0_0_, this_.name as name0_0_, this_.age as age0_0_ from T_USER this_ order by this_.age asc

限定查询笔数

Criteria的setMaxResults()方法可以限定查询回来的笔数,如果配合setFirstResult()设定传回查询结果第一笔资料的位置,就可以实现简单的分页,例如传回第51笔之后的50笔资料(如果有的话):

Criteria criteria = session.createCriteria(User.class);
criteria.setFirstResult(51);
criteria.setMaxResults(50);
List users = criteria.list();

根据您所指定得资料库,Hibernate将自动产生与资料库相依的限定笔数查询子句,例如在MySQL中,将使用limit产生以下的SQL语句:

Hibernate: select this_.id as id0_0_, this_.name as name0_0_, this_.age as age0_0_ from T_USER this_ limit ?, ?

统计动作

您可以对查询结果进行统计动作,使用org.hibernate.criterion.Projections的avg()、rowCount()、count()、max()、min()、 countDistinct()等方法,再搭配Criteria的setProjection()方法加入条件设定,例如对查询结果的"age"作平均:

Criteria criteria = session.createCriteria(User.class);
criteria.setProjection(Projections.avg("age"));
List users = criteria.list();

上面的程式将由Hibernate自动产生SQL的avg函数进行平均计算:

Hibernate: select avg(this_.age) as y0_ from T_USER this_

分组

还可以配合Projections的groupProperty()来对结果进行分组,例如以"age"进行分组,也就是如果资料中"age"如果有 20、20、25、30,则以下会显示20、25、30:

Criteria criteria = session.createCriteria(User.class);
criteria.setProjection(Projections.groupProperty("age"));
List users = criteria.list();

上面的程式将由Hibernate自动产生SQL的group by子句进行分组计算:

Hibernate: select this_.age as y0_ from T_USER this_ group by this_.age

如果想同时结合统计与分组功能,则可以使用org.hibernate.criterion.ProjectionList,例如下面的程式会计算每个年龄各有多少个人:

ProjectionList projectionList = Projections.projectionList();
projectionList.add(Projections.groupProperty("age"));
projectionList.add(Projections.rowCount());

Criteria criteria = session.createCriteria(User.class);
criteria.setProjection(projectionList);
List users = criteria.list();

观察所产生的SQL语句,将使用group by先进行分组,再针对每个分组进行count函数的计数

Hibernate: select this_.age as y0_, count(*) as y1_ from T_USER this_ group by this_.age

根据已知物件进行查询

设定查询条件并非一定要使用Restrictions,如果属性条件很多,使用Restrictions也不方便,如果有一个已知的物件,则可以根据这个物件作为查询的依据,看看是否有属性与之类似的物件,例如:

User user = new User();
user.setAge(new Integer(30));

Criteria criteria = session.createCriteria(User.class);
criteria.add(Example.create(user));

List users = criteria.list();

您可以透过org.hibernate.criterion.Example的create()方法来建立Example实例,Example实作了Criteria介面,因此可以使用add()方法加入至Criteria条件设定之中,Hibernate将自动过滤掉空属性,根据已知物件上已设定的属性,判定是否产生于where子句之中:

Hibernate: select this_.id as id0_0_, this_.name as name0_0_, this_.age as age0_0_ from T_USER this_ where (this_.age=?)

设定SQL范本

如果您了解如何撰写SQL语句,想要设定一些Hibernate产生SQL时的范本,您也可以使用Restrictions的sqlRestriction()方法,提供SQL语法范本作限定查询,例如查询name以cater开头的资料:

Criteria criteria = session.createCriteria(User.class);
criteria.add(Restrictions.sqlRestriction("{alias}.name LIKE (?)", "cater%", Hibernate.STRING));
List users = criteria.list();

其中alias将被替换为与User类别相关的名称,而?将被替换为cater%,也就是第二个参数所提供的值,sqlRestriction() 方法第一个参数所设定的是where子句的部份,所以在SQL撰写时,不必再写where,观察所产生的SQL语句,将使用您所设定的SQL范本作为基础,来完成SQL的条件查询:

Hibernate: select this_.id as id0_0_, this_.name as name0_0_, this_.age as age0_0_ from T_USER this_ where this_.name LIKE (?)

如果有多个查询条件,例如between子句的查询,则可以如下:

Criteria criteria = session.createCriteria(User.class);
Integer[] ages = {new Integer(20), new Integer(40)};
Type[] types = {Hibernate.INTEGER, Hibernate.INTEGER};
criteria.add(Restrictions.sqlRestriction("{alias}.age BETWEEN (?) AND (?)", ages, types));
List users = criteria.list();

观察所产生的SQL语句如下:

Hibernate: select this_.id as id0_0_, this_.name as name0_0_, this_.age as age0_0_ from T_USER this_ where this_.age BETWEEN (?) AND (?)

使用DetchedCriteria

Criteria与Session绑定,其生命週期跟随着Session结束而结束,使用Criteria时进行查询时,每次都要于执行时期动态建立物件,并加入各种查询条件,随着Session的回收,Criteria也跟着回收。

为了能够重複使用Criteria物件,在Hibernate 3中新增了org.hibernate.criterion.DetchedCriteria,您可以先建立DetchedCriteria实例,并加入各种查询条件,并于需要查询时再与Session绑定,获得一个绑定Session的Criteria物件,例如:

// 先建立DetchedCriteria物件
DetachedCriteria detchedCriteria = DetachedCriteria.forClass(User.class);
// 加入查询条件
detchedCriteria.add(Restrictions.ge("age",new Integer(25)));

Session session = sessionFactory.openSession();
// 绑定Session并返回一个Criteria实例
Criteria criteria = detchedCriteria.getExecutableCriteria(session);

List users = criteria.list();

结论

Hibernate的Criteria API可以让您使用物件的方式,组合出查询资料库系统的条件,Hibernate会自动依您所使用的资料库,动态产生SQL语句,让您的应用程式在存取资料库时,不致于因撰写了特定的SQL而相依于特定的资料库,如果您的开发人员不熟悉SQL语句的撰写,也可以试着使用Criteria来解决查询资料库的需求。
posted @ 2010-08-16 22:43 Eric_jiang 阅读(19182) | 评论 (0)编辑 收藏

众所周知,对于类似"aaaaaaaaaaaaaaaaa"这样超长的字符串,会把容器撑的变形.

废话不多说,解决方法:

1. IE : 在相应位置增加如下样式表定义即可
程序代码 程序代码
word-break: break-all;


2. FF : Firefox并不像IE那样听话,自从FF的第一个版本至今,仍然没有解决此问题,而上面的方法也不起作用,只能用javascript来控制了,将如下代码放置在需要折行显示的下面即可
程序代码 程序代码

<script language="javascript" type="text/javascript" defer="defer">
javascript:(function(){var D=document; F(D.body); function F(n){var u,r,c,x; if(n.nodeType==3){ u=n.data.search(/"S{10}/); if(u>=0) { r=n.splitText(u+10); n.parentNode.insertBefore(D.createElement("WBR"),r); } }else if(n.tagName!="STYLE" && n.tagName!="SCRIPT"){for (c=0;x=n.childNodes[c];++c){F(x);}} } })();
</script>

posted @ 2010-08-13 10:08 Eric_jiang 阅读(448) | 评论 (0)编辑 收藏

此方法可多条件查询且可以根据关联的表条件进行查询
如查询某个商品:
表结构如下:
goods(商品表)
goodsid(商品id) goodsname(名称) typeid(分类-外键)  supplierid(供应商-外键)

type(分类表)
typeid(id主键)   typename(分类名称)

supplier(供应商表)
supplierid(ID主键)   suppliername( 供应商名称)

你可建一个查询条件的类,里面包括你要查询的所有字段
如: public class Query{
    private String suppliername;
    private String goodsname;
    private String typename;
..................
get/set方法................
}

得到查询条件后,可以把此类的一个对象传入自己做的方法,此方法可以根据条件的个数及是否输入条件进行查询:
public static List query_goods(Query query){
        Session session = SessionFactory.getSession();
        Criteria criteria = session.createCriteria(Goods.class);
        Criteria type = criteria.createCriteria("type");
        Criteria  supplier= criteria.createCriteria("supplier");
    
        if(null!=query.getGoodsname() && !"".equels(query.getGoodsname() ))
            criteria.add(Restrictions.like("goodsname","%"+query.getGoodsname()+"%"));
      
       if(null!=query.getSuppliername() && !"".equels(query.getSuppliername() ))
            supplier.add(Restrictions.like("suppliername","%"+query.getSuppliername()+"%"));

        if(null!=query.getTypename() && !"".equels(query.getTypename() ))
            type.add(Restrictions.like("typename","%"+query.getTypename+"%"));
       
        List list = criteria.list();
       
        session.clear();
        session.close();
        return list;
    }

以上方面还可多层的嵌套,如type里还有外键,可以按照以上方法进行嵌套。注意,查询时所有涉及到的数据都将一次性写入类的属性中,包括有关联的,即此时goods的关联延迟加载无效,我觉得这一点非常的好。呵呵,有什么好处,可以自己好好的想想。
有许多人曾经提到过用Example,就不用自己判断了,如果没有关联条件查询的话,确实是好,可它的缺点就是不能查询关联中的条件。

 

posted @ 2010-08-12 21:02 Eric_jiang 阅读(2499) | 评论 (0)编辑 收藏

近在项目中使用 Spring 和 Hibernate 进行开发,有感于 Criteria 比较好用,在查询方法

设计上可以灵活的根据 Criteria 的特点来方便地进行查询条件的组装。现在对 Hibernate的Criteria 的用法进行总结:
        Hibernate 设计了 CriteriaSpecification 作为 Criteria 的父接口,下面提供了 Criteria和DetachedCriteria 。
        Criteria 和 DetachedCriteria 的主要区别在于创建的形式不一样, Criteria 是在线的,所以它是由 Hibernate Session 进行创建的;而 DetachedCriteria 是离线的,创建时无需 Session,DetachedCriteria 提供了 2 个静态方法 forClass(Class) 或 forEntityName(Name) 进行DetachedCriteria 实例的创建。 Spring 的框架提供了getHibernateTemplate().findByCriteria(detachedCriteria) 方法可以很方便地根据DetachedCriteria 来返回查询结
果。
        Criteria 和 DetachedCriteria 均可使用 Criterion 和 Projection 设置查询条件。可以设置 FetchMode( 联合查询抓取的模式 ) ,设置排序方式。对于 Criteria 还可以设置 FlushModel (冲刷 Session 的方式)和 LockMode (数据库锁模式)。
下面对 Criterion 和 Projection 进行详细说明。
         Criterion 是 Criteria 的查询条件。Criteria 提供了 add(Criterion criterion) 方法来添加查询条件。
         Criterion 接口的主要实现包括: Example 、 Junction 和 SimpleExpression 。而 Junction 的实际使用是它的两个子类 conjunction 和 disjunction ,分别是使用 AND 和 OR 操作符进行来联结查询条件集合。
         Criterion 的实例可以通过 Restrictions 工具类来创建,Restrictions 提供了大量的静态方法,如 eq (等于)、 ge (大于等于)、 between 等来方法的创建 Criterion 查询条件 (SimpleExpression 实例)。除此之外, Restrictions 还提供了方法来创建 conjunction 和 disjunction 实例,通过往该实例的 add(Criteria) 方法来增加查询条件形成一个查询条件集合。
         至于 Example 的创建有所不同, Example 本身提供了一个静态方法 create(Object entity) ,即根据一个对象(实际使用中一般是映射到数据库的对象)来创建。然后可以设置一些过滤条件:
Example exampleUser =Example.create(u)
.ignoreCase() // 忽略大小写
.enableLike(MatchMode.ANYWHERE);
/ / 对 String 类型的属性,无论在那里值在那里都匹配。相当于 %value%
Project 主要是让 Criteria 能够进行报表查询,并可以实现分组。 Project 主要有 SimpleProjection 、 ProjectionList 和 Property 三个实现。其中 SimpleProjection 和
ProjectionList 的实例化是由内建的 Projections 来完成,如提供的 avg 、 count 、 max 、 min 、 sum 可以让开发者很容易对某个字段进行统计查询。
        Property 是对某个字段进行查询条件的设置,如通过Porperty.forName(“color”).in (new String[]{“black”,”red”,”write”}); 则可以创建一个 Project 实例。通过
criteria 的 add(Project) 方法加入到查询条件中去。
       使用 Criteria 进行查询,主要要清晰的是 Hibernate 提供了那些类和方法来满足开发中查询条件的创建和组装,下面介绍几种用法:
1. 创建一个Criteria 实例
org.hibernate.Criteria接口表示特定持久类的一个查询。Session是 Criteria实例的工厂。
Criteria crit = sess.createCriteria(Cat.class);
crit.setMaxResults(50);
List cats = crit.list();

2. 限制结果集内容
一个单独的查询条件是org.hibernate.criterion.Criterion 接口的一个实例。

org.hibernate.criterion.Restrictions类 定义了获得某些内置Criterion类型的工厂方法。
List cats = sess.createCriteria(Cat.class)
    .add( Restrictions.like("name", "Fritz%") )
    .add( Restrictions.between("weight", minWeight, maxWeight) )
    .list();

约束可以按逻辑分组。

List cats = sess.createCriteria(Cat.class)
    .add( Restrictions.like("name", "Fritz%") )
    .add( Restrictions.or(
        Restrictions.eq( "age", new Integer(0) ),
        Restrictions.isNull("age")
    ) )
    .list();

List cats = sess.createCriteria(Cat.class)
    .add( Restrictions.in( "name", new String[] { "Fritz", "Izi", "Pk" } ) )
    .add( Restrictions.disjunction()
        .add( Restrictions.isNull("age") )
        .add( Restrictions.eq("age", new Integer(0) ) )
        .add( Restrictions.eq("age", new Integer(1) ) )
        .add( Restrictions.eq("age", new Integer(2) ) )
    ) )
    .list();

Hibernate提供了相当多的内置criterion类型(Restrictions 子类), 但是尤其有用的是可以允许

你直接使用SQL。

List cats = sess.createCriteria(Cat.class)
    .add( Restrictions.sql("lower({alias}.name) like lower(?)", "Fritz%",

Hibernate.STRING) )
    .list();

{alias}占位符应当被替换为被查询实体的列别名。
Property实例是获得一个条件的另外一种途径。你可以通过调用Property.forName() 创建一个

Property。

Property age = Property.forName("age");
List cats = sess.createCriteria(Cat.class)
    .add( Restrictions.disjunction()
        .add( age.isNull() )
        .add( age.eq( new Integer(0) ) )
        .add( age.eq( new Integer(1) ) )
        .add( age.eq( new Integer(2) ) )
    ) )
    .add( Property.forName("name").in( new String[] { "Fritz", "Izi", "Pk" } ) )
    .list();

3. 结果集排序
你可以使用org.hibernate.criterion.Order来为查询结果排序。

List cats = sess.createCriteria(Cat.class)
    .add( Restrictions.like("name", "F%")
    .addOrder( Order.asc("name") )
    .addOrder( Order.desc("age") )
    .setMaxResults(50)
    .list();

List cats = sess.createCriteria(Cat.class)
    .add( Property.forName("name").like("F%") )
    .addOrder( Property.forName("name").asc() )
    .addOrder( Property.forName("age").desc() )
    .setMaxResults(50)
    .list();

4. 关联
你可以使用createCriteria()非常容易的在互相关联的实体间建立 约束。

List cats = sess.createCriteria(Cat.class)
    .add( Restrictions.like("name", "F%")
    .createCriteria("kittens")
        .add( Restrictions.like("name", "F%")
    .list();


注意第二个 createCriteria()返回一个新的 Criteria实例,该实例引用kittens 集合中的元素。
接下来,替换形态在某些情况下也是很有用的。

List cats = sess.createCriteria(Cat.class)
    .createAlias("kittens", "kt")
    .createAlias("mate", "mt")
    .add( Restrictions.eqProperty("kt.name", "mt.name") )
    .list();


(createAlias()并不创建一个新的 Criteria实例。)
Cat实例所保存的之前两次查询所返回的kittens集合是 没有被条件预过滤的。如果你希望只获得

符合条件的kittens, 你必须使用returnMaps()。

List cats = sess.createCriteria(Cat.class)
    .createCriteria("kittens", "kt")
    .add( Restrictions.eq("name", "F%") )
    .returnMaps()
    .list();
Iterator iter = cats.iterator();
while ( iter.hasNext() ) {
    Map map = (Map) iter.next();
    Cat cat = (Cat) map.get(Criteria.ROOT_ALIAS);
    Cat kitten = (Cat) map.get("kt");
}

5. 动态关联抓取
你可以使用setFetchMode()在运行时定义动态关联抓取的语义。

List cats = sess.createCriteria(Cat.class)
    .add( Restrictions.like("name", "Fritz%") )
    .setFetchMode("mate", FetchMode.EAGER)
    .setFetchMode("kittens", FetchMode.EAGER)
    .list();

这个查询可以通过外连接抓取mate和kittens。

6. 查询示例
org.hibernate.criterion.Example类允许你通过一个给定实例 构建一个条件查询。

Cat cat = new Cat();
cat.setSex('F');
cat.setColor(Color.BLACK);
List results = session.createCriteria(Cat.class)
    .add( Example.create(cat) )
    .list();


版本属性、标识符和关联被忽略。默认情况下值为null的属性将被排除。
可以自行调整Example使之更实用。

Example example = Example.create(cat)
    .excludeZeroes()           //exclude zero valued properties
    .excludeProperty("color") //exclude the property named "color"
    .ignoreCase()              //perform case insensitive string comparisons
    .enableLike();             //use like for string comparisons
List results = session.createCriteria(Cat.class)
    .add(example)
    .list();


甚至可以使用examples在关联对象上放置条件。

List results = session.createCriteria(Cat.class)
    .add( Example.create(cat) )
    .createCriteria("mate")
        .add( Example.create( cat.getMate() ) )
    .list();


7. 投影(Projections)、聚合(aggregation)和分组(grouping)
org.hibernate.criterion.Projections是 Projection 的实例工厂。我们通过调用

setProjection()应用投影到一个查询。

List results = session.createCriteria(Cat.class)
    .setProjection( Projections.rowCount() )
    .add( Restrictions.eq("color", Color.BLACK) )
    .list();

List results = session.createCriteria(Cat.class)
    .setProjection( Projections.projectionList()
        .add( Projections.rowCount() )
        .add( Projections.avg("weight") )
        .add( Projections.max("weight") )
        .add( Projections.groupProperty("color") )
    )
    .list();


在一个条件查询中没有必要显式的使用 "group by" 。某些投影类型就是被定义为 分组投影,他

们也出现在SQL的group by子句中。

可以选择把一个别名指派给一个投影,这样可以使投影值被约束或排序所引用。下面是两种不同的

实现方式:

List results = session.createCriteria(Cat.class)
    .setProjection( Projections.alias( Projections.groupProperty("color"), "colr" ) )
    .addOrder( Order.asc("colr") )
    .list();

 

List results = session.createCriteria(Cat.class)
    .setProjection( Projections.groupProperty("color").as("colr") )
    .addOrder( Order.asc("colr") )
    .list();

alias()和as()方法简便的将一个投影实例包装到另外一个 别名的Projection实例中。简而言之,

当你添加一个投影到一个投影列表中时 你可以为它指定一个别名:

List results = session.createCriteria(Cat.class)
    .setProjection( Projections.projectionList()
        .add( Projections.rowCount(), "catCountByColor" )
        .add( Projections.avg("weight"), "avgWeight" )
        .add( Projections.max("weight"), "maxWeight" )
        .add( Projections.groupProperty("color"), "color" )
    )
    .addOrder( Order.desc("catCountByColor") )
    .addOrder( Order.desc("avgWeight") )
    .list();


List results = session.createCriteria(Domestic.class, "cat")
    .createAlias("kittens", "kit")
    .setProjection( Projections.projectionList()
        .add( Projections.property("cat.name"), "catName" )
        .add( Projections.property("kit.name"), "kitName" )
    )
    .addOrder( Order.asc("catName") )
    .addOrder( Order.asc("kitName") )
    .list();


也可以使用Property.forName()来表示投影:

List results = session.createCriteria(Cat.class)
    .setProjection( Property.forName("name") )
    .add( Property.forName("color").eq(Color.BLACK) )
    .list();
List results = session.createCriteria(Cat.class)
    .setProjection( Projections.projectionList()
        .add( Projections.rowCount().as("catCountByColor") )
        .add( Property.forName("weight").avg().as("avgWeight") )
        .add( Property.forName("weight").max().as("maxWeight") )
        .add( Property.forName("color").group().as("color" )
    )
    .addOrder( Order.desc("catCountByColor") )
    .addOrder( Order.desc("avgWeight") )
    .list();


8. 离线(detached)查询和子查询
DetachedCriteria类使你在一个session范围之外创建一个查询,并且可以使用任意的 Session来

执行它。

DetachedCriteria query = DetachedCriteria.forClass(Cat.class)
    .add( Property.forName("sex").eq('F') );
//创建一个Session
Session session = .;
Transaction txn = session.beginTransaction();
List results = query.getExecutableCriteria(session).setMaxResults(100).list();
txn.commit();
session.close();


DetachedCriteria也可以用以表示子查询。条件实例包含子查询可以通过 Subqueries或者
Property获得。

DetachedCriteria avgWeight = DetachedCriteria.forClass(Cat.class)
    .setProjection( Property.forName("weight").avg() );
session.createCriteria(Cat.class)
    .add( Property.forName("weight).gt(avgWeight) )
    .list();
DetachedCriteria weights = DetachedCriteria.forClass(Cat.class)
    .setProjection( Property.forName("weight") );
session.createCriteria(Cat.class)
    .add( Subqueries.geAll("weight", weights) )
    .list();

相互关联的子查询也是有可能的:

DetachedCriteria avgWeightForSex = DetachedCriteria.forClass(Cat.class, "cat2")
    .setProjection( Property.forName("weight").avg() )
    .add( Property.forName("cat2.sex").eqProperty("cat.sex") );
session.createCriteria(Cat.class, "cat")
    .add( Property.forName("weight).gt(avgWeightForSex) )
    .list();

 

posted @ 2010-08-12 20:51 Eric_jiang 阅读(528) | 评论 (0)编辑 收藏

      Hibernate的对象有3种状态,分别为:瞬时态(Transient)、 持久态(Persistent)、脱管态(Detached)。处于持久态的对象也称为PO(Persistence Object),瞬时对象和脱管对象也称为VO(Value Object)。

    *  瞬时态

    由new命令开辟内存空间的java对象,

    eg. Person person = new Person("amigo", "女");

    如果没有变量对该对象进行引用,它将被java虚拟机回收。

     瞬时对象在内存孤立存在,它是携带信息的载体,不和数据库的数据有任何关联关系,在Hibernate中,可通过session的save()或 saveOrUpdate()方法将瞬时对象与数据库相关联,并将数据对应的插入数据库中,此时该瞬时对象转变成持久化对象

    * 持久态

    处于该状态的对象在数据库中具有对应的记录,并拥有一个持久化标识。如果是用hibernate的delete()方法,对应的持久对象就变成瞬时对象,因数据库中的对应数据已被删除,该对象不再与数据库的记录关联。
    当一个session执行close()或clear()、evict()之后,持久对象变成脱管对象,此时持久对象会变成脱管对象,此时该对象虽然具有数据库识别值,但它已不在HIbernate持久层的管理之下。

    持久对象具有如下特点

    1. 和session实例关联
    2. 在数据库中有与之关联的记录

    * 脱管态

    当与某持久对象关联的session被关闭后,该持久对象转变为脱管对象。当脱管对象被重新关联到session上时,并再次转变成持久对象。
    脱管对象拥有数据库的识别值,可通过update()、saveOrUpdate()等方法,转变成持久对象

    脱管对象具有如下特点:

    1. 本质上与瞬时对象相同,在没有任何变量引用它时,JVM会在适当的时候将它回收
    2. 比瞬时对象多了一个数据库记录标识值

 

    瞬态(Transient),也叫临时态。处于这种状态的对象具备的特征如下:                          
                                                
        不在Session的缓存中,不与任何的Session实例相关联。                                     
                                                
        在数据库中没有与之相对应的记录。                                                        
                                                
    持久态(Persistent),处于这种状态的对象具备的特征如下:                                   
                                                
        在Session的缓存中,与Session实例相关联。                                               
                                                
        在数据库中存在与之相对应的记录。                                                        
                                                
    脱管态(Detached),也叫游离态。处于这种状态的对象具备的特征如下:                         
                                                
        不在Session的缓存中,不与任何的Session实例相关联。                                     
                                            
        在数据库中存在与之相对应的记录。(前提条件是没有其他Session实例删除该条记录)。           
                                        
一、预备知识
对于hibernate,它的对象有三种状态,transient、persistent、detached
下边是常见的翻译办法:
transient:瞬态或者自由态
    (new DeptPo(1,”行政部”,20,”行政相关”),该po的实例和session没有关联,该po的实例处于transient)
persistent:持久化状态
    (和数据库中记录想影射的Po实例,它的状态是persistent, 通过get和load等得到的对象都是persistent)
detached:脱管状态或者游离态
    (1)当通过get或load方法得到的po对象它们都处于persistent,但如果执行delete(po)时(但不能执行事务),该po状态就处于detached, (表示和session脱离关联),因delete而变成游离态可以通过save或saveOrUpdate()变成持久态
    (2)当把session关闭时,session缓存中的persistent的po对象也变成detached

因关闭session而变成游离态的可以通过lock、save、update变成持久态
持久态实例可以通过调用 delete()变成脱管状态。
通过get()或load()方法得到的实例都是持久化状态的。
脱管状态的实例可以通过调用lock()或者replicate()进行持久化。
save()和persist()将会引发SQL的INSERT,delete()会引发SQLDELETE,
而update()或merge()会引发SQL UPDATE。对持久化(persistent)实例的修改在刷新提交的时候会被检测到,它也会引起SQL UPDATE。
saveOrUpdate()或者replicate()会引发SQL INSERT或者UPDATE

二、save 和update区别
    把这一对放在第一位的原因是因为这一对是最常用的。
    save的作用是把一个新的对象保存
    update是把一个脱管状态的对象或自由态对象(一定要和一个记录对应)更新到数据库
三、update 和saveOrUpdate区别
    这个是比较好理解的,顾名思义,saveOrUpdate基本上就是合成了save和update,而update只是update;引用hibernate reference中的一段话来解释他们的使用场合和区别
    通常下面的场景会使用update()或saveOrUpdate():
    程序在第一个session中加载对象,接着把session关闭
    该对象被传递到表现层
    对象发生了一些改动
    该对象被返回到业务逻辑层最终到持久层
    程序创建第二session调用第二个session的update()方法持久这些改动
    saveOrUpdate(po)做下面的事:
    如果该po对象已经在本session中持久化了,在本session中执行saveOrUpdate不做任何事
    如果savaOrUpdate(新po)与另一个与本session关联的po对象拥有相同的持久化标识(identifier),抛出一个异常
    org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [org.itfuture.www.po.Xtyhb#5]
    saveOrUpdate如果对象没有持久化标识(identifier)属性,对其调用save() ,否则update() 这个对象
四、persist和save区别
    这个是最迷离的一对,表面上看起来使用哪个都行,在hibernate reference文档中也没有明确的区分他们.
    这里给出一个明确的区分。(可以跟进src看一下,虽然实现步骤类似,但是还是有细微的差别)
    主要内容区别:
    1,persist把一个瞬态的实例持久化,但是并"不保证"标识符(identifier主键对应的属性)被立刻填入到持久化实例中,标识符的填入可能被推迟到flush的时候。
    2,save, 把一个瞬态的实例持久化标识符,及时的产生,它要返回标识符,所以它会立即执行Sql insert
五、saveOrUpdate,merge和update区别
    比较update和merge
    update的作用上边说了,这里说一下merge的
    如果session中存在相同持久化标识(identifier)的实例,用用户给出的对象覆盖session已有的持久实例
    (1)当我们使用update的时候,执行完成后,会抛出异常
    (2)但当我们使用merge的时候,把处理自由态的po对象A的属性copy到session当中处于持久态的po的属性中,执行完成后原来是持久状态还是持久态,而我们提供的A还是自由态
六、flush和update区别
    这两个的区别好理解
    update操作的是在自由态或脱管状态(因session的关闭而处于脱管状态)的对象//updateSQL
    而flush是操作的在持久状态的对象。
    默认情况下,一个持久状态的对象的改动(包含set容器)是不需要update的,只要你更改了对象的值,等待hibernate flush就自动更新或保存到数据库了。hibernate flush发生在以下几种情况中:
    1, 调用某些查询的和手动flush(),session的关闭、SessionFactory关闭结合
    get()一个对象,把对象的属性进行改变,把资源关闭。
    2,transaction commit的时候(包含了flush)
七、lock和update区别
    update是把一个已经更改过的脱管状态的对象变成持久状态
    lock是把一个没有更改过的脱管状态的对象变成持久状态(针对的是因Session的关闭而处于脱管状态的po对象(2),不能针对因delete而处于脱管状态的po对象)
    对应更改一个记录的内容,两个的操作不同:
    update的操作步骤是:
    (1)属性改动后的脱管的对象的修改->调用update
    lock的操作步骤是:
    (2)调用lock把未修改的对象从脱管状态变成持久状态-->更改持久状态的对象的内容-->等待flush或者手动flush
八、clear和evcit的区别
    clear完整的清除session缓存
    evcit(obj)把某个持久化对象从session的缓存中清空。                                                                             

posted @ 2010-08-10 23:39 Eric_jiang 阅读(605) | 评论 (0)编辑 收藏

apply and call

   它们的作用都是将函数绑定到另外一个对象上去运行,两者仅在定义参数方式有所区别:

    apply(thisArg,argArray);

    call(thisArg[,arg1,arg2…] ]);

即所有函数内部的this指针都会被赋值为thisArg,这可实现将函数作为另外一个对象的方法运行的目的

apply的说明

       如果 argArray 不是一个有效的数组或者不是 arguments 对象,那么将导致一个 TypeError。 
如果没有提供 argArray 和 thisArg任何一个参数,那么 Global 对象将被用作 thisArg, 
并且无法被传递任何参数。

call的说明

      call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisArg指定的新对象。 
如果没有提供 thisArg参数,那么 Global 对象被用作 thisArg

相关技巧:

     应用call和apply还有一个技巧在里面,就是用call和apply应用另一个函数(类)以后,当前的 
函数(类)就具备了另一个函数(类)的方法或者是属性,这也可以称之为“继承”。
看下面示例:

// 继承的演示 
 function base() { 

    this.member = "我是基类的属性!"
    
this.method = function() { 
        window.alert(
"我是基类method方法!"); 
    } 

function extend() { 
    base.call(
this); 
    window.alert(member); 
    window.alert(
this.method); 
     
this.me = "扩展出来的新属性";
     alert(
this.me);
}
extend();

上面的例子可以看出,通过call之后,extend可以继承到base的方法和属性。



call 方法 

调用一个对象的一个方法,以另一个对象替换当前对象。 

call([thisObj[,arg1[, arg2[, [,.argN]]]]]) 

参数 

thisObj 

可选项。将被用作当前对象的对象(替代当前对象的对象)。 

arg1, arg2, , argN 

可选项。将被传递方法参数序列(传递给替代对象的参数列表)。 

说明 

call 方法可以用来代替另一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。 

如果没有提供 thisObj 参数,那么 Global 对象被用作 thisObj。 


说明白一点其实就是更改对象的内部指针,即改变对象的this指向的内容。这在面向对象的js编程过程中有时是很有用的。 

引用网上一个代码段,运行后自然就明白其道理。 

 <input type="text" id="myText" value="input text"> 

 function Obj(){this.value="对象!";} 

var value="global 变量"
function Fun1(){alert(this.value);} 
window.Fun1(); 
//global 变量 
Fun1.call(window); //global 变量 
Fun1.call(document.getElementById('myText')); //input text 
Fun1.call(new Obj()); //对象!


call函数和apply方法的第一个参数都是要传入给当前对象的对象,及函数内部的this。后面的参数都是传递给当前对象的参数。 

运行如下代码: 

 var func=new function(){this.a="func"

var myfunc=function(x){ 
var a="myfunc"
alert(
this.a); 
alert(x); 

myfunc.call(func,
"var"); 

可见分别弹出了func和var。到这里就对call的每个参数的意义有所了解了。 


对于apply和call两者在作用上是相同的,但两者在参数上有区别的。 

对于第一个参数意义都一样,但对第二个参数: 

apply传入的是一个参数数组,也就是将多个参数组合成为一个数组传入,而call则作为call的参数传入(从第二个参数开始)。 

如 func.call(func1,var1,var2,var3)对应的apply写法为:func.apply(func1,[var1,var2,var3]) 


同时使用apply的好处是可以直接将当前函数的arguments对象作为apply的第二个参数传入

posted @ 2010-08-04 17:13 Eric_jiang 阅读(1738) | 评论 (0)编辑 收藏

仅列出标题
共57页: First 上一页 45 46 47 48 49 50 51 52 53 下一页 Last