提供如下资料仅供参考:MS SQL Server:分区表、分区索引 详解http://space.itpub.net/7705285/viewspace-4175581. 分区表简介 使用分区表的主要目的,是为了改善大型表以及具有各种访问模式的表的可伸缩性和可管理性。? 大型表:数据量巨大的表。 ? 访问模式:因目的不同,需访问的不同的数据行集,每种目的的访问可以称之为一种访问模式。分区一方面可以将数据分为更小、更易管理的部分,为提高性能起到一定的作用;另一方面,对于如果具有多个CPU的系统,分区可以是对表的操作通过并行的方式进行,这对于提升性能是非常有帮助的。注意:只能在 SQL Server Enterprise Edition 中创建分区函数。只有 SQL Server Enterprise Edition 支持分区。 2. 创建分区表或分区索引的步骤 可以分为以下步骤: 1. 确定分区列和分区数 2. 确定是否使用多个文件组 3. 创建分区函数 4. 创建分区架构(Schema) 5. 创建分区表 6. 创建分区索引下面详细描述的创建分区表、分区索引的步骤。 2.1. 确定分区列和分区数 在开始做分区操作之前,首先要确定待分区表的访问模式,该模式决定了什么列适合做分区键。例如,对于销售数据,一般会先根据日期把数据范围限定在一个范围内,然后在这个基础上做进一步的查询,这样,就可以把日期作为分区列。确定了分区列之后,需要进一步确定分区数,亦即分区表中需要包含多少数据,每个分区的数据应该限定在哪个范围。2.2. 确定是否使用多个文件组 为了有助于优化性能和维护,应该使用文件组分离数据。一般情况下,如果经常对分区的整个数据集操作,则文件组数最好与分区数相同,并且这些文件组通常应该位于不同的磁盘上,再配合多个CPU,则SQL Server 可以并行处理多个分区,从而大大缩短处理大量复杂报表和分析的总体时间。2.3. 创建分区函数 分区函数用于定义分区的边界条件,创建分区函数的语法如下: CREATE PARTITION FUNCTION partition_function_name ( input_parameter_type ) AS RANGE [ LEFT | RIGHT ] FOR VALUES ( [ boundary_value [ ,...n ] ] ) [ ; ]参数说明: ? partition_function_name 是分区函数的名称。分区函数名称在数据库内必须唯一,并且符合标识符的规则。? input_parameter_type 是用于分区的列的数据类型。当用作分区列时,除 text、ntext、image、xml、timestamp、varchar(max)、nvarchar(max)、varbinary(max)、别名数据类型或 CLR 用户定义数据类型外,所有数据类型均有效。实际列(也称为分区列)是在 CREATE TABLE 或 CREATE INDEX 语句中指定的。? boundary_value 为使用 partition_function_name 的已分区表或索引的每个分区指定边界值。如果 boundary_value 为空,则分区函数使用 partition_function_name 将整个表或索引映射到单个分区。只能使用 CREATE TABLE 或 CREATE INDEX 语句中指定的一个分区列。boundary_value 是可以引用变量的常量表达式。这包括用户定义类型变量,或函数以及用户定义函数。它不能引用 Transact-SQL 表达式。boundary_value 必须与 input_parameter_type 中提供的数据类型相匹配或者可隐式转换为该数据类型,并且如果该值的大小和小数位数与 input_parameter_type 中相应的值的大小和小数位数不匹配,则在隐式转换过程中该值不能被截断。注意: 如果 boundary_value 包含 datetime 或 smalldatetime 文字值,则为这些文字值在计算时假设 us_english 是会话语言。不推荐使用此行为。要确保分区函数定义对于所有会话语言都具有预期的行为,建议使用对于所有语言设置都以相同方式进行解释的常量,例如 yyyymmdd 格式;或者将文字值显式转换为特定样式。有关详细信息,请参阅编写国际化 Transact-SQL 语句。若要确定服务器的语言会话,请运行 SELECT @@LANGUAGE。? ...n 指定 boundary_value 提供的值的数目,不能超过 999。所创建的分区数等于 n + 1。不必按顺序列出各值。如果值未按顺序列出,则 Microsoft SQL Server 2005 数据库引擎 将对它们进行排序,创建函数并返回一个警告,说明未按顺序提供值。如果 n 包括任何重复的值,则数据库引擎将返回错误。? LEFT | RIGHT 指定当间隔值由 数据库引擎 按升序从左到右排序时,boundary_value [ ,...n ] 属于每个边界值间隔的哪一侧(左侧还是右侧)。如果未指定,则默认值为 LEFT。创建分区函数示例: CREATE PARTITION FUNCTION PF_Left(int) AS RANGE LEFT FOR VALUES(10, 20) GOCREATE PARTITION FUNCTION PF_Right(int) AS RANGE LEFT FOR VALUES(10, 20) GOPF_Left 和 PF_Right 分区函数的区分: 分区函数 分区1 分区2 分区3 PF_Left <= 10 > 10 and <= 20 > 20 PF_Right < 10 >= 10 and < 20 >= 202.4. 创建分区架构(Schema) 创建分区函数后,必须将其与分区架构(Schema)相关联,以便将分区定向至特定的文件组。定义分区架构师,即使多个 分区位于同一个文件组中,也必须为每个分区指定一个文件组。创建分区架构的语法如下: GOCREATE PARTITION SCHEME partition_scheme_name AS PARTITION partition_function_name [ ALL ] TO ( { file_group_name | [ PRIMARY ] } [ ,...n ] ) [ ; ]参数: ? partition_scheme_name 分区方案的名称。分区方案名称在数据库中必须是唯一的,并且符合标识符规则。? partition_function_name 使用分区方案的分区函数的名称。分区函数所创建的分区将映射到在分区方案中指定的文件组。partition_function_name 必须已经存在于数据库中。? ALL 指定所有分区都映射到在 file_group_name 中提供的文件组,或映射到主文件组(如果指定了 [PRIMARY]。如果指定了 ALL,则只能指定一个 file_group_name。? file_group_name | [ PRIMARY ] [ ,...n] 指定用来持有由 partition_function_name 指定的分区的文件组的名称。file_group_name 必须已经存在于数据库中。如果指定了 [PRIMARY],则分区将存储于主文件组中。如果指定了 ALL,则只能指定一个 file_group_name。分区分配到文件组的顺序是从分区 1 开始,按文件组在 [,...n] 中列出的顺序进行分配。在 [,...n] 中,可以多次指定同一个 file_group_name。如果 n 不足以拥有在 partition_function_name 中指定的分区数,则 CREATE PARTITION SCHEME 将失败,并返回错误。如果 partition_function_name 生成的分区数少于文件组数,则第一个未分配的文件组将标记为 NEXT USED,并且出现显示命名 NEXT USED 文件组的信息。如果指定了 ALL,则单独的 file_group_name 将为该 partition_function_name 保持它的 NEXT USED 属性。如果在 ALTER PARTITION FUNCTION 语句中创建了一个分区,则 NEXT USED 文件组将再接收一个分区。若要再创建一个未分配的文件组来拥有新的分区,请使用 ALTER PARTITION SCHEME。在 file_group_name[ 1,...n] 中指定主文件组时,必须像在 [PRIMARY] 中那样分隔 PRIMARY,因为它是关键字。创建分区架构示例: CREATE PARTITION FUNCTION myRangePF1 (int) AS RANGE LEFT FOR VALUES (1, 100, 1000); GO CREATE PARTITION SCHEME myRangePS1 AS PARTITION myRangePF1 TO (test1fg, test2fg, test3fg, test4fg); GO2.5. 创建分区表 定义了分区函数(逻辑结构)和 分区架构(物理结构)后,既可以创建分区表来利用它们。分区表定义应使用的分区架构,而分区架构又定义其使用的分区函数。要将这三者结合起来,必须指定应用于分区函数的 列 。范围分区始终只映射到表中的一列。CREATE TABLE 语法如下: CREATE TABLE [ database_name . [ schema_name ] . | schema_name . ] table_name ( { <column_definition> | <computed_column_definition> } [ <table_constraint> ] [ ,...n ] ) [ ON { partition_scheme_name ( partition_column_name ) | filegroup | "default" } ] [ { TEXTIMAGE_ON { filegroup | "default" } ] [ ; ]示例如下: CREATE TABLE myRangePT1 ( ID int not null, AGE int, PRIMARY KEY (ID) ) ON myRangePS1(myRangePF1) GO 2.6. 创建分区索引 索引对于提高查询性能非常有效,因此,一般应该考虑应该考虑为分区表建立索引,为分区表建立索引与为普通表建立索引的语法一直,但是,其行为与普通索引有所差异。默认情况下,分区表中创建的索引使用与分区表相同分区架构和分区列,这样,索引将于表对齐。将表与其索引对齐,可以使管理工作更容易进行,对于滑动窗口方案尤其如此。若要启动分区切换,表的所有索引都必须对齐。在创建索引时,也可以指定不同的分区方案(Schema)或单独的文件组(FileGroup)来存储索引,这样SQL Server 不会将索引与表对齐。在已分区的表上创建索引(分区索引)时,应该注意以下事项: ? 唯一索引 建立唯一索引(聚集或者非聚集)时,分区列必须出现在索引列中。此限制将使SQL Server只调查单个分区,并确保表中 宠物的新键值。如果分区依据列不可能包含在唯一键中,则必须使用DML触发器,而不是强制实现唯一性。? 非唯一索引 对非唯一的聚集索引进行分区时,如果未在聚集键中明确指定分区依据列,默认情况下SQL Server 将在聚集索引列中添加分区依据列。 对非唯一的非聚集索引进行分区时,默认情况下SQL Server 将分区依据列添加为索引的包含性列,以确保索引与基表对齐,若果索引中已经存在分区依据列,SQL Server 将不会像索引中添加分区依据列。3. 分区操作 分区适用于可以缩放的大型表,所以随着时间和环境的变化,就会产生对分区的拆分、合并、移动的需求。 3.1. 拆分与合并分区 通过拆分或合并边界值更改分区函数。通过执行 ALTER PARTITION FUNCTION,可以将使用分区函数的任何表或索引的某个分区拆分为两个分区,也可以将两个分区合并为一个分区。
注意:多个表或索引可以使用同一分区函数。ALTER PARTITION FUNCTION 在单个事务中影响所有这些表或索引。
ALTER PARTITION FUNCTION 语法如下: ALTER PARTITION FUNCTION partition_function_name() { SPLIT RANGE ( boundary_value ) | MERGE RANGE ( boundary_value ) } [ ; ]参数说明: ? partition_function_name 要修改的分区函数的名称。? SPLIT RANGE ( boundary_value ) 在分区函数中添加一个分区。boundary_value 确定新分区的范围,因此它必须不同于分区函数的现有边界范围。根据 boundary_value,Microsoft SQL Server 2005 数据库引擎 将某个现有范围拆分为两个范围。在这两个范围中,新 boundary_value 所在的范围被视为是新分区。重要提示: 文件组必须处于联机状态,并且必须由使用此分区函数的分区方案标记为 NEXT USED,以保存新分区。在 CREATE PARTITION SCHEME 语句中,将把文件组分配给分区。如果 CREATE PARTITION SCHEME 语句分配了多余的文件组(在 CREATE PARTITION FUNCTION 语句中创建的分区数少于用于保存它们的文件组),则存在未分配的文件组,分区方案将把其中的某个文件组标记为 NEXT USED。该文件组将保存新的分区。如果分区方案未将任何文件组标记为 NEXT USED,则必须使用 ALTER PARTITION SCHEME 添加一个文件组或指定一个现有文件组来保存新分区。可以指定已保存分区的文件组来保存附加分区。由于一个分区函数可以参与多个分区方案,因此所有使用分区函数(您向其中添加了分区)的分区方案都必须拥有一个 NEXT USED 文件组。否则,ALTER PARTITION FUNCTION 将失败并出现错误,该错误显示缺少 NEXT USED 文件组的一个或多个分区方案。? MERGE [ RANGE ( boundary_value) ] 删除一个分区并将该分区中存在的所有值都合并到剩余的某个分区中。RANGE (boundary_value) 必须是一个现有边界值,已删除分区中的值将合并到该值中。如果最初保存 boundary_value 的文件组没有被剩余分区使用,也没有使用 NEXT USED 属性进行标记,则将从分区方案中删除该文件组。合并的分区驻留在最初不保存 boundary_value 的文件组中。boundary_value 是一个可以引用变量(包括用户定义类型变量)或函数(包括用户定义函数)的常量表达式。它无法引用 Transact-SQL 表达式。boundary_value 必须匹配或可以隐式转换为其对应列的数据类型,并且当值的大小和小数位数不匹配其对应 input_parameter_type 时,将无法在隐式转换过程中被截断。示例: ALTER PARTITION SCHEME PS_HistoryArchive NEXT USED [PRIMARY]备注: ALTER PARTITION FUNCTION 在单个原子操作中对使用该函数的任何表和索引进行重新分区。但该操作在脱机状态下进行,并且根据重新分区的范围,可能会消耗大量资源。ALTER PARTITION FUNCTION 只能用于将一个分区拆分为两个分区,或将两个分区合并为一个分区。若要更改其他情况下对表进行分区方法(例如,将 10 个分区合并为 5 个分区),可以尝试使用以下任何选项。根据系统配置,这些选项可能在资源消耗方面有所不同:? 使用所需的分区函数创建一个新的已分区表,然后使用 INSERT INTO...SELECT FROM 语句将旧表中的数据插入新表。? 为堆创建分区聚集索引。 注意: 删除已分区的聚集索引将产生分区堆。? 通过将 Transact-SQL CREATE INDEX 语句与 DROP EXISTING = ON 子句一起使用来删除并重新生成现有的已分区索引。? 执行一系列 ALTER PARTITION FUNCTION 语句。ALTER PARITITION FUNCTION 所影响的全部文件组都必须处于联机状态。 如果使用分区函数的任何表中存在已禁用的聚集索引,ALTER PARTITION FUNCTION 都将失败。 Microsoft SQL Server 2005 不对修改分区函数提供复制支持。必须在订阅数据库中手动应用对发布数据库中的分区函数的更改。3.2. 移动分区数据 可以使用 ALTER TABLE ....... SWITCH 语句按一下方式快速有效地移动数据子集: ? 将某个表中的数据移动到另一个表中; ? 将某个表作为分区添加到现存的已分区表中; ? 将分区从一个已分区表切换到另一个已分区表; ? 删除分区以形成单个表。使用这些方案移动数据时,无论集合有多大,此方案都能快速有效地进行传输,因为操作并不以物理方式移动数据,只有关于存储位置的元数据会从一个分区变为另一个分区。 ALTER TABLE .... SWITCH 的语法如下: ALTER TABLE [ database_name . [ schema_name ] . | schema_name . ] table_name { SWITCH [ PARTITION source_partition_number_expression ] TO [schema_name].target_table [ PARTITION target_partition_number_expression ] } [ ; ]
分区函数是数据库中的一个独立对象,它将表的行映射到一组分区,所以分区函数解决的是HOW的问题,即表如何分区的问题。创建分区函数时,必须指明数据分区的边界点以及分区依据列,这样便知道如何对表或索引进行分区。分区函数的创建语法如下: CREATE PARTITION FUNCTION partition_function_name ( input_parameter_type ) AS RANGE [ LEFT | RIGHT ] FOR VALUES ( [ boundary_value [ ,...n ] ] ) [ ; ] 分区函数语法的相关解释: 1, 创建一个分区函数和创建一个普通的数据库对象(例如表)没什么区别。所以根据标准语法走就OK了。 2, partition_function_name是分区函数的名称。分区函数名称在数据库内必须唯一,并且符合标识符的规则。 3, input_parameter_type是用于分区的列的数据类型,习惯把它称为分区依据列。当用作分区列时,除 text、ntext、image、xml、timestamp、varchar(max)、nvarchar(max)、varbinary(max)、别名数据类型或 CLR 用户定义数据类型外,其他所有数据类型均有效。分区依据列是在 CREATE TABLE 或 CREATE INDEX 语句中指定的。 4, boundary_value [ ,...n ]中的boundary_value是边界值(或边界点的值),n代表可以最多有n个边界值,即n指定 boundary_value 提供的值的数目,但n不能超过 999。所创建的分区数等于 n + 1。不必按顺序列出各值。如果值未按顺序列出,则 Database Engine 将对这些边界值进行排序,创建分区函数并返回一个警告,说明未按顺序提供值。如果 n 包括任何重复的值,则数据库引擎将返回错误。边界值的取值一定是和分区依据列相关的,所以只能使用 CREATE TABLE 或 CREATE INDEX 语句中指定的一个分区列。 5, LEFT | RIGHT 指定boundary_value [ ,...n ] 的每个boundary_value属于每个边界值间隔的哪一侧(左侧还是右侧)。如果未指定,则默认值为 LEFT。 例如我们可以依据某个表的int列来创建分区函数: create partition function MyPF1(int) range left --默认是left,所以可以省略left for values(500000,1000000,1500000) 很明显,这个分区函数创建了4个分区,因为此时n=3,所以分区总数是n+1=4。而那个int分区依据列表明将要分区的那个表里面一定有一列是int类型,是分区依据列。这个分区函数我们用的是range left,各个分区的取值范围如下表: 分区 取值范围 1 (负无穷,500000] 2 [500001,1000000] 3 [1000001,1500000] 4 [1500001,正无穷) 如果换成range right,即创建分区函数时代码如下: create partition function MyPF1(int) range right for values(500000,1000000,1500000) 那么各个分区的取值范围如下表: 分区 取值范围 1 (负无穷,499999] 2 [500000,999999] 3 [1000000,1499999] 4 [1500000,正无穷) 我们还可以根据日期列创建分区函数,例如: create partition function MyPF2(datetime) range right for values('2008/01/01', '2009/01/01') 这个分区函数非常适合查询和归档某一年的数据。各个分区的取值范围如下表: 分区 取值范围 1 <=2007/12/31 2 [2008/01/01,2008/12/31] 3 >=2009/01/01 当然我们也可以根据月份分区,而分区依据列支持的数据类型非常多,参照项目的实际情况选择最能表示分区的列类型。分区方案 对表和索引进行分区的第二步是创建分区方案。分区方案定义了一个特定的分区函数将使用的物理存储结构(其实就是文件组),或者说是分区方案将分区函数生成的分区映射到我们定义的一组文件组。所以分区方案解决的是Where的问题,即表的各个分区在哪里存储的问题。分区方案的创建语法如下: CREATE PARTITION SCHEME partition_scheme_name AS PARTITION partition_function_name [ ALL ] TO ( { file_group_name | [ PRIMARY ] } [ ,...n ] ) [ ; ]
分区方案语法的相关解释: 1, 创建分区方案时,根据分区函数的参数,定义映射表分区的文件组。必须指定足够的文件组来容纳分区数。可以指定所有分区映射到不同文件组、某些分区映射到单个文件组或所有分区映射到单个文件组。如果您希望在以后添加更多分区,还可以指定其他“未分配的”文件组。在这种情况下,SQL Server 用 NEXT USED 属性标记其中一个文件组。这意味着该文件组将包含下一个添加的分区。一个分区方案仅可以使用一个分区函数。但是,一个分区函数可以参与多个分区方案。 2, partition_scheme_name 是分区方案的名称。分区方案名称在数据库中必须是唯一的,并且符合标识符规则。 3, partition_function_name 是使用当前分区方案的分区函数的名称。分区函数所创建的分区将映射到在分区方案中指定的文件组。partition_function_name 必须已经存在于数据库中。 4, ALL 指定所有分区都映射到在 file_group_name 中提供的同一个文件组,或映射到主文件组(如果指定了 [PRIMARY])。如果指定了 ALL,则只能指定一个 file_group_name。 5, file_group_name | [ PRIMARY ] [ ,...n] 代表n个文件组。和分区函数中的各个分区对应。文件组必须已经存在于数据库中。 如果指定了 [PRIMARY],则分区将存储于主文件组中。如果指定了 ALL,则只能指定一个 file_group_name。分区分配到文件组的顺序是从分区 1 开始,按文件组在 [,...n] 中列出的顺序进行分配。在 [,...n] 中,可以多次指定同一个文件组。如果 n 不足以拥有在分区函数中指定的分区数,则 CREATE PARTITION SCHEME 将失败,并返回错误。 6, 如果分区函数生成的分区数少于创建分区方案时提供的文件组数,则分区方案中第一个未分配的文件组将被标记为 NEXT USED,并且出现显示命名 NEXT USED 文件组的信息。如果指定了 ALL,则单独的文件组将为该分区函数保持它的NEXT USED 属性。如果在 ALTER PARTITION FUNCTION 语句中创建了一个分区,则 NEXT USED 文件组将再接收一个分区。若要再创建一个未分配的文件组来拥有新的分区,请使用 ALTER PARTITION SCHEME。 分区方案例子1:下面的代码先创建一个分区函数,然后再创建这个分区函数使用的分区方案,这个分区方案将每个分区映射到不同文件组。代码如下: create partition function MyPF1(int) as range left for values(500000,1000000,1500000) go create partition scheme MyPS1 as partition MyPF1 to (fg1, fg2, fg3, fg4) 文件组、分区和分区边界值范围之间的关系如下表: 文件组 分区 取值范围 fg1 1 (负无穷,500000] fg2 2 [500001,1000000] fg3 3 [1000001,1500000] fg4 4 [1500001,正无穷) 分区方案例子2:下面的代码先创建一个分区函数,然后再创建这个分区函数使用的分区方案,这个分区方案将多个分区映射到同一个文件组。代码如下: create partition function MyPF2(int) as range left for values(500000,1000000,1500000) go create partition scheme MyPS2 as partition MyPF2 to (fg1, fg1, fg1, fg2) 文件组、分区和分区边界值范围之间的关系如下表: 文件组 分区 取值范围 fg1 1 (负无穷,500000] Fg1 2 [500001,1000000] Fg1 3 [1000001,1500000] Fg2 4 [1500001,正无穷) 分区方案例子3:下面的代码先创建一个分区函数,然后再创建这个分区函数使用的分区方案,这个分区方案将所有分区映射到同一个文件组。代码如下: create partition function MyPF3 (int) as range left for values(500000,1000000,1500000) go create partition scheme MyPS3 as partition MyPF3 all to (fg1) 文件组、分区和分区边界值范围之间的关系如下表: 文件组 分区 取值范围 fg1 1 (负无穷,500000] Fg1 2 [500001,1000000] Fg1 3 [1000001,1500000] Fg1 4 [1500001,正无穷) 分区方案例子4:下面的代码先创建一个分区函数,然后再创建这个分区函数使用的分区方案,这个分区方案指定了“NEXT USED”文件组。代码如下: create partition function MyPF4(int) as range left for values(500000,1000000,1500000) --4个分区 go create partition scheme MyPS4 as partition MyPF4 to (fg1, fg2, fg3, fg4, fg5) --5个文件组 那么文件组fg5将自动被标记为“NEXT USED”文件组。 分区方案例子5:下面的代码先创建一个分区函数,然后再创建这个分区函数使用的分区方案,这个分区方案指定了“[primary]”文件组。代码如下: create partition function MyPF5(datetime) range right for values('2008/01/01', '2009/01/01') go create partition scheme MyPS5 as partition MyPF5 to([primary], fg1, fg2)
最后必须明白一点,一张表最多只能有1000个分区。
分区表
在分区函数和分区方案创建完成后,创建分区表的准备工作已经完成。我们看一个完整的例子,代码如下: --创建分区函数 create partition function MyPF(datetime) range right for values('2007-1-1', '2008-1-1') go --创建分区方案 create partition scheme MyPS as partition MyPF to(fg1, fg2, fg3) go --创建分区表 create table orders ( OrderID int identity(1,1) primary key, OrderDate datetime, CustID varchar(10) ) on MyPS(OrderDate)
使用分区表的主要目的,是为了改善大型表以及具有各种访问模式的表的可伸缩性和可管理性。? 大型表:数据量巨大的表。
? 访问模式:因目的不同,需访问的不同的数据行集,每种目的的访问可以称之为一种访问模式。分区一方面可以将数据分为更小、更易管理的部分,为提高性能起到一定的作用;另一方面,对于如果具有多个CPU的系统,分区可以是对表的操作通过并行的方式进行,这对于提升性能是非常有帮助的。注意:只能在 SQL Server Enterprise Edition 中创建分区函数。只有 SQL Server Enterprise Edition 支持分区。
2. 创建分区表或分区索引的步骤
可以分为以下步骤:
1. 确定分区列和分区数
2. 确定是否使用多个文件组
3. 创建分区函数
4. 创建分区架构(Schema)
5. 创建分区表
6. 创建分区索引下面详细描述的创建分区表、分区索引的步骤。
2.1. 确定分区列和分区数
在开始做分区操作之前,首先要确定待分区表的访问模式,该模式决定了什么列适合做分区键。例如,对于销售数据,一般会先根据日期把数据范围限定在一个范围内,然后在这个基础上做进一步的查询,这样,就可以把日期作为分区列。确定了分区列之后,需要进一步确定分区数,亦即分区表中需要包含多少数据,每个分区的数据应该限定在哪个范围。2.2. 确定是否使用多个文件组
为了有助于优化性能和维护,应该使用文件组分离数据。一般情况下,如果经常对分区的整个数据集操作,则文件组数最好与分区数相同,并且这些文件组通常应该位于不同的磁盘上,再配合多个CPU,则SQL Server 可以并行处理多个分区,从而大大缩短处理大量复杂报表和分析的总体时间。2.3. 创建分区函数
分区函数用于定义分区的边界条件,创建分区函数的语法如下:
CREATE PARTITION FUNCTION partition_function_name ( input_parameter_type )
AS RANGE [ LEFT | RIGHT ]
FOR VALUES ( [ boundary_value [ ,...n ] ] )
[ ; ]参数说明:
? partition_function_name
是分区函数的名称。分区函数名称在数据库内必须唯一,并且符合标识符的规则。? input_parameter_type
是用于分区的列的数据类型。当用作分区列时,除 text、ntext、image、xml、timestamp、varchar(max)、nvarchar(max)、varbinary(max)、别名数据类型或 CLR 用户定义数据类型外,所有数据类型均有效。实际列(也称为分区列)是在 CREATE TABLE 或 CREATE INDEX 语句中指定的。? boundary_value
为使用 partition_function_name 的已分区表或索引的每个分区指定边界值。如果 boundary_value 为空,则分区函数使用 partition_function_name 将整个表或索引映射到单个分区。只能使用 CREATE TABLE 或 CREATE INDEX 语句中指定的一个分区列。boundary_value 是可以引用变量的常量表达式。这包括用户定义类型变量,或函数以及用户定义函数。它不能引用 Transact-SQL 表达式。boundary_value 必须与 input_parameter_type 中提供的数据类型相匹配或者可隐式转换为该数据类型,并且如果该值的大小和小数位数与 input_parameter_type 中相应的值的大小和小数位数不匹配,则在隐式转换过程中该值不能被截断。注意:
如果 boundary_value 包含 datetime 或 smalldatetime 文字值,则为这些文字值在计算时假设 us_english 是会话语言。不推荐使用此行为。要确保分区函数定义对于所有会话语言都具有预期的行为,建议使用对于所有语言设置都以相同方式进行解释的常量,例如 yyyymmdd 格式;或者将文字值显式转换为特定样式。有关详细信息,请参阅编写国际化 Transact-SQL 语句。若要确定服务器的语言会话,请运行 SELECT @@LANGUAGE。? ...n
指定 boundary_value 提供的值的数目,不能超过 999。所创建的分区数等于 n + 1。不必按顺序列出各值。如果值未按顺序列出,则 Microsoft SQL Server 2005 数据库引擎 将对它们进行排序,创建函数并返回一个警告,说明未按顺序提供值。如果 n 包括任何重复的值,则数据库引擎将返回错误。? LEFT | RIGHT
指定当间隔值由 数据库引擎 按升序从左到右排序时,boundary_value [ ,...n ] 属于每个边界值间隔的哪一侧(左侧还是右侧)。如果未指定,则默认值为 LEFT。创建分区函数示例:
CREATE PARTITION FUNCTION PF_Left(int)
AS RANGE LEFT
FOR VALUES(10, 20)
GOCREATE PARTITION FUNCTION PF_Right(int)
AS RANGE LEFT
FOR VALUES(10, 20)
GOPF_Left 和 PF_Right 分区函数的区分:
分区函数 分区1 分区2 分区3
PF_Left <= 10 > 10 and <= 20 > 20
PF_Right < 10 >= 10 and < 20 >= 202.4. 创建分区架构(Schema)
创建分区函数后,必须将其与分区架构(Schema)相关联,以便将分区定向至特定的文件组。定义分区架构师,即使多个 分区位于同一个文件组中,也必须为每个分区指定一个文件组。创建分区架构的语法如下:
GOCREATE PARTITION SCHEME partition_scheme_name
AS PARTITION partition_function_name
[ ALL ] TO ( { file_group_name | [ PRIMARY ] } [ ,...n ] )
[ ; ]参数:
? partition_scheme_name
分区方案的名称。分区方案名称在数据库中必须是唯一的,并且符合标识符规则。? partition_function_name
使用分区方案的分区函数的名称。分区函数所创建的分区将映射到在分区方案中指定的文件组。partition_function_name 必须已经存在于数据库中。? ALL
指定所有分区都映射到在 file_group_name 中提供的文件组,或映射到主文件组(如果指定了 [PRIMARY]。如果指定了 ALL,则只能指定一个 file_group_name。? file_group_name | [ PRIMARY ] [ ,...n]
指定用来持有由 partition_function_name 指定的分区的文件组的名称。file_group_name 必须已经存在于数据库中。如果指定了 [PRIMARY],则分区将存储于主文件组中。如果指定了 ALL,则只能指定一个 file_group_name。分区分配到文件组的顺序是从分区 1 开始,按文件组在 [,...n] 中列出的顺序进行分配。在 [,...n] 中,可以多次指定同一个 file_group_name。如果 n 不足以拥有在 partition_function_name 中指定的分区数,则 CREATE PARTITION SCHEME 将失败,并返回错误。如果 partition_function_name 生成的分区数少于文件组数,则第一个未分配的文件组将标记为 NEXT USED,并且出现显示命名 NEXT USED 文件组的信息。如果指定了 ALL,则单独的 file_group_name 将为该 partition_function_name 保持它的 NEXT USED 属性。如果在 ALTER PARTITION FUNCTION 语句中创建了一个分区,则 NEXT USED 文件组将再接收一个分区。若要再创建一个未分配的文件组来拥有新的分区,请使用 ALTER PARTITION SCHEME。在 file_group_name[ 1,...n] 中指定主文件组时,必须像在 [PRIMARY] 中那样分隔 PRIMARY,因为它是关键字。创建分区架构示例:
CREATE PARTITION FUNCTION myRangePF1 (int)
AS RANGE LEFT FOR VALUES (1, 100, 1000);
GO
CREATE PARTITION SCHEME myRangePS1
AS PARTITION myRangePF1
TO (test1fg, test2fg, test3fg, test4fg);
GO2.5. 创建分区表
定义了分区函数(逻辑结构)和 分区架构(物理结构)后,既可以创建分区表来利用它们。分区表定义应使用的分区架构,而分区架构又定义其使用的分区函数。要将这三者结合起来,必须指定应用于分区函数的 列 。范围分区始终只映射到表中的一列。CREATE TABLE 语法如下:
CREATE TABLE
[ database_name . [ schema_name ] . | schema_name . ] table_name
( { <column_definition> | <computed_column_definition> }
[ <table_constraint> ] [ ,...n ] )
[ ON { partition_scheme_name ( partition_column_name ) | filegroup
| "default" } ]
[ { TEXTIMAGE_ON { filegroup | "default" } ]
[ ; ]示例如下:
CREATE TABLE myRangePT1
(
ID int not null,
AGE int,
PRIMARY KEY (ID)
) ON myRangePS1(myRangePF1)
GO
2.6. 创建分区索引
索引对于提高查询性能非常有效,因此,一般应该考虑应该考虑为分区表建立索引,为分区表建立索引与为普通表建立索引的语法一直,但是,其行为与普通索引有所差异。默认情况下,分区表中创建的索引使用与分区表相同分区架构和分区列,这样,索引将于表对齐。将表与其索引对齐,可以使管理工作更容易进行,对于滑动窗口方案尤其如此。若要启动分区切换,表的所有索引都必须对齐。在创建索引时,也可以指定不同的分区方案(Schema)或单独的文件组(FileGroup)来存储索引,这样SQL Server 不会将索引与表对齐。在已分区的表上创建索引(分区索引)时,应该注意以下事项:
? 唯一索引
建立唯一索引(聚集或者非聚集)时,分区列必须出现在索引列中。此限制将使SQL Server只调查单个分区,并确保表中 宠物的新键值。如果分区依据列不可能包含在唯一键中,则必须使用DML触发器,而不是强制实现唯一性。? 非唯一索引
对非唯一的聚集索引进行分区时,如果未在聚集键中明确指定分区依据列,默认情况下SQL Server 将在聚集索引列中添加分区依据列。
对非唯一的非聚集索引进行分区时,默认情况下SQL Server 将分区依据列添加为索引的包含性列,以确保索引与基表对齐,若果索引中已经存在分区依据列,SQL Server 将不会像索引中添加分区依据列。3. 分区操作
分区适用于可以缩放的大型表,所以随着时间和环境的变化,就会产生对分区的拆分、合并、移动的需求。
3.1. 拆分与合并分区
通过拆分或合并边界值更改分区函数。通过执行 ALTER PARTITION FUNCTION,可以将使用分区函数的任何表或索引的某个分区拆分为两个分区,也可以将两个分区合并为一个分区。
注意:多个表或索引可以使用同一分区函数。ALTER PARTITION FUNCTION 在单个事务中影响所有这些表或索引。
ALTER PARTITION FUNCTION 语法如下:
ALTER PARTITION FUNCTION partition_function_name()
{
SPLIT RANGE ( boundary_value )
| MERGE RANGE ( boundary_value )
} [ ; ]参数说明:
? partition_function_name
要修改的分区函数的名称。? SPLIT RANGE ( boundary_value )
在分区函数中添加一个分区。boundary_value 确定新分区的范围,因此它必须不同于分区函数的现有边界范围。根据 boundary_value,Microsoft SQL Server 2005 数据库引擎 将某个现有范围拆分为两个范围。在这两个范围中,新 boundary_value 所在的范围被视为是新分区。重要提示:
文件组必须处于联机状态,并且必须由使用此分区函数的分区方案标记为 NEXT USED,以保存新分区。在 CREATE PARTITION SCHEME 语句中,将把文件组分配给分区。如果 CREATE PARTITION SCHEME 语句分配了多余的文件组(在 CREATE PARTITION FUNCTION 语句中创建的分区数少于用于保存它们的文件组),则存在未分配的文件组,分区方案将把其中的某个文件组标记为 NEXT USED。该文件组将保存新的分区。如果分区方案未将任何文件组标记为 NEXT USED,则必须使用 ALTER PARTITION SCHEME 添加一个文件组或指定一个现有文件组来保存新分区。可以指定已保存分区的文件组来保存附加分区。由于一个分区函数可以参与多个分区方案,因此所有使用分区函数(您向其中添加了分区)的分区方案都必须拥有一个 NEXT USED 文件组。否则,ALTER PARTITION FUNCTION 将失败并出现错误,该错误显示缺少 NEXT USED 文件组的一个或多个分区方案。? MERGE [ RANGE ( boundary_value) ]
删除一个分区并将该分区中存在的所有值都合并到剩余的某个分区中。RANGE (boundary_value) 必须是一个现有边界值,已删除分区中的值将合并到该值中。如果最初保存 boundary_value 的文件组没有被剩余分区使用,也没有使用 NEXT USED 属性进行标记,则将从分区方案中删除该文件组。合并的分区驻留在最初不保存 boundary_value 的文件组中。boundary_value 是一个可以引用变量(包括用户定义类型变量)或函数(包括用户定义函数)的常量表达式。它无法引用 Transact-SQL 表达式。boundary_value 必须匹配或可以隐式转换为其对应列的数据类型,并且当值的大小和小数位数不匹配其对应 input_parameter_type 时,将无法在隐式转换过程中被截断。示例:
ALTER PARTITION SCHEME PS_HistoryArchive
NEXT USED [PRIMARY]备注:
ALTER PARTITION FUNCTION 在单个原子操作中对使用该函数的任何表和索引进行重新分区。但该操作在脱机状态下进行,并且根据重新分区的范围,可能会消耗大量资源。ALTER PARTITION FUNCTION 只能用于将一个分区拆分为两个分区,或将两个分区合并为一个分区。若要更改其他情况下对表进行分区方法(例如,将 10 个分区合并为 5 个分区),可以尝试使用以下任何选项。根据系统配置,这些选项可能在资源消耗方面有所不同:? 使用所需的分区函数创建一个新的已分区表,然后使用 INSERT INTO...SELECT FROM 语句将旧表中的数据插入新表。? 为堆创建分区聚集索引。
注意:
删除已分区的聚集索引将产生分区堆。? 通过将 Transact-SQL CREATE INDEX 语句与 DROP EXISTING = ON 子句一起使用来删除并重新生成现有的已分区索引。? 执行一系列 ALTER PARTITION FUNCTION 语句。ALTER PARITITION FUNCTION 所影响的全部文件组都必须处于联机状态。
如果使用分区函数的任何表中存在已禁用的聚集索引,ALTER PARTITION FUNCTION 都将失败。
Microsoft SQL Server 2005 不对修改分区函数提供复制支持。必须在订阅数据库中手动应用对发布数据库中的分区函数的更改。3.2. 移动分区数据
可以使用 ALTER TABLE ....... SWITCH 语句按一下方式快速有效地移动数据子集:
? 将某个表中的数据移动到另一个表中;
? 将某个表作为分区添加到现存的已分区表中;
? 将分区从一个已分区表切换到另一个已分区表;
? 删除分区以形成单个表。使用这些方案移动数据时,无论集合有多大,此方案都能快速有效地进行传输,因为操作并不以物理方式移动数据,只有关于存储位置的元数据会从一个分区变为另一个分区。 ALTER TABLE .... SWITCH 的语法如下:
ALTER TABLE [ database_name . [ schema_name ] . | schema_name . ] table_name
{
SWITCH [ PARTITION source_partition_number_expression ]
TO [schema_name].target_table
[ PARTITION target_partition_number_expression ]
}
[ ; ]
分区表支持和标准表相关的所有属性和功能,包括约束、默认值、标识和时间戳值以及触发器等。决定是否实现分区主要取决于表当前的大小或将来的大小以及对表执行查询和维护操作的完善程度。
通常,如果某个大型表同时满足下列两个条件,则可能适于进行分区:
1,该表包含或将包含以多种不同方式使用的大量数据
2,维护开销超过了预定义的维护期
例如,如果对当前年份或当前月份的数据主要执行 SELECT 、INSERT、UPDATE 和 DELETE 操作,而对以前年份或以前月份的数据主要执行 SELECT 查询,则如果按年份或月份对表进行分区,表的管理要容易些,因为此时对表的维护操作只针对一个数据子集。如果该表没有分区,那么就需要对整个数据集执行这些操作,这样就会消耗大量资源。
所以常常根据日期和分类对表进行分区,当然利用某个标识列ID也是很好的选择。例如,电子商务数据库的某张表可能包含了近6年的数据,但是只定期访问本年度或某个月的数据,那么就可以按年份或月份分区,而另外一张表包含了近几十种类型商品的订单,那么此时可以为每种类型商品分一个区。
一般而言,衡量大型表是以数据为标准的,但对于适合分区的大型表,衡量大型表更重要的是对数据访问的性能,如果对于某些表的访问和维护有较严重的性能问题,就可以视为大型表,就应该考虑通过更好的设计和分区来解决性能问题。
创建分区表必须经过如下三个步骤:
1, 创建分区函数
2, 创建映射到分区函数的分区方案
3, 创建使用该分区方案的分区表
分区函数
分区函数是数据库中的一个独立对象,它将表的行映射到一组分区,所以分区函数解决的是HOW的问题,即表如何分区的问题。创建分区函数时,必须指明数据分区的边界点以及分区依据列,这样便知道如何对表或索引进行分区。分区函数的创建语法如下:
CREATE PARTITION FUNCTION partition_function_name ( input_parameter_type )
AS RANGE [ LEFT | RIGHT ]
FOR VALUES ( [ boundary_value [ ,...n ] ] )
[ ; ]
分区函数语法的相关解释:
1, 创建一个分区函数和创建一个普通的数据库对象(例如表)没什么区别。所以根据标准语法走就OK了。
2, partition_function_name是分区函数的名称。分区函数名称在数据库内必须唯一,并且符合标识符的规则。
3, input_parameter_type是用于分区的列的数据类型,习惯把它称为分区依据列。当用作分区列时,除 text、ntext、image、xml、timestamp、varchar(max)、nvarchar(max)、varbinary(max)、别名数据类型或 CLR 用户定义数据类型外,其他所有数据类型均有效。分区依据列是在 CREATE TABLE 或 CREATE INDEX 语句中指定的。
4, boundary_value [ ,...n ]中的boundary_value是边界值(或边界点的值),n代表可以最多有n个边界值,即n指定 boundary_value 提供的值的数目,但n不能超过 999。所创建的分区数等于 n + 1。不必按顺序列出各值。如果值未按顺序列出,则 Database Engine 将对这些边界值进行排序,创建分区函数并返回一个警告,说明未按顺序提供值。如果 n 包括任何重复的值,则数据库引擎将返回错误。边界值的取值一定是和分区依据列相关的,所以只能使用 CREATE TABLE 或 CREATE INDEX 语句中指定的一个分区列。
5, LEFT | RIGHT 指定boundary_value [ ,...n ] 的每个boundary_value属于每个边界值间隔的哪一侧(左侧还是右侧)。如果未指定,则默认值为 LEFT。
例如我们可以依据某个表的int列来创建分区函数:
create partition function MyPF1(int)
range left --默认是left,所以可以省略left
for values(500000,1000000,1500000)
很明显,这个分区函数创建了4个分区,因为此时n=3,所以分区总数是n+1=4。而那个int分区依据列表明将要分区的那个表里面一定有一列是int类型,是分区依据列。这个分区函数我们用的是range left,各个分区的取值范围如下表:
分区 取值范围
1 (负无穷,500000]
2 [500001,1000000]
3 [1000001,1500000]
4 [1500001,正无穷)
如果换成range right,即创建分区函数时代码如下:
create partition function MyPF1(int)
range right
for values(500000,1000000,1500000)
那么各个分区的取值范围如下表:
分区 取值范围
1 (负无穷,499999]
2 [500000,999999]
3 [1000000,1499999]
4 [1500000,正无穷)
我们还可以根据日期列创建分区函数,例如:
create partition function MyPF2(datetime)
range right
for values('2008/01/01', '2009/01/01')
这个分区函数非常适合查询和归档某一年的数据。各个分区的取值范围如下表:
分区 取值范围
1 <=2007/12/31
2 [2008/01/01,2008/12/31]
3 >=2009/01/01 当然我们也可以根据月份分区,而分区依据列支持的数据类型非常多,参照项目的实际情况选择最能表示分区的列类型。分区方案
对表和索引进行分区的第二步是创建分区方案。分区方案定义了一个特定的分区函数将使用的物理存储结构(其实就是文件组),或者说是分区方案将分区函数生成的分区映射到我们定义的一组文件组。所以分区方案解决的是Where的问题,即表的各个分区在哪里存储的问题。分区方案的创建语法如下:
CREATE PARTITION SCHEME partition_scheme_name
AS PARTITION partition_function_name
[ ALL ] TO ( { file_group_name | [ PRIMARY ] } [ ,...n ] )
[ ; ]
分区方案语法的相关解释:
1, 创建分区方案时,根据分区函数的参数,定义映射表分区的文件组。必须指定足够的文件组来容纳分区数。可以指定所有分区映射到不同文件组、某些分区映射到单个文件组或所有分区映射到单个文件组。如果您希望在以后添加更多分区,还可以指定其他“未分配的”文件组。在这种情况下,SQL Server 用 NEXT USED 属性标记其中一个文件组。这意味着该文件组将包含下一个添加的分区。一个分区方案仅可以使用一个分区函数。但是,一个分区函数可以参与多个分区方案。
2, partition_scheme_name 是分区方案的名称。分区方案名称在数据库中必须是唯一的,并且符合标识符规则。
3, partition_function_name 是使用当前分区方案的分区函数的名称。分区函数所创建的分区将映射到在分区方案中指定的文件组。partition_function_name 必须已经存在于数据库中。
4, ALL 指定所有分区都映射到在 file_group_name 中提供的同一个文件组,或映射到主文件组(如果指定了 [PRIMARY])。如果指定了 ALL,则只能指定一个 file_group_name。
5, file_group_name | [ PRIMARY ] [ ,...n] 代表n个文件组。和分区函数中的各个分区对应。文件组必须已经存在于数据库中。 如果指定了 [PRIMARY],则分区将存储于主文件组中。如果指定了 ALL,则只能指定一个 file_group_name。分区分配到文件组的顺序是从分区 1 开始,按文件组在 [,...n] 中列出的顺序进行分配。在 [,...n] 中,可以多次指定同一个文件组。如果 n 不足以拥有在分区函数中指定的分区数,则 CREATE PARTITION SCHEME 将失败,并返回错误。
6, 如果分区函数生成的分区数少于创建分区方案时提供的文件组数,则分区方案中第一个未分配的文件组将被标记为 NEXT USED,并且出现显示命名 NEXT USED 文件组的信息。如果指定了 ALL,则单独的文件组将为该分区函数保持它的NEXT USED 属性。如果在 ALTER PARTITION FUNCTION 语句中创建了一个分区,则 NEXT USED 文件组将再接收一个分区。若要再创建一个未分配的文件组来拥有新的分区,请使用 ALTER PARTITION SCHEME。
分区方案例子1:下面的代码先创建一个分区函数,然后再创建这个分区函数使用的分区方案,这个分区方案将每个分区映射到不同文件组。代码如下:
create partition function MyPF1(int)
as range left
for values(500000,1000000,1500000)
go
create partition scheme MyPS1
as partition MyPF1
to (fg1, fg2, fg3, fg4)
文件组、分区和分区边界值范围之间的关系如下表:
文件组 分区 取值范围
fg1 1 (负无穷,500000]
fg2 2 [500001,1000000]
fg3 3 [1000001,1500000]
fg4 4 [1500001,正无穷)
分区方案例子2:下面的代码先创建一个分区函数,然后再创建这个分区函数使用的分区方案,这个分区方案将多个分区映射到同一个文件组。代码如下:
create partition function MyPF2(int)
as range left
for values(500000,1000000,1500000)
go
create partition scheme MyPS2
as partition MyPF2
to (fg1, fg1, fg1, fg2)
文件组、分区和分区边界值范围之间的关系如下表:
文件组 分区 取值范围
fg1 1 (负无穷,500000]
Fg1 2 [500001,1000000]
Fg1 3 [1000001,1500000]
Fg2 4 [1500001,正无穷)
分区方案例子3:下面的代码先创建一个分区函数,然后再创建这个分区函数使用的分区方案,这个分区方案将所有分区映射到同一个文件组。代码如下:
create partition function MyPF3 (int)
as range left
for values(500000,1000000,1500000)
go
create partition scheme MyPS3
as partition MyPF3
all to (fg1)
文件组、分区和分区边界值范围之间的关系如下表:
文件组 分区 取值范围
fg1 1 (负无穷,500000]
Fg1 2 [500001,1000000]
Fg1 3 [1000001,1500000]
Fg1 4 [1500001,正无穷)
分区方案例子4:下面的代码先创建一个分区函数,然后再创建这个分区函数使用的分区方案,这个分区方案指定了“NEXT USED”文件组。代码如下:
create partition function MyPF4(int)
as range left
for values(500000,1000000,1500000) --4个分区
go
create partition scheme MyPS4
as partition MyPF4
to (fg1, fg2, fg3, fg4, fg5) --5个文件组
那么文件组fg5将自动被标记为“NEXT USED”文件组。
分区方案例子5:下面的代码先创建一个分区函数,然后再创建这个分区函数使用的分区方案,这个分区方案指定了“[primary]”文件组。代码如下:
create partition function MyPF5(datetime)
range right
for values('2008/01/01', '2009/01/01')
go
create partition scheme MyPS5
as partition MyPF5
to([primary], fg1, fg2)
最后必须明白一点,一张表最多只能有1000个分区。
分区表
在分区函数和分区方案创建完成后,创建分区表的准备工作已经完成。我们看一个完整的例子,代码如下:
--创建分区函数
create partition function MyPF(datetime)
range right
for values('2007-1-1', '2008-1-1')
go
--创建分区方案
create partition scheme MyPS
as partition MyPF
to(fg1, fg2, fg3)
go
--创建分区表
create table orders
(
OrderID int identity(1,1) primary key,
OrderDate datetime,
CustID varchar(10)
)
on MyPS(OrderDate)