SQL Server 2005 中的分区表和索引

SQL Server 2005 中的分区表和索引

 

为什么要进行分区?
  
  什么是分区?为什么要使用分区?简单的回答是:为了改善大型表以及具有各种访问模式的表的可伸缩性和可管理性。通常,创建表是为了存储某种实体(例如客户或销售)的信息,并且每个表只具有描述该实体的属性。一个表对应一个实体是最容易设计和理解的,因此不需要优化这种表的性能、可伸缩性和可管理性,尤其是在表变大的情况下。
  
  大型表是由什么构成的呢?超大型数据库 (VLDB) 的大小以数百 GB 计算,甚至以 TB 计算,但这个术语不一定能够反映数据库中各个表的大小,但对分区来说,了解表的大小更重要。
  
  除了大小之外,当表中的不同行集拥有不同的使用模式时,具有不同访问模式的表也可能会影响性能和可用性。尽管使用模式并不总是在变化(这也不是进行分区的必要条件),但在使用模式发生变化时,通过分区可以进一步改善管理、性能和可用性。还以销售表为例,当前月份的数据可能是可读写的,但以往月份的数据(通常占表数据的大部分)是只读的。在数据使用发生变化的类似情况下,或在维护成本随着在表中读写数据的次数增加而变得异常庞大的情况下,表响应用户请求的能力可能会受到影响。相应地,这也限制了服务器的可用性和可伸缩性。
  
  此外,如果以不同的方式使用大量数据集,则需要经常对静态数据执行维护操作。这可能会造成代价高昂的影响,例如性能问题、阻塞问题、备份(空间、时间和运营成本),还可能会对服务器的整体可伸缩性产生负面影响。
  
  分区可以带来什么帮助?当表和索引变得非常大时,分区可以将数据分为更小、更容易管理的部分,从而提供一定的帮助。本文重点介绍横向分区,在横向分区中,大量的行组存储在多个相互独立的分区中。分区集的定义根据需要进行自定义、定义和管理。Microsoft SQL Server 2005 允许您根据特定的数据使用模式,使用定义的范围或列表对表进行分区。SQL Server 2005 还围绕新的表和索引结构设计了几种新功能,为分区表和索引的长期管理提供了大量的选项。
  
  此外,如果具有多个 CPU 的系统中存在一个大型表,则对该表进行分区可以通过并行操作获得更好的性能。通过对各个并行子集执行多项操作,可以改善在极大型数据集(例如数百万行)中执行大规模操作的性能。通过分区改善性能的例子可以从以前版本中的聚集看出。例如,除了聚集成一个大型表外,SQL Server 还可以分别处理各个分区,然后将各个分区的聚集结果再聚集起来。在 SQL Server 2005 中,连接大型数据集的查询可以通过分区直接受益;SQL Server 2000 支持对子集进行并行连接操作,但需要动态创建子集。在 SQL Server 2005 中,已分区为相同分区键和相同分区函数的相关表(如 Order 和 OrderDetails 表)被称为已对齐。当优化程序检测到两个已分区且已对齐的表连接在一起时,SQL Server 2005 可以先将同一分区中的数据连接起来,然后再将结果合并起来。这使 SQL Server 2005 可以更有效地使用具有多个 CPU 的计算机。
  
  分区的发展历史
  
  分区的概念对 SQL Server 来说并不陌生。实际上,此产品的每个版本中都可以实现不同形式的分区。但是,由于没有为了帮助用户创建和维护分区架构而专门设计一些功能,因此分区一直是一个很繁琐的过程,没有得到充分的利用。而且,用户和开发人员对此架构存在误解(由于其数据库设计比较复杂),低估了它的优点。但是,由于概念中固有的重要性能改善,SQL Server 7.0 开始通过分区视图实现各种分区方式,以此来改进这种功能。现在,SQL Server 2005 为通过分区表对大型数据集进行分区又迈出了最大的一步。
  
  对 SQL Server 7.0 之前的版本中的对象进行分区
  
  在 SQL Server 6.5 及以前的版本中,分区只能通过设计来完成,还必须内置到所有数据访问编码和查询方法中。通过创建多个表,然后通过存储过程、视图或客户端应用程序管理对正确表的访问,通常可以改善某些操作的性能,但代价是增加了设计的复杂性。每个用户和开发人员都必须知道(并正确引用)正确的表。单独创建和管理每个分区,而使用视图来简化访问;但是这种解决方案对性能并没有太大的改善。使用联合视图简化用户和应用程序访问时,查询处理器必须访问每个基础表才能确定结果集所需的数据。如果只需要基础表的有限子集,则每个用户和开发人员都必须了解此设计,以便只引用相应的表。
  
  SQL Server 7.0 中的分区视图
  
  在 SQL Server 7.0 之前的版本中,手动创建分区所面临的挑战主要与性能有关。尽管视图可以简化应用程序设计、用户访问和查询的编写,但却无法改善性能。而在 SQL Server 7.0 版本中,视图结合了约束,允许查询优化程序从查询计划中删除不相关的表(即分区消除),大大降低了联合视图访问多个表时的总计划成本。
  
  请参见图 1 中的 YearlySales 视图。您可以定义十二个单独的表(如 SalesJanuary2003、SalesFebruary2003 等),然后定义每个季度的视图以及全年的视图 YearlySales,而不是将所有销售数据放到一个大型表中。
  

 SQL Server 2005 中的分区表和索引(1)


  

图 1:SQL Server 7.0/2000 中的分区视图


  
  使用以下查询访问 YearlySales 视图的用户只会被引导至 SalesJanuary2003 表。
  
  SELECT ys.*
  FROM dbo.YearlySales AS ys
  WHERE ys.SalesDate = '20030113'
  
  只要约束可信并且访问视图的查询使用 WHERE 子句根据分区键(定义?际牧校┫拗撇檠峁琒QL Server 就会只访问必需的基础表。受信任的约束是指 SQL Server 能够确保所有数据符合该约束所定义的属性的约束。创建约束时,默认行为是创建约束 WITH CHECK。此设置将导致对表执行架构锁定,以便根据约束验证数据。如果验证结果表明现有数据有效,则添加约束;一旦解除架构锁定,后续的插入、更新和删除操作都必须符合正在应用的约束。通过使用此过程创建受信任的约束,开发人员无需直接访问(甚至不需要知道)他们感兴趣的表,从而大大降低了使用视图的设计的复杂性。通过受信任的约束,SQL Server 可以从执行计划中删除不需要的表,从而改善性能。
  
  注意:约束可以通过各种方式变得“不可信任”;例如,如果未指定 CHECK_CONSTRAINTS 参数即执行批量插入,或者使用 NOCHECK 创建约束。如果约束不可信任,查询处理器将转而扫描所有基础表,因为它无法确定所请求的数据是否真的位于正确的基础表中。
  
  SQL Server 2000 中的分区视图
  
  尽管 SQL Server 7.0 大大简化了设计并改善了 SELECT 语句的性能,但是并没有为数据修改语句带来任何好处。INSERT、UPDATE 和 DELETE 语句只能针对基础表,而不能直接针对用于联合表的视图。在 SQL Server 2000 中,数据修改语句还可以受益于 SQL Server 7.0 中引入的分区视图功能。由于数据修改语句可以使用相同的分区视图结构,因此,SQL Server 可以通过视图将修改定向至相应的基础表。为了正确配置此设置,需要对分区键及其创建设置额外的限制;但是,基本原理是相同的,因为 SELECT 查询与修改都会直接发送给相应的基础表。有关在 SQL Server 2000 中进行分区的限制、设置、配置和最佳方法的详细信息,请参见 Using Partitions in a Microsoft SQL Server 2000 Data Warehouse。
  
  SQL Server 2005 中的分区表
  
  尽管 SQL Server 7.0 和 SQL Server 2000 中的改进大大改善了使用分区视图时的性能,但是并没有简化分区数据集的管理、设计或开发。使用分区视图时,必须单独创建和管理每个基础表(在其中定义视图的表)。尽管简化了应用程序设计并为用户带来了好处(用户不再需要知道直接访问哪个基础表),但是由于要管理的表太多,而且必须为每个表管理数据完整性约束,管理工作变得更复杂。因为管理方面的问题,通常只有在需要存档或加载数据时才使用分区视图来分离表。当数据被移动到只读表或从只读表中删除后,操作的代价变得十分高昂,不仅花费时间、占据日志空间,通常还会导致系统阻塞。
  
  另外,由于以前版本中的分区策略需要开发人员创建各个表和索引,然后通过视图将它们联合起来,因此优化程序需要验证并确定每个分区的计划(因为索引可能已发生变化)。这样一来,SQL Server 2000 中的查询优化时间通常会随着处理的分区数增加而直线上升。
  
  在 SQL Server 2005 中,从定义上讲,每个分区都拥有相同的索引。例如,请考虑这样一种方案,即当前月份的联机事务处理 (OLTP) 数据需要移动到每个月末的分析表中。分析表(用于只读查询)是具有一个群集索引和两个非群集索引的表;批量加载 1 GB 数据(加载到已建立索引并激活的一个表中)将使当前用户遭受系统阻塞的情况,因为表和/或索引变得支离破碎和/或被锁定。另外,因为每传入一行都需要维护表和索引,所以加载过程还将耗费大量的时间。虽然可以通过多种方法加快批量加载的速度,但这些方法可能会直接影响所有其他用户,因为追求速度而无法实现并发操作。
  
  如果将这些数据单独放到一个新创建的(空)且未建立索引(堆)的表中,则可以先加载数据,而在加载数据之后建立索引。

     要删除最旧的分区(2003 年 9 月),请将新的合并选项(如图 7 所示)与 ALTER TABLE 结合使用。有效地合并边界点将删除边界点,从而删除分区。这将加载数据的分区数减少到 n-1(本例中为 12)。如果不需要移动行,合并分区应该是一个非常快的操作(因为要合并的边界点没有数据行)。在本例中,因为第一个分区为空,不需要从第一个分区向第二个分区中移动任何行。如果在第一个分区非空的情况下合并边界点,必须将第一个分区的行移动到第二个分区中,这可能是一个代价非常高昂的操作。但是,在最常见的滑动窗口方案中(空分区与活动分区合并,并且不移动任何行),不需要执行此操作。
  

 SQL Server 2005 中的分区表和索引(2)(图一)


  

图 7:合并分区


  
  最后,必须将新表移入分区表。要将此操作作为元数据更改来执行,必须在新表中(分区表的边界之外)加载和建立索引。要移入分区,请先将最后一个范围和最近一个空范围拆分为两个分区。另外,还需要更新表的约束以允许新的范围。分区表将再次拥有 13 个分区。在滑动窗口方案中,使用 LEFT 分区函数的最后一个分区将始终为空。
  

 SQL Server 2005 中的分区表和索引(2)(图二)


  

图 8:拆分分区


  
  最后,新加载的数据已准备就绪,可以移入第十二个分区,即 2004 年 9 月。
  

 SQL Server 2005 中的分区表和索引(2)(图三)


  

图 9:移入分区


  
  表的结果是:
  

 SQL Server 2005 中的分区表和索引(2)(图四)


  

图 10:加载/存档数据后的范围分区边界


  
  因为一次只能添加或删除一个分区,所以应重新创建需要添加或删除多个分区的表。要更改为这种新的分区结构,请先创建新的分区表,然后将数据加载到新创建的表中。与每次拆分后重新平衡整个表相比,这种方法更好。此过程是使用新的分区函数和新的分区架构,然后将数据移动到新分区的表中来完成的。要移动数据,请先使用 INSERT newtable SELECT columnlist FROM oldtable 复制数据,然后删除原始表。用户不应在此过程中修改数据,以防数据丢失。
  
  有关详细信息,请参见 SQL Server Books Online 中的“ALTER PARTITION FUNCTION”和“ALTER TABLE”。
  
  创建分区表的步骤
  
  现在,您对分区表的价值有了一定的了解,下一节将详细介绍实现分区表的过程以及有助于完成此过程的功能。逻辑流程如下:
  

 SQL Server 2005 中的分区表和索引(2)(图五)


  

图 11:创建分区表或索引的步骤


  
  确定是否应为对象分区
  
  虽然分区可以带来众多的好处,但也增加了实现对象的管理费用和复杂性,这可能是得不偿失的。尤其是,您可能不需要为较小的表或目前满足性能和维护要求的表分区。前面提到的销售方案使用分区减轻了移动行和数据的负担,但在决定是否实现分区时,您应考虑您的方案是否存在这种负担。
  
  确定分区键和分区数
  
  如果您正在尝试改善大型数据子集的性能和可管理性,并且已经定义了访问模式,则可以使用范围分区减少数据争用的情况,同时减少只读数据不需要分区时的维护工作。要确定分区数,应先评估您的数据中是否存在逻辑分组和模式。如果您通常一次只处理这些已定义子集中的少数几个,则应定义范围以隔离查询,使其只处理相应的数据(即,只处理特定的分区)。
  
  有关详细信息,请参见SQL Server Books Online中的“Designing Partitioned Tables and Indexes”。
  
  确定是否应使用多个文件组
  
  为了有助于优化性能和维护,应使用文件组分离数据。文件组的数目一定程度上由硬件资源决定:一般情况下,文件组数最好与分区数相同,并且这些文件组通常位于不同的磁盘上。但是,这主要适用于打算对整个数据集进行分析的系统。如果您有多个 CPU,SQL Server 则可以并行处理多个分区,从而大大缩短处理大量复杂报表和分析的总体时间。这种情况下,可以获得并行处理以及在分区表中移入和移出分区的好处。
  
  创建文件组
  
  如果需要为多个文件放置一个分区表以获得更好的 I/O 平衡,则至少需要创建一个文件组。文件组可以由一个或多个文件构成,而每个分区必须映射到一个文件组。一个文件组可以由多个分区使用,但是为了更好地管理数据(例如,为了获得更精确的备份控制),应该对分区表进行设计,以便只有相关数据或逻辑分组的数据位于同一个文件组中。使用 ALTER DATABASE,可以添加逻辑文件组名,然后添加文件。要为 AdventureWorks 数据库创建名为 2003Q3 的文件组,请按以下方式使用 ALTER DATABASE:
  
  ALTER DATABASE AdventureWorks ADD FILEGROUP [2003Q3]
  
  创建文件组后,使用 ALTER DATABASE 将文件添加到该文件组中。
  
  ALTER DATABASE AdventureWorks
  ADD FILE
  (NAME = N'2003Q3',
  FILENAME = N'C:\AdventureWorks\2003Q3.ndf',
  SIZE = 5MB,
  MAXSIZE = 100MB,
  FILEGROWTH = 5MB)
  TO FILEGROUP [2003Q3]
  
  通过在 CREATE TABLE 的 ON 子句中指定一个文件组,可以为文件创建一个表。但是,如果表未分区,则不能为多个文件组创建一个表。要为一个文件组创建表,请使用 CREATE TABLE 的 ON 子句。要创建分区表,必须先确定分区的功能机制。进行分区的标准以分区函数的形式从逻辑上与表相分离。此分区函数作为独立于表的定义存在,而这种物理分离将起到帮助作用,因为多个对象都可以使用该分区函数。因此,为表分区的第一步是创建分区函数。
  
  为范围分区创建分区函数
  
  范围分区必须使用边界条件进行定义。而且,即使通过 CHECK 约束对表进行了限制,也不能消除该范围任一边界的值。为了允许定期将数据移入该表,需要创建最后一个空分区。
  
  在范围分区中,首先定义边界点:如果存在五个分区,则定义四个?呓绲阒担⒅付扛鲋凳堑谝桓龇智纳媳呓? (LEFT) 还是第二个分区的下边界 (RIGHT)。根据 LEFT 或 RIGHT 指定,始终有一个空分区,因为该分区没有明确定义的边界点。
  
  具体来讲,如果分区函数的第一个值(或边界条件)是 '20001001',则边界分区中的值将是:
  
  对于 LEFT
  
  第一个分区是所有小于或等于 '20001001' 的数据
  
  第二个分区是所有大于 '20001001' 的数据
  
  对于 RIGHT
  
  第一个分区是所有小于 '20001001' 的数据

     

     第二个分区是所有大于或等于 '20001001' 数据
  
  由于范围分区可能在 datetime 数据中进行定义,因此必须了解其含义。使用 datetime 具有某种含义:即总是同时指定日期和时间。未定义时间值的日期表示时间部分为“0”的 12:00 A.M。如果将 LEFT 与此类数据结合使用,则日期为 10 月 1 日 12:00 A.M. 的数据将位于第一个分区,而 10 月份的其他数据将位于第二个分区。从逻辑上讲,最好将开始值与 RIGHT 结合使用,而将结束值与 LEFT 结合使用。下面的三个子句将创建逻辑上相同的分区结构:
  
  RANGE LEFT FOR VALUES ('20000930 23:59:59.997',
        '20001231 23:59:59.997',
        '20010331 23:59:59.997',
        '20010630 23:59:59.997')
  
  或
  
  RANGE RIGHT FOR VALUES ('20001001 00:00:00.000', '20010101 00:00:00.000', '20010401 00:00:00.000', '20010701 00:00:00.000')
  
  或
  
  RANGE RIGHT FOR VALUES ('20001001', '20010101', '20010401', '20010701')
  
  注意:此处使用 datetime 数据类型确实增加了一定的复杂性,但您需要确保设置正确的边界情况。请注意使用 RIGHT 的简单性,因为默认时间为 12:00:00.000 A.M。对于 LEFT,复杂性增加是因为 datetime 数据类型具有精度。必须选择 23:59:59.997 的原因在于,datetime 数据无法保证毫秒级别的精度。相反,datetime 数据的精度在 3.33 毫秒内。使用 23:59:59.999 这个确切的时间值是不行的,因为该值将被舍入到最接近的时间值,即第二天的 12:00:00.000 A.M。由于进行了这种舍入,将无法正确定义边界。对于 datetime 数据,必须对明确提供的毫秒值加倍小心。
  
  注意:分区函数还允许将函数作为?智ㄒ宓囊徊糠帧D梢允褂? DATEADD(ms,-3,'20010101'),而不是使用 '20001231 23:59:59.997' 明确定义时间。
  
  有关详细信息,请参见SQL Server Books Online的“Transact-SQL Reference”中的“Date and Time”部分。
  
  要在四个活动分区(每个分区代表一个日历季度)中存储四分之一的 Orders 数据,并创建第五个分区以备将来使用(还是作为占位符,用于在分区表中移入和移出数据),请将 LEFT 分区函数与以下四个边界条件结合使用:
  
  CREATE PARTITION FUNCTION OrderDateRangePFN(datetime)
  AS
  RANGE LEFT FOR VALUES ('20000930 23:59:59.997',
      '20001231 23:59:59.997',
      '20010331 23:59:59.997',
      '20010630 23:59:59.997')
  
  记住,定义四个边界点将创建五个分区。通过查看以下数据集检查此分区创建的数据集:
  
  边界点 '20000930 23:59:59.997' 作为 LEFT(设置模式):
  
  最左侧的分区将包含所有小于或等于 '20012 个逻辑驱动器都位于 RAID 1+0 配置中,因此 Orders 和 OrderDetails 数据所需的总磁盘数为 48 个。存储区域网络支持 78 个磁盘,而另外 30 个用于事务日志、TempDB、系统数据库和其他更小的表,例如 Customers(900 万)和 Products(386,750 行)。Orders 和 OrderDetails 表都使用相同的边界条件、磁盘位置和分区架构。结果是(只看图 13 中的两个逻辑驱动器 [驱动器 E:\ 和 F:\]),相同月份的 Orders 和 OrderDetails 的数据都存储在相同的磁盘上:
  

 SQL Server 2005 中的分区表和索引(3)


  

图 13:磁盘阵列上盘区位置的范围分区


  
  虽然看起来很复杂,但创建过程非常简单。设计分区表最难的部分在于从大量数据源传输数据,即 283 个存储位置都必须使用一种标准的传输机制。但是,中央服务器上只定义了一个 Orders 表和一个 OrderDetails 表。要将两个表都创建为分区表,请先创建分区函数和分区架构。分区架构定义分区在磁盘上的物理位置,因此必须存在文件组。在此表中,文件组是必需的,因此下一步是创建文件组。每个文件组的语法都与下面的语法相同,但必须创建所有 24 个文件组。有关创建所有 24 个文件组的完整脚本,请参见 RangeCaseStudyFilegroups.sql 脚本。
  
  注意:如果没有指定相应的驱动器号,将无法运行此脚本;但是此脚本包含一个“setup”表,可以修改此表以简化测试。您可以将驱动器号/位置更改为一个驱动器,以测试和学习语法。同时,确保将文件大小调整为 MB 而不是 GB,并根据可用的磁盘空间考虑指定一个较小的初始大小。
  
  将为 SalesDB 数据库创建 24 个文件和文件组。每个文件和文件组都具有相同的语法,只是位置、文件名和文件组名不相同:
  
  ALTER DATABASE SalesDB
  ADD FILE
  (NAME = N'SalesDBFG1File1',
  FILENAME = N'E:\SalesDB\SalesDBFG1File1.ndf',
  SIZE = 20GB,
  MAXSIZE = 35GB,
  FILEGROWTH = 5GB)
  TO FILEGROUP [FG1]
  GO
  
  创建所有 24 个文件和文件组后,即可定义分区函数和分区架构。要验证文件和文件组,请分别使用 sp_helpfile 和 sp_helpfilegroup。
  
  分区函数将在 OrderDate 列中进行定义。使用的数据类型为 datetime,而且两个表都需要存储 OrderDate 才能根据此值对两个表进行分区。实际上,如果根据相同的键值对两个表进行分区,则分区键值属于重复信息,但它对于获得对齐优点又是必需的。而且,在大多数情况下,应该是一个相当窄的列(datetime 数据类型为 8 个字节)。如本文前面的“为范围分区创建分区函数”部分所述,此函数将是一个范围分区函数,其中的第一个边界条件位于 LEFT(第一个)分区中。
  
  CREATE PARTITION FUNCTION TwoYearDateRangePFN(datetime)
  AS
  RANGE LEFT FOR VALUES ('20021031 23:59:59.997',   -- 2002 年 10 月
  '20021130 23:59:59.997',  -- 2002 年 11 月
  '20021231 23:59:59.997',  -- 2002 年 12 月
  '20030131 23:59:59.997',  -- 2003 年 1 月
  '20030228 23:59:59.997',  -- 2003 年 2 月
  '20030331 23:59:59.997',  -- 2003 年 3 月
  '20030430 23:59:59.997',  -- 2003 年 4 月
  '20030531 23:59:59.997',  -- 2003 年 5 月
  '20030630 23:59:59.997',  -- 2003 年 6 月
  '20030731 23:59:59.997',  -- 2003 年 7 月
  '20030831 23:59:59.997',  -- 2003 年 8 月
  '20030930 23:59:59.997',  -- 2003 年 9 月
  '20031031 23:59:59.997',  -- 2003 年 10 月
  '20031130 23:59:59.997',  -- 2003 年 11 月
  '20031231 23:59:59.997',  -- 2003 年 12 月
  '20040131 23:59:59.997',  -- 2004 年 1 月
  '20040229 23:59:59.997',  -- 2004 年 2 月
  '20040331 23:59:59.997',  -- 2004 年 3 月
  '20040430 23:59:59.997',  -- 2004 年 4 月
  '20040531 23:59:59.997',  -- 2004 年 5 月
  '20040630 23:59:59.997',  -- 2004 年 6 月
  '20040731 23:59:59.997',  -- 2004 年 7 月
  '20040831 23:59:59.997',  -- 2004 年 8 月
  '20040930 23:59:59.997')  -- 2004 年 9 月
  GO
  
  因为包含了最左侧和最右侧的边界情况,所以此分区函数将创建 25 个分区。该表将保留第 25 个分区为空白。不需要为这个空分区指定特殊的文件组(因为其中永远不会包含数据)作为限制表数据的约束。要将数据定向至相应的磁盘,可以使用分区架构将分区映射到文件组。分区架构将为 24 个将要包含数据的文件组使用明确的文件组名,而为第 25 个空分区使用 PRIMARY 文件组。
  
  CREATE PARTITION SCHEME [TwoYearDateRangePScheme]
  AS
  PARTITION TwoYearDateRangePFN TO
  ( [FG1], [FG2], [FG3], [FG4], [FG5], [FG6],
  [FG7], [FG8], [FG9], [FG10],[FG11],[FG12],
  [FG13],[FG14],[FG15],[FG16],[FG17],[FG18],
  [FG19],[FG20],[FG21],[FG22],[FG23],[FG24],
  [PRIMARY] )
  GO
  
  通过使用默认的文件组或用户定义的文件组作为未分区的表,或者使用架构创建分区表,可以使用与以前的版本支持的相同语法创建表。哪种方法更好取决于表的填充方式和创建的分区数。从性能角度看,先填充堆再建立群集索引可能要胜过在已经建立索引的表中加载数据。另外,如果有多个 CPU,您可以通过并行 BULK INSERT 语句将数据加载到表中,然后也以并行方式建立索引。对于 Orders 表,按照正常的方式创建表,然后通过 INSERT SELECT 语句(从 AdventureWorks 示例数据库中提取数据)加载现有的数据。要将 Orders 表建为分区表,请在该表的 ON 子句中指定分区架构。Orders 表是使用以下语法创建的:
  
  CREATE TABLE SalesDB.[dbo].[Orders]
  (
  [PurchaseOrderID] [int] NOT NULL,
  [EmployeeID] [int] NULL,
  [VendorID] [int] NULL,
  [TaxAmt] [money] NULL,
  [Freight] [money] NULL,
  [SubTotal] [money] NULL,
  [Status] [tinyint]  NOT NULL,
  [RevisionNumber] [tinyint] NULL,
  [ModifiedDate] [datetime] NULL,
  [ShipMethodID]  tinyint NULL,
  [ShipDate] [datetime] NOT NULL,
  [OrderDate] [datetime] NULL
  CONSTRAINT OrdersRangeYear
  CHECK ([OrderDate] >= '20021001'
  AND [OrderDate] < '20041001'),
  [TotalDue] [money] NULL
  ) ON TwoYearDateRangePScheme(OrderDate)
  GO
  
  因为 OrderDetails 表也将使用此架构,而且必须包含 OrderDate,所以使用以下语法创建 OrderDetails 表:
  
  CREATE TABLE [dbo].[OrderDetails](
  [OrderID] [int] NOT NULL,
  [LineNumber] [smallint] NOT NULL,
  [ProductID] [int] NULL,
  [UnitPrice] [money] NULL,
  [OrderQty] [smallint] NULL,
  [ReceivedQty] [float] NULL,
  [RejectedQty] [float] NULL,
  [OrderDate] [datetime] NOT NULL
  CONSTRAINT OrderDetailsRangeYearCK
  CHECK ([OrderDate] >= '20021001'
  AND [OrderDate] < '20041001'),
  [DueDate] [datetime] NULL,
  [ModifiedDate] [datetime] NOT NULL
  CONSTRAINT [OrderDetailsModifiedDateDFLT]
  DEFAULT (getdate()),
  [LineTotal] AS (([UnitPrice]*[OrderQty])),
  [StockedQty] AS (([ReceivedQty]-[RejectedQty]))
  ) ON TwoYearDateRangePScheme(OrderDate)
  GO
  
  加载数据的下一步是通过两个 INSERT 语句处理的。这两个语句使用新的 AdventureWorks 数据库(从中复制数据)。请安装 AdventureWorks 示例数据库以复制此数据:
  
  INSERT dbo.[Orders]
  SELECT o.[PurchaseOrderID]
  , o.[EmployeeID]
  , o.[VendorID]
  , o.[TaxAmt]
  , o.[Freight]
  , o.[SubTotal]
  , o.[Status]
  , o.[RevisionNumber]
  , o.[ModifiedDate]
  , o.[ShipMethodID]
  , o.[ShipDate]
  , o.[OrderDate]
  , o.[TotalDue]
  FROM AdventureWorks.Purchasing.PurchaseOrderHeader AS o
  WHERE ([OrderDate] >= '20021001'
  AND [OrderDate] < '20041001')
  GO
  INSERT dbo.[OrderDetails]
  SELECT  od.PurchaseOrderID
  , od.LineNumber
  , od.ProductID
  , od.UnitPrice
  , od.OrderQty
  , od.ReceivedQty
  , od.RejectedQty
  , o.OrderDate
  , od.DueDate
  , od.ModifiedDate
  FROM AdventureWorks.Purchasing.PurchaseOrderDetail AS od
  JOIN AdventureWorks.Purchasing.PurchaseOrderHeader AS o
  ON o.PurchaseOrderID = od.PurchaseOrderID
  WHERE (o.[OrderDate] >= '20021001'
  AND o.[OrderDate] < '20041001')
  GO
  
  现在,数据已加载到分区表中,您可以使用新的内置系统函数来确定数据所在的分区。下面的查询很有用,因为它将返回包含数据的每个分区的以下信息:每个分区内存在的行数以及最小和最大 OrderDate。此查询不会返回不包含行的分区。
  
  SELECT $partition.TwoYearDateRangePFN(o.OrderDate)
  AS [Partition Number]
  , min(o.Orde

 


 

posted @ 2008-07-24 17:42  吴碧宇  阅读(391)  评论(0编辑  收藏  举报