具有分区问题的Sql Server主键

时间:2011-10-04 23:02:16

标签: sql-server sql-server-2008 filestream

我正在构建一个将被分区并包含FILESTREAM列的表。我遇到的问题是,似乎我必须有一个复合主键(FILE_IDFILE_UPLOADED_DATE),因为FILE_UPLOADED_DATE是我的分区方案的一部分。那是对的吗?我不希望这是一个复合键,只是让FILE_ID成为主键.....这可能只是一个用户错误?

任何建议都将不胜感激。

  

版本:SQL Server 2008 R2

分区方案和功能:

CREATE PARTITION FUNCTION DocPartFunction (datetime)
AS RANGE RIGHT FOR VALUES ('20101220')
GO
CREATE PARTITION SCHEME DocPartScheme AS
PARTITION DocPartFunction TO (DATA_FG_20091231, DATA_FG_20101231);
GO
CREATE PARTITION SCHEME DocFSPartScheme AS
PARTITION DocPartFunction TO (FS_FG_20091231,FS_FG_20101231);
GO

创建声明:

CREATE TABLE [dbo].[FILE](
    [FILE_ID] [int] IDENTITY(1,1) NOT NULL,
    [DOCUMENT] [varbinary](max) FILESTREAM  NULL,
    [FILE_UPLOADED_DATE] [datetime] NOT NULL,
    [FILE_INT] [int] NOT NULL,
    [FILE_EXTENSION] [varchar](10) NULL,
    [DocGUID] [uniqueidentifier] ROWGUIDCOL  NOT NULL UNIQUE ON [PRIMARY],
CONSTRAINT [PK_File] PRIMARY KEY CLUSTERED
    (   [FILE_ID] ASC
    ) ON DocPartScheme ([FILE_UPLOADED_DATE])
)ON DocPartScheme ([FILE_UPLOADED_DATE])
FILESTREAM_ON DocFSPartScheme;

如果我不包含FILE_UPLOADED_DATE,则会出错:

Msg 1908, Level 16, State 1, Line 1
Column 'FILE_UPLOADED_DATE' is partitioning column of the index 'PK_File'. Partition columns for a unique index must be a subset of the index key.
Msg 1750, Level 16, State 0, Line 1
Could not create constraint. See previous errors.

谢谢!

3 个答案:

答案 0 :(得分:10)

您将主键和聚簇索引混淆。这两者没有理由是同一个。您可以在FILE_UPLOADED_DATE上拥有聚簇索引,在FILE_ID上拥有单独的非群集主键。实际上,您已经为DocGUID列做了类似的事情:

CREATE TABLE [dbo].[FILE](
    [FILE_ID] [int] IDENTITY(1,1) NOT NULL,
    [DOCUMENT] [varbinary](max) FILESTREAM  NULL,
    [FILE_UPLOADED_DATE] [datetime] NOT NULL,
    [FILE_INT] [int] NOT NULL,
    [FILE_EXTENSION] [varchar](10) NULL,
    [DocGUID] [uniqueidentifier] ROWGUIDCOL  NOT NULL,
    constraint UniqueDocGUID UNIQUE NONCLUSTERED ([DocGUID]) 
        ON [PRIMARY])
    ON DocPartScheme ([FILE_UPLOADED_DATE])
    FILESTREAM_ON DocFSPartScheme;

CREATE CLUSTERED INDEX cdx_File 
   ON [FILE] (FILE_UPLOADED_DATE)
   ON DocPartScheme ([FILE_UPLOADED_DATE])
   FILESTREAM_ON DocFSPartScheme;

ALTER TABLE [dbo].[FILE]
    ADD CONSTRAINT PK_File PRIMARY KEY NONCLUSTERED (FILE_ID)
    ON [PRIMARY];

然而,这样的设计将导致不对齐的索引,这可能导致非常严重的性能问题,并且还会阻止所有快速分区切换操作。见Special Guidelines for Partitioned Indexes

  

每个排序表都需要最少的内存来构建。当你   正在构建一个与其基表对齐的分区索引,   使用较少的内存,一次构建一个排序表。但是,什么时候   您正在构建一个非对齐的分区索引,排序表是   同时建造。

     

因此,必须有足够的内存来处理这些问题   并发排序。分区数越大,内存越多   需要。每个分区表的最小大小为   40页,每页8千字节。例如,一个不对齐的   具有100个分区的分区索引需要足够的内存   同时对4,000(40 * 100)页进行连续排序。如果这个记忆   可用,构建操作将成功,但性能可能会   遭受。如果此内存不可用,则构建操作将失败

您的设计已经有一个DocGUID的非对齐索引,因此性能问题可能已经存在。如果必须保持索引一致,则必须承认选择分区方案的一个副作用:除非密钥包含分区键,否则不能再使用逻辑主键,也不能使用唯一约束强制执行。

最后,必须要问:为什么要使用分区表?它们始终比未分区的替代品慢。除非您需要ETL的快速分区切换操作(由于DocGUID上的非对齐索引,您已经开始使用),因此基本上没有动机使用分区表。 (抢先评论:FILE_UPLOADED_DATE上的聚集索引保证比'分区消除'更好的选择。)

答案 1 :(得分:4)

分区列必须始终存在于分区表的聚簇索引中。你提出的任何解决办法都必须考虑到这一点。

答案 2 :(得分:0)

我知道,这是一个古老的问题,但也许谷歌会引导别人来讨论这个问题:

一种可能的解决方案是不按日期列进行分区,而是按File_ID进行分区。每天/每周/每月(或您使用的任何时间段)您必须在午夜运行代理作业,Max(File_ID) file_uploadet_date < GetDate(),将下一个文件组添加到分区方案并进行拆分在MaxID + 1

当然,您仍然会遇到DocID上非对齐索引的问题,除非您将file_id添加到此唯一索引(可能导致非唯一DocIds)和/或检查其唯一性插入/更新触发器。