代码改变世界

BIWORK 分区表阅读与实践笔记

2012-12-07 18:00  BIWORK  阅读(778)  评论(2编辑  收藏

/***************************************************************

BIWORK 分区表阅读与实践笔记

 

Note: 示例中使用到了SQL Server 2000的 Demo Database, 可以从此链接中下载 

http://www.microsoft.com/en-us/download/confirmation.aspx?id=23654

  

在检查删除Partition Function 以及Partition Scheme ,要注意

Partition Scheme 引用了Partition Function, 所有需要先删除

Partition Scheme. 同理,引用了Partition Scheme 的表应该先删除掉.

引用关系: TABLE -> PARTITION SCHEME -> PARTITION FUNCTION

***************************************************************/

IF OBJECT_ID('dbo.Orders')IS NOT NULL

DROP TABLE Orders

GO

 

IF EXISTS (SELECT * FROM sys.partition_schemes WHERE name = 'PS_Orders')

DROP PARTITION SCHEME PS_Orders

GO

 

IF EXISTS (SELECT * FROM sys.partition_functions WHERE name = 'PF_Orders_OrderDateRange')

DROP PARTITION FUNCTION PF_Orders_OrderDateRange

GO

 

/***************************************************************

1. 如果在不需要对数据库进行物理分组的情况下,比如分区表还是享用同

一个文件组,那么应该可以从创建分区函数开始

*** 创建分区函数

*** 确定分区键列的类型(DATETIME)以及分区的边界值:

   (''1997-01-01','1998-01-01','1999-01-01'')

*** N个边界值确定N+1 个分区

*** RIGHT - 第一个分区的所有值都小于VAL < 1997-01-1

            第二个分区的值范围是1997-01-01 <= VAL < 1998-01-01

***************************************************************/

CREATE PARTITION FUNCTION PF_Orders_OrderDateRange(DATETIME)

AS

RANGE RIGHT FOR VALUES

(

   '1997-01-01',

   '1998-01-01',

   '1999-01-01'

)

GO

 

EXEC dbo.sp_show_partition_range

     @partition_function = 'PF_Orders_OrderDateRange'

/***************************************************************

显示分区函数的分区情况,PARTITION FUNCTION,PARTITION,MinVal,VALUE,MaxVal 

PF_Orders_OrderDateRange    1    NULL                                   <= val <    1997-01-01 00:00:00.000

PF_Orders_OrderDateRange    2    1997-01-01 00:00:00.000    <= val <    1998-01-01 00:00:00.000

PF_Orders_OrderDateRange    3    1998-01-01 00:00:00.000    <= val <    1999-01-01 00:00:00.000

PF_Orders_OrderDateRange    4    1999-01-01 00:00:00.000    <= val <    NULL

****************************************************************/

 

/***************************************************************

2. 创建了分区函数后,便可以创建分区方案

*** 因为在上一个分区函数中有个边界值,4个分区,并且并没有其它的

    数据库文件组,所以当分区方案应用到具体的分区函数时所有的分区

    都是指向PRIMARY 文件组

***************************************************************/

CREATE PARTITION SCHEME PS_Orders

AS

PARTITION PF_Orders_OrderDateRange

TO ([primary],[primary],[primary],[primary])

GO

 

/***************************************************************

3. 创建分区表时要应用分区方案,并提供具体的分区键列

ON 分区函数(分区键列)

****************************************************************/

CREATE TABLE dbo.Orders

(

   OrderID     INT         NOT NULL,

   CustomerID  VARCHAR(10) NOT NULL,

   EmployeeID  INT         NOT NULL,

   OrderDate   DATETIME    NOT NULL

)

ON PS_Orders(OrderDate)

GO

 

/******************************************************************

4. 在创建分区表后,需要创建聚集分区索引

 

*** 根据订单表Orders 查询时经常使用OrderDate 范围条件来查询的特点,

*** 我们最好在Orders.OrderDate 列上建立聚集索引(clustered index.

*** 为了便于进行分区切换(partition swtich)

    大多数情况下,建议在分区表上建立分区索引。

*******************************************************************/

CREATE CLUSTERED INDEX IXC_Orders_OrderDate ON dbo.Orders(OrderDate)

GO

 

/*******************************************************************

5. 为分区表创建主键

 

如果主键不包含分区键列,将会出现以下错误信息:

 

Msg 1908, Level 16, State 1, Line 2

Column 'OrderDate' is partitioning column of the index 'PK_Orders'.

Partition columns for a unique index must be a subset of the index key.

Msg 1750, Level 16, State 0, Line 2

Could not create constraint. See previous errors.

 

原因:

主键实际上是个唯一索引,但分区表在建立唯一索引(分区索引)的时候,

分区列必须是唯一索引的一部分.因为SQL Server 不但要保证索引在各个

分区是唯一的,还要保证在整个表中是唯一的.

********************************************************************/

ALTER TABLE dbo.Orders ADD CONSTRAINT PK_Orders

PRIMARY KEY(OrderID, CustomerID,OrderDate)

GO

 

/************************************************************************

查看分区表Orders 上的索引:

 

IXC_Orders_OrderDate|clustered located on PS_Orders|OrderDate

PK_Orders|nonclustered,unique,primary key located on PS_Orders|OrderID, CustomerID, OrderDate

************************************************************************/

EXEC sp_helpindex 'dbo.Orders'

 

/**********************************************************************

6. SQL Server 2000 NorthWind 导入测试数据

***********************************************************************/

INSERT INTO dbo.Orders

SELECT OrderID,

       CustomerID,

       EmployeeID,

       OrderDate

FROM Northwind.dbo.Orders

 

/************************************************************************

7. 查看分区表各分区数据情况(数据行数,最大最小OrderDate 值)

*************************************************************************/

SELECT PARTITION = $PARTITION.PF_Orders_OrderDateRange(OrderDate),

       ROWS      = COUNT(*),

       MinVal    = MIN(OrderDate),

       MaxVal    = MAX(OrderDate)

FROM dbo.Orders

GROUP BY $PARTITION.PF_Orders_OrderDateRange(OrderDate)

ORDER BY PARTITION

GO

/************************************************************************

在目前的测试数据中,并没有大于1999年的数据,所以在上面的查询结果中并没有

看到第个分区的信息: PARTITION,ROWS,MinVal,MaxVal

1    152    1996-07-04 00:00:00.000    1996-12-31 00:00:00.000

2    408    1997-01-01 00:00:00.000    1997-12-31 00:00:00.000

3    270    1998-01-01 00:00:00.000    1998-05-06 00:00:00.000

*************************************************************************/

 

-- 插入一条测试数据

INSERT INTO dbo.Orders VALUES(11111,'TEST',1,'2000-10-10 10:10:10:100')

 

-- 再次查询

SELECT PARTITION = $PARTITION.PF_Orders_OrderDateRange(OrderDate),

       ROWS      = COUNT(*),

       MinVal    = MIN(OrderDate),

       MaxVal    = MAX(OrderDate)

FROM dbo.Orders

GROUP BY $PARTITION.PF_Orders_OrderDateRange(OrderDate)

ORDER BY PARTITION

/************************************************************************

查询结果显示了个分区的信息

PARTITION,ROWS,MinVal,MaxVal

1    152    1996-07-04 00:00:00.000    1996-12-31 00:00:00.000

2    408    1997-01-01 00:00:00.000    1997-12-31 00:00:00.000

3    270    1998-01-01 00:00:00.000    1998-05-06 00:00:00.000

4    1        2000-10-10 10:10:10.100    2000-10-10 10:10:10.100

*************************************************************************/

GO

 

/*************************************************************************

************* 切换分区表的一个分区到普通数据表***************************

************* Partition to Table ****************************************/

 

/*************************************************************************

1. 首先建立普通数据表Orders_1998,该表用来存放订单日期为1998 年的所有数据

2. 分区到普通表的切换,最好满足以下的前提条件:

   a. 普通表必须建立在分区表切换分区所在的文件组上ON [PRIMARY]

   b. 普通表的表结构跟分区表的一致

   c. 普通表上的索引要跟分区表一致(聚集索引,非聚集索引)

   d. 普通表必须是空表,不能有任何数据

*************************************************************************/

IF OBJECT_ID('Orders_1998') IS NOT NULL

DROP TABLE Orders_1998

GO

 

CREATE TABLE dbo.Orders_1998

(

   OrderID     INT         NOT NULL,

   CustomerID  VARCHAR(10) NOT NULL,

   EmployeeID  INT         NOT NULL,

   OrderDate   DATETIME    NOT NULL

)ON [PRIMARY]

GO

 

-- 添加聚集索引,和分区表一致

CREATE CLUSTERED INDEX IXC_Orders1998_OrderDate ON dbo.Orders_1998(OrderDate)

GO

 

-- 添加主键,和分区表一致

ALTER TABLE dbo.Orders_1998 ADD CONSTRAINT PK_Orders_1998

PRIMARY KEY(OrderID,CustomerID,OrderDate)

GO

 

/***************************************************************************

** 开始切换分区表Orders 第三个分区的数据(1998年的数据)到普通表Orders_1998

** 关键字- SWITCH PARTITION [NUMBER] TO [History Table]

***************************************************************************/

ALTER TABLE dbo.Orders SWITCH PARTITION 3 TO dbo.Orders_1998

 

/***************************************************************************

查询源分区表结果

分区号为的数据已经没有了

1    152    1996-07-04 00:00:00.000    1996-12-31 00:00:00.000

2    408    1997-01-01 00:00:00.000    1997-12-31 00:00:00.000

4    1        2000-10-10 10:10:10.100    2000-10-10 10:10:10.100

***************************************************************************/

SELECT PARTITION = $PARTITION.PF_Orders_OrderDateRange(OrderDate),

       ROWS      = COUNT(*),

       MinVal    = MIN(OrderDate),

       MaxVal    = MAX(OrderDate)

FROM dbo.Orders

GROUP BY $PARTITION.PF_Orders_OrderDateRange(OrderDate)

ORDER BY PARTITION

 

/***************************************************************************

查询存档表结果

3    270    1998-01-01 00:00:00.000    1998-05-06 00:00:00.000

***************************************************************************/

SELECT PARTITION = $PARTITION.PF_Orders_OrderDateRange(OrderDate),

       ROWS      = COUNT(*),

       MinVal    = MIN(OrderDate),

       MaxVal    = MAX(OrderDate)

FROM dbo.Orders_1998

GROUP BY $PARTITION.PF_Orders_OrderDateRange(OrderDate)

ORDER BY PARTITION

 

/************************************************************************

*************  切换普通表数据到分区表的一个分区中  *********************

*************    Table to Partition             ************************/ 

 

/*************************************************************************

上面我们已经把分区表Orders 第三个分区的数据切换到普通表Orders_1998 中了,

现在我们再切换回来:

**************************************************************************/

ALTER TABLE dbo.Orders_1998 SWITCH PARTITION 3 TO dbo.Orders

/*************************************************************************

错误信息:

Msg 4911, Level 16, State 2, Line 1

Cannot specify a partitioned table without partition number in ALTER TABLE

SWITCH statement. The table 'SSISDemoDB.dbo.Orders' is partitioned.

原因:

实际上应该是将dbo.Orders_1998 表中的数据SWITCH dbo.Orders 表的

Partition 分区中.

而不能说是将dbo.Orders_1998 的分区的数据SWITCH dbo.Orders 全表中

**************************************************************************/

 

ALTER TABLE dbo.Orders_1998 SWITCH TO dbo.Orders PARTITION 3

/*************************************************************************

错误信息:

Msg 4982, Level 16, State 1, Line 1

ALTER TABLE SWITCH statement failed. Check constraints of source table

'dbo.Orders_1998' allow values that are not allowed by range defined by

partition 3 on target table 'dbo.Orders'.

 

原因:

dbo.Orders 的数据经过分区函数的分区列定义, 各个分区的数据实际上已经经过

了数据约束检查,符合分区边界范围(Range)的数据才会录入到各个分区中.

但是在历史表/存档表dbo.Orders_1998中的数据实际上是没有边界约束的,比如完全

可以手动的插入一条年的数据,这样一来在进行SWITCH时肯定是不会成功的.

所以在SWITCH,先进行了约束性检查,尽管没有不符合规范的数据,但是有潜在的威胁.

 

所以在SWITCH之前,先为dbo.Orders_1998添加一个检查约束,并再次SWITCH,成功!

**************************************************************************/

ALTER TABLE dbo.Orders_1998 ADD CONSTRAINT CK_Orders1998_OrderDate

CHECK(OrderDate>='1998-01-01' AND OrderDate<'1999-01-01')

 

ALTER TABLE dbo.Orders_1998 SWITCH TO dbo.Orders PARTITION 

 

/***************************************************************************

查询源分区表结果,分区的数据已经从dbo.Orders_1998 回来了

1    152    1996-07-04 00:00:00.000    1996-12-31 00:00:00.000

2    408    1997-01-01 00:00:00.000    1997-12-31 00:00:00.000

3    270    1998-01-01 00:00:00.000    1998-05-06 00:00:00.000

4    1        2000-10-10 10:10:10.100    2000-10-10 10:10:10.100

***************************************************************************/

SELECT PARTITION = $PARTITION.PF_Orders_OrderDateRange(OrderDate),

       ROWS      = COUNT(*),

       MinVal    = MIN(OrderDate),

       MaxVal    = MAX(OrderDate)

FROM dbo.Orders

GROUP BY $PARTITION.PF_Orders_OrderDateRange(OrderDate)

ORDER BY PARTITION

 

/***************************************************************************

查询存档表结果,没有任何数据,已经成功SWITCH to Orders 表的PARTITION 3

***************************************************************************/

SELECT PARTITION = $PARTITION.PF_Orders_OrderDateRange(OrderDate),

       ROWS      = COUNT(*),

       MinVal    = MIN(OrderDate),

       MaxVal    = MAX(OrderDate)

FROM dbo.Orders_1998

GROUP BY $PARTITION.PF_Orders_OrderDateRange(OrderDate)

ORDER BY PARTITION

/****************************************************************************

所以在进行存档表的数据向分区表迁移过程中(TABLE TO PARTITION),

相比(PARTITION TO TABLE)多一个条件:

普通表必须加上和分区数据范围一致的约束条件.

*****************************************************************************/

 

/************************************************************************

*************  切换分区表数据到分区表   ********************************

*************    PARTITION TO PARTITION  **********************************/

 

/*************************************************************************

-- 新的存档分区表在结构上和源分区表是一致的,包括分区函数和分区方案,

但是需要重新创建,不能简单地直接使用dbo.Orders 表上的分区函数和分区方案,

因为他们之间有绑定关系

**************************************************************************/

IF OBJECT_ID('OrdersArchive') IS NOT NULL

DROP TABLE OrdersArchive

GO

 

IF EXISTS (SELECT * FROM sys.partition_schemes WHERE name = 'PS_OrdersArchive')

DROP PARTITION SCHEME PS_OrdersArchive

GO

 

IF EXISTS (SELECT * FROM sys.partition_functions WHERE name = 'PF_OrdersArchive_OrderDateRange')

DROP PARTITION FUNCTION PF_OrdersArchive_OrderDateRange

GO

 

CREATE PARTITION FUNCTION PF_OrdersArchive_OrderDateRange(DATETIME)

AS

RANGE RIGHT FOR VALUES

(

   '1997-01-01',

   '1998-01-01',

   '1999-01-01'

)

GO

 

CREATE PARTITION SCHEME PS_OrdersArchive

AS

-- 分区Scheme和分区函数绑定了

PARTITION PF_OrdersArchive_OrderDateRange

TO ([primary],[primary],[primary],[primary])

GO

 

CREATE TABLE dbo.OrdersArchive

(

   OrderID     INT         NOT NULL,

   CustomerID  VARCHAR(10) NOT NULL,

   EmployeeID  INT         NOT NULL,

   OrderDate   DATETIME    NOT NULL

)

-- 表和分区Scheme绑定了

ON PS_OrdersArchive(OrderDate)

GO

 

CREATE CLUSTERED INDEX IXC_OrdersArchive_OrderDate ON dbo.OrdersArchive(OrderDate)

 

ALTER TABLE dbo.OrdersArchive ADD CONSTRAINT PK_OrdersArchive

PRIMARY KEY(OrderID, CustomerID,OrderDate)

GO

 

/*********************************************************************************

开始切换分区

**********************************************************************************/

ALTER TABLE dbo.Orders SWITCH PARTITION  TO dbo.OrdersArchive PARTITION 1

ALTER TABLE dbo.Orders SWITCH PARTITION  TO dbo.OrdersArchive PARTITION 2

ALTER TABLE dbo.Orders SWITCH PARTITION  TO dbo.OrdersArchive PARTITION 3

/***************************************************************************

查询源分区表结果,只会有分区的数据

4    1    2000-10-10 10:10:10.100    2000-10-10 10:10:10.100

***************************************************************************/

SELECT PARTITION = $PARTITION.PF_Orders_OrderDateRange(OrderDate),

       ROWS      = COUNT(*),

       MinVal    = MIN(OrderDate),

       MaxVal    = MAX(OrderDate)

FROM dbo.Orders

GROUP BY $PARTITION.PF_Orders_OrderDateRange(OrderDate)

ORDER BY PARTITION

 

/***************************************************************************

查询存档表结果,已经成功转移

1    152    1996-07-04 00:00:00.000    1996-12-31 00:00:00.000

2    408    1997-01-01 00:00:00.000    1997-12-31 00:00:00.000

3    270    1998-01-01 00:00:00.000    1998-05-06 00:00:00.000

***************************************************************************/

SELECT PARTITION = $PARTITION.PF_OrdersArchive_OrderDateRange(OrderDate),

       ROWS      = COUNT(*),

       MinVal    = MIN(OrderDate),

       MaxVal    = MAX(OrderDate)

FROM dbo.OrdersArchive

GROUP BY $PARTITION.PF_OrdersArchive_OrderDateRange(OrderDate)

ORDER BY PARTITION

 

/***************************************************************************

总结: 分区表分区切换并没有真正去移动数据,而是SQL Server 在系统底层改变了表

的元数据。因此分区表分区切换是高效,快速,灵活的.利用分区表的分区切换功能,

我们可以快速加载数据到分区表.卸载分区数据到普通表,然后TRUNCATE 普通表,

以实现快速删除分区表数据,快速归档不活跃数据到历史表。

****************************************************************************/

注 : 已经不记得原博客地址了, 这篇日志是基于别人的分析成果之上加上自己亲自实践, 思考, 重新添加了一些代码和注释. 在注解和结论验证方面按照自己的理解做出了还算比较细致的说明,对刚接触表分区概念的朋友们相信会有很大的帮助. 我也是通过这种方式学习和掌握了表分区的一些基础操作步骤和处理方式.