分区表概述:
大凡在应用系统和数据库系统中行走江湖多年的朋友,都会面临数据统计、分析以及归档的问题,企业信息化进程加速了各种数据的极具增长,商务智能(BI)的出现和实施着实给信息工作者和决策者带来了绝妙的体验,但从 OLTP 向 OLAP系统加载数据是很头疼的事,常常需要数分钟或数小时,解决这一问题的技术之一便是分区表,一旦实施了分区表,这样的操作往往只需几秒钟,太让人兴奋了。而大型表或索引经过分区后更容易进行管理,因为这样可以快速高效地管理和访问数据子集,同时维护数据集合的完整性。分区表的数据分布于一个数据库中的多个文件组单元中,数据是按水平方式分区的(数据分区的多种方式会在分区表前传中阐述),因此一个表的某些行映射到某个分区,而另外一些行映射到另外某个分区,以此类推。当对数据进行查询或更新时,表将被视为单个逻辑实体,所以在数据访问层你会感觉和访问普通表一样,而好处就在于可以查询想要的某个分区,而不必扫描整个表。有一点必须明白,单个表的所有分区都必须位于同一个数据库中。
分区表支持和标准表相关的所有属性和功能,包括约束、默认值、标识和时间戳值以及触发器等。决定是否实现分区主要取决于表当前的大小或将来的大小以及对表执行查询和维护操作的完善程度。
通常,如果某个大型表同时满足下列两个条件,则可能适于进行分区:
○1 、该表包含或将包含以多种不同方式使用的大量数据
○2 、维护开销超过了预定义的维护期
例如,
如果对当前年份或当前月份的数据主要执行SELECT 、INSERT、UPDATE 和DELETE 操作,而对以前年份或以前月份的数据主要执行SELECT 查询,则如果按年份或月份对表进行分区,表的管理要容易些,因为此时对表的维护操作只针对一个数据子集。如果该表没有分区,那么就需要对整个数据集执行这些操作,这样就会消耗大量资源。所以常常根据日期和分类对表进行分区,当然利用某个标识列ID 也是很好的选择。例如,电子商务数据库的某张表可能包含了近6 年的数据,但是只定期访问本年度或某个月的数据,那么就可以按年份或月份分区,而另外一张表包含了近几十种类型商品的订单,那么此时可以为每种类型商品分一个区。
一般而言,衡量大型表是以数据为标准的,但对于适合分区的大型表,衡量大型表更重要的是对数据访问的性能,如果对于某些表的访问和维护有较严重的性能问题,就可以视为大型表,就应该考虑通过更好的设计和分区来解决性能问题。
创建分区表必须经过如下三个步骤:
○1 、 创建分区函数
○2 、 创建映射到分区函数的分区方案
○3 、 创建使用该分区方案的分区表
1)、分区函数:
分区函数是数据库中的一个独立对象,它将表的行映射到一组分区,所以分区函数解决的是HOW 的问题,即表如何分区的问题。创建分区函数时,必须指明数据分区的边界点以及分区依据列,这样便知道如何对表或索引进行分区。分区函数的创建语法如下:
CREATE PARTITION FUNCTION partition_function_name(input_parameter_type)
AS RANGE [ LEFT | RIGHT]
FOR VALUES ( [boundary_value [ ,...n ] ] )
[ ; ]
2)、分区方案
对表和索引进行分区的第二步是创建分区方案。分区方案定义了一个特定的分区函数将使用的物理存储结构(其实就是文件组),或者说是分区方案将分区函数生成的分区映射到我们定义的一组文件组。所以分区方案解决的是Where 的问题,即表的各个分区在哪里存储的问题。
分区方案的创建语法如下:
CREATE PARTITION SCHEMEpartition_scheme_name
AS PARTITION partition_function_name
[ ALL ] TO ( { file_group_name|[PRIMARY ] } [ ,...n ] )
[ ; ]
分区方案例子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)
分区方案例子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)
分区方案例子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)
分区方案例子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 个分区。
3)、分区表
在分区函数和分区方案创建完成后,创建分区表的准备工作已经完成。我们看一个完整的例子,代码如下:
--创建分区函数
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)
4)、DEMO1:新建一个表,写入100万条记录,要求<200000 的记录存储在FG1、200000<&<600000记录存储在FG2,>600000记录存储在FG3
删除demo_db 数据库中文件及文件组:
USE [demo_db]
GO
ALTER DATABASE demo_db
REMOVE FILE file1
GO
ALTER DATABASE [demo_db]
REMOVE FILEGROUP fg1
GO
ALTER DATABASE demo_db
REMOVE FILE file2
GO
ALTER DATABASE [demo_db]
REMOVE FILEGROUP fg2
GO
ALTER DATABASE demo_db
REMOVE FILE file3
GO
ALTER DATABASE demo_db
REMOVE FILEGROUP fg3
GO
分区表创建流程:
创建文件组 → 建文件(指定属于那个文件组) → 创建分区函数(分区的条件) → 创建分区方案 → 生产环境中(将需分区的文件重命名) → 新增一个table,配置其onschema,所在的分区分案 → 从旧表导入数据到新表 → 验证
--1、建立我们要使用的数据库,最重要的是建立多个文件组
CREATE DATABASE Sales ON PRIMARY
(
NAME = N‘Sales‘,
FILENAME = N‘C:\Sales.mdf‘,
SIZE = 3MB,
MAXSIZE = 100MB,
FILEGROWTH = 10%
),
FILEGROUP FG1
(
NAME = N‘File1‘,
FILENAME = N‘C:\File1.ndf‘,
SIZE = 1MB,
MAXSIZE = 100MB,
FILEGROWTH = 10%
),
FILEGROUP FG2
(
NAME = N‘File2‘,
FILENAME = N‘C:\File2.ndf‘,
SIZE = 1MB,
MAXSIZE = 100MB,
FILEGROWTH = 10%
),
FILEGROUP FG3
(
NAME = N‘File3‘,
FILENAME = N‘C:\File3.ndf‘,
SIZE = 1MB,
MAXSIZE = 100MB,
FILEGROWTH = 10%
)
LOG ON
(
NAME = N‘Sales_Log‘,
FILENAME = N‘C:\Sales_Log.ldf‘,
SIZE = 1MB,
MAXSIZE = 100MB,
FILEGROWTH = 10%
)
GO
--2、建立分区函数,这里我们建立三个分区。how(如何对数据进行分区)
USE Sales
go
CREATE PARTITION FUNCTION [PF1] (int)
AS RANGE RIGHT
FOR VALUES (200000,600000)
/*
注意CREATEPARTITION FUNCTION 建立数据分割函数有RANGE LEFT|RIGHT 两种选择代表每个边界值是在局部的哪一边,即符合该值的记录是在局部的哪一边,例如:
参数 partition1 2 3
LEFT值-∞<值域<=20W 20w<值域<=60W 60w 以上
RIGHT值-∞<值域<20W 20w<=值域<60W 60w 以上(含)
*/
--3、创建分区方案,关联到分区函数 。 where(在哪里对数据进行分区)
create partition scheme ps1
as partition PF1
to (FG1,FG2,FG3)
--4、创建带数据分区的测试表
CREATE TABLE tbl
(
c1 int identity(1,1),
c2 int,c3 nvarchar(100)
)
on PS1(c1)
--5、通过循环写入测试数据
DECLARE @i int
SET @i=1
WHILE @i<21
BEGIN
INSERT tbl(c2,c3) SELECT c2,c3 FROM tbl
SET @i=@i+1
END
--6、最后我们查询数据是否真的被分割
SELECT $PARTITION.[PF1](c1) as [partition],*
FROM tbl where c1 in (1,200000,200001,599999,600000,600001)
5)、DEMO2:对表timeRecords 进行分区,并按日期进行分区
--1、创建一个测试数据库,并创建3 个文件组及3 个文件,将3 个文件指定到文件组中.
CREATE DATABASE [test] ON PRIMARY
( NAME = N‘test‘,
FILENAME = N‘C:\ProgramFiles\Microsoft SQL
Server\MSSQL10.MSSQLSERVER\MSSQL\DATA\test.mdf‘,
SIZE = 75776KB ,
MAXSIZE = UNLIMITED,
FILEGROWTH = 1024KB ),
FILEGROUP [FGROUP1]
( NAME = N‘fgroup1‘,
FILENAME = N‘C:\ProgramFiles\Microsoft SQL
Server\MSSQL10.MSSQLSERVER\MSSQL\DATA\fgroup1.ndf‘,
SIZE = 2048KB ,
MAXSIZE = UNLIMITED,
FILEGROWTH = 1024KB ),
FILEGROUP [FGROUP2]
( NAME = N‘fgroup2‘,
FILENAME = N‘C:\ProgramFiles\Microsoft SQL
Server\MSSQL10.MSSQLSERVER\MSSQL\DATA\fgroup2.ndf‘,
SIZE = 2048KB ,
MAXSIZE = UNLIMITED,
FILEGROWTH = 1024KB ),
FILEGROUP [FGROUP3]
( NAME = N‘fgroup3‘,
FILENAME = N‘C:\ProgramFiles\Microsoft SQL
Server\MSSQL10.MSSQLSERVER\MSSQL\DATA\fgroup3.ndf‘,
SIZE = 2048KB ,
MAXSIZE = UNLIMITED,
FILEGROWTH = 1024KB )
LOG ON
( NAME = N‘test_log‘,
FILENAME = N‘C:\ProgramFiles\Microsoft SQL
Server\MSSQL10.MSSQLSERVER\MSSQL\DATA\test_log.ldf‘,
SIZE = 102144KB ,
MAXSIZE = 2048GB ,
FILEGROWTH = 10%)
GO
--2、创建分区函数:
按日期进行分区,要求<2011-07-01 的一个分区,在2011-07-01 至2011-07-31在一个分区,>=2011-08-01 在一个分区
create partition function PF1 (datetime)
as range right for values
(‘2011-07-01‘,
‘2011-08-01‘)
--3、创建分区方案
create partition scheme PS1
as partition PF1
to (FGROUP1,FGROUP2,FGROUP3)
GO
--4、从MYDB 中,将timerecords表复制到test 数据库中
select *
into timerecords
from mydb.dbo.TimeRecords
--5、创建一个空白的timerecords 表,格式与timerecords_bak 一样,指定表存储在新建的存储方案PS1 中
CREATE TABLE [dbo].[timerecords]
(
[id] [int] IDENTITY(1,1) NOT NULL,
[clock_id] [int] NOT NULL,
[emp_id] [nvarchar](12) NULL,
[join_id] [int] NULL,
[depart_id] [nvarchar](30) NULL,
[card_id] [nvarchar](16) NOT NULL,
[sign_time] [datetime] NOT NULL,
[mark] [tinyint] NULL,
[flag] [int] NULL,
[kind] [int] NULL,
[reader_no] [int] NULL,
[door_flag] [int] NULL,
[bill_id] [nvarchar](20) NULL,
[checker] [nvarchar](16) NULL,
[check_date] [datetime] NULL,
[op_user] [nvarchar](16) NULL,
[op_date] [datetime] NULL,
[audit] [bit] NULL,
[memo] [nvarchar](200) NULL,
[order_time] [datetime] NULL,
[order_money] [money] NULL,
[luocard] [bit] NULL,
[bcancel] [bit] NULL,
[is_days] [int] NULL
)
ON PS1(sign_time)
GO
--6、写入数据,由于timerecords表中第一列id 为自增列,因此在写入数据时,需去掉第一列id 才能从timerecords_bak中引入数据.
INSERT INTO [test].[dbo].[timerecords]
([clock_id]
,[emp_id]
,[join_id]
,[depart_id]
,[card_id]
,[sign_time]
,[mark]
,[flag]
,[kind]
,[reader_no]
,[door_flag]
,[bill_id]
,[checker]
,[check_date]
,[op_user]
,[op_date]
,[audit]
,[memo]
,[order_time]
,[order_money]
,[luocard]
,[bcancel]
,[is_days])
select
[clock_id]
,[emp_id]
,[join_id]
,[depart_id]
,[card_id]
,[sign_time]
,[mark]
,[flag]
,[kind]
,[reader_no]
,[door_flag]
,[bill_id]
,[checker]
,[check_date]
,[op_user]
,[op_date]
,[audit]
,[memo]
,[order_time]
,[order_money]
,[luocard]
,[bcancel]
,[is_days]
from timerecords_bak
GO
--7、查看分区的结果:
select $partition.PF1(SIGN_time)
as partition, *
from timerecords
where CONVERT(char(10),sign_time,121) in (‘2011-06-10‘,
‘2011-06-30‘,
‘2011-07-01‘,
‘2011-07-31‘,
‘2011-08-01‘,
‘2011-08-31‘)
and emp_id = ‘P1003101‘
order by sign_time
本文出自 “Ricky's Blog” 博客,请务必保留此出处http://57388.blog.51cto.com/47388/1628081
原文地址:http://57388.blog.51cto.com/47388/1628081