您的位置:首页 > 数据库

反驳“小议数据库主键选取策略(转)”

2007-11-21 11:50 302 查看
其实你们注意“主键”的同时而忽略了另外一个很重要的东西====〉“索引”

当我们建立一个主键的时候,系统会默认在这个主键上建立一个索引(这里说明一下,我是以MS Sql Server2000为例,其他厂商的数据库我不熟悉,不知道怎么样的),这个索引默认是CLUSTERED索引,也就是聚集索引(聚簇索引)

聚集索引对于数据都会进行排序,然后在索引的页面里面,分别按照数据页里面的索引字建立索引

如下图所示:

CREATE TABLE [tabInt] (

[ID] [int] IDENTITY (1, 1) NOT NULL ,

[myValue] [int] NULL ,

CONSTRAINT [PK_tabInt] PRIMARY KEY CLUSTERED

(

[ID]

) ON [PRIMARY]

) ON [PRIMARY]

GO

CREATE TABLE [tabUnq] (

[ID] [uniqueidentifier] NOT NULL CONSTRAINT [DF_tabUnq_ID] DEFAULT (newid()),

[myValue] [int] NULL ,

CONSTRAINT [PK_tabUnq] PRIMARY KEY CLUSTERED

(

[ID]

) ON [PRIMARY]

) ON [PRIMARY]

GO

CREATE TABLE [tabComb] (

[ID] [uniqueidentifier] NOT NULL ,

[myValue] [int] NULL ,

CONSTRAINT [PK_tabComb] PRIMARY KEY CLUSTERED

(

[ID]

) ON [PRIMARY]

) ON [PRIMARY]

GO

CREATE TABLE [tabUnq2] (

[ID] [uniqueidentifier] NOT NULL CONSTRAINT [DF_tabUnq2_ID] DEFAULT (newid()),

[myValue] [int] NULL ,

CONSTRAINT [PK_tabUnq2] PRIMARY KEY NONCLUSTERED

(

[ID]

) ON [PRIMARY]

) ON [PRIMARY]

GO

declare @i bigint

declare @v int

declare @s datetime

declare @e datetime

set @i=0

set @s=getdate()

print 'int类型为主键的表插入开始时间:' + cast(@s as nvarchar)

while @i<10000

begin

set @v=rand()

insert into tabInt ([myValue]) values (@v)

set @i=@i+1

end

set @e =getdate()

print 'int类型为主键的表插入结束时间:' + cast(@e as nvarchar)

print 'int类型为主键的表插入共用时间:' + cast (DATEDIFF(ms, @s, @e) as nvarchar) + '毫秒'

print ' '

print ' '

set @i=0

set @s=getdate()

print 'GUID(聚簇索引)类型为主键的表插入开始时间:' + cast(@s as nvarchar)

while @i<10000

begin

set @v=rand()

insert into tabUnq ([myValue]) values (@v)

set @i=@i+1

end

set @e =getdate()

print 'GUID类型(聚簇索引)为主键的表插入结束时间:' + cast(@e as nvarchar)

print 'GUID类型(聚簇索引)为主键的表插入共用时间:'+ cast (DATEDIFF(ms, @s, @e) as nvarchar) + '毫秒'

print ' '

print ' '

set @i=0

set @s=getdate()

print 'GUID(非聚簇索引)类型为主键的表插入开始时间:' + cast(@s as nvarchar)

while @i<10000

begin

set @v=rand()

insert into tabUnq2 ([myValue]) values (@v)

set @i=@i+1

end

set @e =getdate()

print 'GUID类型(非聚簇索引)为主键的表插入结束时间:' + cast(@e as nvarchar)

print 'GUID类型(非聚簇索引)为主键的表插入共用时间:'+ cast (DATEDIFF(ms, @s, @e) as nvarchar) + '毫秒'

print ' '

print ' '

DECLARE @aGuid UNIQUEIDENTIFIER

set @i=0

set @s=getdate()

print 'COMB类型为主键的表插入开始时间:' + cast(@s as nvarchar)

while @i<10000

begin

SET @aGuid = CAST(CAST(NEWID() AS BINARY(10)) + CAST(GETDATE() AS BINARY(6)) AS UNIQUEIDENTIFIER)

set @v=rand()

insert into tabComb ([ID],[myValue]) values (@aGuid,@v)

set @i=@i+1

end

set @e =getdate()

print 'COMB类型为主键的表插入结束时间:' + cast(@e as nvarchar)

print 'COMB类型为主键的表插入共用时间:' + cast (DATEDIFF(ms, @s, @e) as nvarchar) + '毫秒'

建立了4个表,分别用int(聚簇索引),GUID(聚簇索引),GUID(聚簇索引),以及COMB类型来做为主键

然后分别插入10000条数据

结果如下:

int类型为主键的表插入开始时间:07 19 2004 2:14PM

int类型为主键的表插入结束时间:07 19 2004 2:14PM

int类型为主键的表插入共用时间:7750毫秒

GUID(聚簇索引)类型为主键的表插入开始时间:07 19 2004 2:14PM

GUID类型(聚簇索引)为主键的表插入结束时间:07 19 2004 2:14PM

GUID类型(聚簇索引)为主键的表插入共用时间:8193毫秒

GUID(非聚簇索引)类型为主键的表插入开始时间:07 19 2004 2:14PM

GUID类型(非聚簇索引)为主键的表插入结束时间:07 19 2004 2:14PM

GUID类型(非聚簇索引)为主键的表插入共用时间:7540毫秒

COMB类型为主键的表插入开始时间:07 19 2004 2:14PM

COMB类型为主键的表插入结束时间:07 19 2004 2:14PM

COMB类型为主键的表插入共用时间:7880毫秒

我们会看到,在这里,采用GUID来做主键(非聚簇索引),它的速度是最快的

当然,采用非聚簇索引也有不方便的地方,那就是不能排序了。

MSDN中对于非聚簇索引的应用场景建议如下:

包含大量非重复值的列,如姓氏和名字的组合(如果聚集索引用于其它列)。如果只有很少的非重复值,如只有 1 和 0,则大多数查询将不使用索引,因为此时表扫描通常更有效。
不返回大型结果集的查询。
返回精确匹配的查询的搜索条件(WHERE 子句)中经常使用的列。
经常需要联接和分组的决策支持系统应用程序。应在联接和分组操作中使用的列上创建多个非聚集索引,在任何外键列上创建一个聚集索引。
在特定的查询中覆盖一个表中的所有列。这将完全消除对表或聚集索引的访问。

而对于聚簇索引,建议的应用场景如下:

包含大量非重复值的列。
使用下列运算符返回一个范围值的查询:BETWEEN、>、>=、< 和 <=。
被连续访问的列。
返回大型结果集的查询。
经常被使用联接或 GROUP BY 子句的查询访问的列;一般来说,这些是外键列。对 ORDER BY 或 GROUP BY 子句中指定的列进行索引,可以使 SQL Server 不必对数据进行排序,因为这些行已经排序。这样可以提高查询性能。
OLTP 类型的应用程序,这些程序要求进行非常快速的单行查找(一般通过主键)。应在主键上创建聚集索引。

所以,在这里,如果只是为了需要一个唯一标示符来做为主键,并且在这个字段上不需要做什么排序呀,范围对比等操作的话,那么我们可以放心大胆的使用GUID来做为主键,但是记得要把它设置成为非聚簇索引。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: