首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 数据库 > SQL Server >

SQL Server 目录的疑问

2013-11-20 
SQL Server 索引的疑问最近在SQL Server 2008 R2上调优从SQL Server 2000升级上来的系统所以经常接触索引,

SQL Server 索引的疑问
最近在SQL Server 2008 R2上调优从SQL Server 2000升级上来的系统
所以经常接触索引,对索引优化也有一点认识和疑问,提几个问题大家讨论一下

1.网上到处都是说索引字段中不能有null值或者说不能在索引字段中使用is null 或者 is not null
在CSDN上还找到一个帖子说is null 可以is not null不可以,还给出了测试代码和测试数据

这个讨论应该不是空穴来风的,因为在我的系统上也曾经出现过一些索引中使用null的怪现象
我的一个表建立了一个非聚集索引(多字段的),我的一个查询使用到 is null 查询该索引的某个字段
(当然条件是复杂的了)结果非常慢,效率很低,后来没办法什么办法都试了,最后试到一个办法就是使用
SET ANSI_NULLS OFF选项,吧null的不可比较关闭,然后用=号查找效率就上来了,但最近升级到2008
后在联机文档中看到SET ANSI_NULLS OFF标志将在以后版本不在支持了,我现在想知道的确切答案是
到底什么情况下索引中字段与null的之间的比较不会降低效率,怎么提高这种索引字段中含有null的
查询的查询效率吗(难道非得使用默认值不能使用null吗),请大家给出样板数据和执行计划说明,实操一下
不然人云亦云理所当然,我在SQL Server 2008 R2 上测试是不管我怎么写查询条件
 is null ,is not null, not in (),<> 等等都可以使用索引

2.还有一个就是全表扫描的问题,我今天特意想试一下怎么写查询条件能够避免全表扫描,结果试了一些
网上说会出现全表扫描的写法,结果我在执行计划中还是不能看到全表扫描,很奇怪啊,一个很明显会全表
扫描的例子 select * from table where a like '%1%' (a没有索引,table记录数在1万条左右)
结果在执行计划中也没看到全表扫描的计划,而是使用了聚集索引查找,我的table有聚集索引,但不是
在a字段上.后来不管我怎么写,还是没办法遇上全表扫描的计划,神啊,呵呵,想他出现的时候却不出现
不想他出现的时候却到来.有谁能给出一个样板例子(最好代码创建样板数据),在 SQL Server 2008 R2
上使用索引和不能使用索引的例子呢?


PS:网上很所优化的文章今天我试都是没效果的,或者说根本和文章说讲得不一样,很可能是因为现在
查询优化已经很智能了,以前很多不能优化的查询现在都可以优化了,举一个这次我优化中遇到的一个
问题说大家讨论一下,一个where如果有多个条件用and连接,那么这几个条件的先后顺序是有讲究的
例如把计算复杂度很高的写在前面(前还是后这个就要看SQL Server是怎么分析SQL产生执行计划了,
有人说是从后面往前分析,所以应该把简单的条件写在后面),那么就会大大增加运算时间,不知道SQL
的and 连接是不是断路的(例如.NET里面的andalso),因为第一个条件不成立我就没必要去计算第二个
条件了.我就曾经优化过一个if ... and ...这样的条件语句,把简单的判断写在前面,结果执行时间
大大降低,效率提高几倍.但奇怪的是我试了一下这样的写法:select * from tableA where a=1 
and exists(select * from tableB where tableB.b=tableA.a)
这个查询不管我怎么调换两个条件的顺序,查询时间都是差不多的,优化器是怎么做到的啊!!!
难道优化器能够优化到分析每一个and的复杂度然后先执行复杂度低的???
[解决办法]
这个是实验代码,从4中,我们能够看到v(key)列中有null,说明了索引中也包含了null值。

最后的查询,通过 is null,发现也是走的索引查找。



--1.建表
if OBJECT_ID('t1') is not null
   drop table t1
go

create table t1
(
id int primary key,
v varchar(20)
)


insert into t1
select 1 ,'aa' union all
select 2 ,'bb' union all
select 3 ,'cc' union all
select 4 ,'dd' union all
select 5 ,'ee' union all
select 6 ,'ff' union all
select 7 ,null union all
select 8 ,'gg' union all
select 9 ,null
go


--2.创建一个非聚集索引
create index idx_t1_v on t1(v)
go


--3.index_id = 2 为idx_t1_v索引
select *
from sys.indexes
where object_id = object_id('t1')


--hobt_id = 72057594041466880
select *
from sys.partitions
where index_id = 2
      and object_id = object_id('t1')


--root_page = 0xAF0000000100
select *
from sys.system_internals_allocation_units
where container_id = 72057594041466880



--4.0100:文件id,而AF是16进制,转化为10进制是 175,pageId = 175
DBCC page(12,   --数据库id : 10  
          1,    --文件id: 1  
          175,  --页id: 188  
          3) --with tableresults  
/*
文件id  页id    索引行号 层级 v列的值 这个v列值所对应的行号 id列的hash值
FileIdPageIdRowLevelv (key)id (key)KeyHashValue
1    175    00    NULL7        (8d4dc9cd25b3)
1    175    10    NULL9        (fd07a7dffc59)
1    175    20    aa    1        (e5e354933dff)
1    175    30    bb    2        (df47e5a393e1)
1    175    40    cc    3        (36248ab30914)
1    175    50    dd    4        (40ee14f42575)
1    175    60    ee    5        (a98d7be4bf80)
1    175    70    ff    6        (9329cad4119e)


1    175    80    gg    8        (f34ca041b78d)
*/     
     
  
 
--5.走的索引查找        
select *
from t1
where v is null


--走的是索引扫描,之所以这里走索引,是因为在v列的非聚集索引中,也包含了所有的数据
select *
from t1
where v like '%a%'



SQL Server 目录的疑问

[解决办法]
是的。查找是seek。堆表(没有聚集索引)和聚集索引是表的两种存储结构。聚集索引扫描的机制其实和表扫描几乎一样,所以很多文献都会提到表扫描和聚集索引扫描性能相差无几。

select a from table where b like '%1%'
当表上有聚集索引之后,你可以理解为表已经不再是“堆表”,换了一种存储格式,也就是按B-Tree存储,扫描的时候就会遍历所有节点。你这个查询,如果没有聚集索引,且没有非聚集索引的话,就是表扫描,有非聚集索引的话,应该是索引扫描。
[解决办法]
对于第二个问题,我觉得你把所有索引删了试试,这样你就会看到你想看到的结果了。

表扫描,也就是TABLE SCAN   是因为这个表是堆表。数据的存放是不会按照任何一个字段的顺序来存放的。也就说数据存放的物理顺序是随机的。

聚集索引扫描  也就是clustered scan   这是因为表有聚集索引,那么聚集索引是跟表的数据复制版本存放在一起的,数据是按照聚集索引指定的顺序存放,物理顺序不是随机的。
[解决办法]
回答你的1,2问题和PS的

第一个,索引里的NULL值,对效率没影响。网上不知道怎么说的,我唯一看到相关的有价值的文章是讨论NULL值的存储空间的。结论是NULL值比空字符串更占空间,所以尽量少存储NULL值很有必要。对于你的测试,基于之前你那篇帖子里的测试方法,以及我粗略的看的你这篇帖子的一些话,我认为你测试的样例有问题,导致你的结果不可靠。
我之前测试过NULL值的查找效率,同一个字段存储N个NULL值和N个空字符串和N个超长字符,查找效率是一样的,计划也没有什么不同,目前还没碰到过差异很大的案例。如果碰到了可能我会研究一下,但仅从现在我掌握的知识和碰到的案例来说,完全没影响。

IS NOT NULL比IS NULL傻这个是有的,不过说实话傻点也是索引查找,对效率影响并不大。

第二个,你还是没明白聚集索引的意思,上面t啥啥的已经说过了,建了聚集索引就没有表扫描只有聚集索引扫描。聚集表的表扫描就是聚集索引扫描。建议你先去读一遍MSDN的表和索引结构,然后再就你心中的疑问做测试。

你PS里提到的条件顺序问题,很确定的告诉你,2005以上的SELECT不分顺序,排出花来也是那个计划。你的if不是SELECT,所以可能有顺序。

热点排行