首先普及下sql server数据库的三种常见文件类型:
  .mdf 全称(primary data file)
  主要数据文件包含数据库的启动信息,并指向数据库中的其他文件。用户数据和对象可存储在此文件中,也可以存储在次要数据文件中。每个数据库有一个主要数据文件。主要数据文件的建议文件扩展名是 .mdf。
   .ndf全称(Secondary data files)
  次要数据文件是可选的,由用户定义并存储用户数据。通过将每个文件放在不同的磁盘驱动器上,次要文件可用于将数据分散到多个磁盘上。另外,如果数据库超过了单个 Windows 文件的最大大小,可以使用次要数据文件,这样数据库就能继续增长,次要数据文件的建议文件扩展名是 .ndf。
   .ldf 全称(log data files)
   事务日志文件保存用于恢复数据库的日志信息。每个数据库必须至少有一个日志文件。事务日志的建议文件扩展名是 .ldf。
   而所谓的分区,其实就是建出多个.ndf文件,用分区函数把table里面的用户存储的数据按照一定的分区策略存储在不同的.ndf中。
   分区函数的有点:
  1.改善查询性能:关键点就是并行IO,当然真正应用我们一般是磁盘阵列;
  2.增强可用性:如果表的某个分区出现故障,表在其他分区的数据仍然可用,当然生产环境不受限的话会是故障转移群集
  3.维护方便:如果表的某个分区出现故障,需要修复数据,修复该分区即可;
  注意:分区函数不能解决负载均衡,当一个磁盘空闲的时候,分区函数和分区策略都不会检测哪个磁盘时空闲的,而是把数据按照分区函数的分区策略继续分配到各个分区里(磁盘)去,所以原本高负荷的磁盘还是高负荷,不能达到负载均衡。
  进入正题,step1:先把用到的文件夹建立起来,不然执行语句会报错文件夹路劲找不到,,这里打算做3个分区,所以有3个.ndf,1个.mdf,1个.ldf:

create  database  mytest on primary
(
  name=N'mytest',
  filename=N'D:\data\Primary\mytest.mdf',
  size=20MB,
  maxsize=100MB,
  filegrowth=10%
),
filegroup MyFG1
(
 NAME = N'File1',   
 FILENAME = N'D:\data\FG1\File1.ndf',   
 SIZE = 5MB,   
 MAXSIZE = 100MB,   
 FILEGROWTH = 10% 
),
FILEGROUP MyFG2   
(   
 NAME = N'File2',   
 FILENAME = N'D:\data\FG2\File2.ndf',   
 SIZE = 5MB,   
 MAXSIZE = 100MB,
 FILEGROWTH = 10%   
),
FILEGROUP MyFG3   
(   
 NAME = N'File3',   
 FILENAME = N'D:\data\FG3\File3.ndf',   
 SIZE = 5MB,   
 MAXSIZE = 100MB,   
 FILEGROWTH = 10%   
)   
LOG ON   
(   
 NAME = N'mytest_Log',   
 FILENAME = N'D:\data\Primary\mytest_Log.ldf',   
 SIZE = 20MB,   
 MAXSIZE = 100MB,   
 FILEGROWTH = 10%
)
GO

step2:建分区函数:

USE mytest ;
GO
CREATE PARTITION FUNCTION UDF_PF_DateKey (int)    --注意参数类型需要和下面的value相对应
AS RANGE RIGHT   
FOR VALUES ('201801', '201802')  --values的值需要满足形参的类型或者能隐式转换成形成类型
GO

--删除分区语法
drop partition function <分区函数名>

step3:关联分区函数把分区策略建出来:

Use mytest ;
go
create  partition  scheme UDF_PS_DateKey
as partition  UDF_PF_DateKey
to(MyFG1,MyFG2,MyFG3)
go

--删除分区方案语法
drop partition scheme<分区方案名称>

关于分区函数和分区策略的关系,可以理解为,分区函数里面的values是分区策略里面的分界点,类似五个手指四个叉,即四个叉是分区函数的values,五个手指就是五个分区,关系如下图(当然分区实际磁盘存储空间不一定是按照下图一样连续的,不要误解),取左边边界:

sql server 数据库分文件存储 sql server数据文件分类_sql server 数据库分文件存储


step4:建分区表,其实很简单,就是多一句ON [UDF_PF_DateKey](datekey)的操作,当然这个就有讲究了,如果你的分区表后面有switch的操作,那么一定要先建分区再建非聚集索引,否则,先建非聚集索引再建分区的话,会导致非聚集索引不会分区,做switch操作会报错,没有该操作则无所谓,一般三种场景,具体说明如下:

IF EXISTS(select 1 from dbo.sysobjects where id = object_id(N'dbo.mytesttable') and OBJECTPROPERTY(id, N'IsUserTable') = 1) 
DROP table dbo.mytesttable;
go

create table dbo.mytesttable
(
    datekey   int not null
   ,col1  bigint not null
   ,col2  int not null
   ,col3 nvarchar(200)
   ,col4 decimal(18,2)
   ,IsDeleted bit
   ,inserttime  datetime
   ,updatetime  datetime   
)ON [UDF_PS_DateKey](datekey)    --注意明确要做分区表,ON [UDF_PS_DateKey](datekey)在这个位子最合适,而且用的是分区策略的名字而不是分区函数的名字,传入的实参要和分区函数定义的形参类型一致或者能隐式转换;
;

--create PK and Partition
ALTER TABLE dbo.mytesttable ADD  CONSTRAINT [PK_mytesttable] PRIMARY KEY CLUSTERED 
(
    datekey ASC,
    col1 ASC,
    col2 ASC
) --如果该表之前不是分区表,这次改成分区表,用这句alter table,同时 ON [UDF_PS_DateKey](datekey)在后面,数据会重新分区的,当然做这个之前先把非聚集索引删除,建好这个分区以后再重新建;
--create NONCLUSTERED
--Warn:Must create partition at first,then create NONCLUSTERED,if not,It will be error on switch partition
CREATE NONCLUSTERED INDEX IX_mytesttable_col3  ON dbo.mytesttable (col3 ASC);
CREATE NONCLUSTERED INDEX IX_mytesttable_col4 ON dbo.mytesttable (col4 ASC);

step5,正常insert数据,insert的操作和普通表没什么区别,写入测试数据如下:

sql server 数据库分文件存储 sql server数据文件分类_sql server分区函数_02

step6,怎么查看某个数据在哪一个分区如下:

[ database_name. ] $PARTITION.partition_function_name(expression)返回是一个int型,从1到最大分区数
如查看不同数据所在的分区:
select top 100 $PARTITION.UDF_PF_DateKey(datekey) PartitionNo,datekey from dbo.mytesttable;

sql server 数据库分文件存储 sql server数据文件分类_sql server 数据库分文件存储_03

查询每个分区含有多少数据:
select $PARTITION.UDF_PF_DateKey(datekey) partition_name,count(*) amount from rpt.mytesttable group by $PARTITION.UDF_PF_DateKey(datekey);

sql server 数据库分文件存储 sql server数据文件分类_mdf与ndf_04


到此,分区的原理和实现就结束了,下面来说一下关于分区的实用性的操作:

1.合并分区,代码如下,

ALTER PARTITION FUNCTION UDF_PF_DateKey() MERGE RANGE ('201802')
--分区datekey为201802的数据全部会合并到>=201801这个分区,同时,分区策略也会自动跟着更新

--再下面的语句,得到的结果是
--partition_name	amount
--1	                   1
--2	                   5
select $PARTITION.UDF_PF_DateKey(datekey) partition_name,count(*) amount from dbo.mytesttable group by $PARTITION.UDF_PF_DateKey(datekey);

2.拆分分区,即如果分区文件组已经存在,可以给分区再加一个分界点,如在第一步已经合并掉了分区>=201802,分区策略也跟着变化了,但是文件组MyFG3是还存在的,如果要回复就是如下代码:

Use mytest ;
go

ALTER PARTITION SCHEME UDF_PS_DateKey NEXT USED MyFG3 --先调整分区策略,因为在第一步已经自动更改了分区策略
ALTER PARTITION FUNCTION UDF_PF_DateKey() split RANGE(N'201802')  --将第二个分区拆为2个分区

3.新建分区文件组和新建分区

ALTER DATABASE mytest ADD FILEGROUP MyFG4;  --添加一个文件组
--绑定文件组和具体文件的地址
ALTER DATABASE mytest ADD FILE 
( 
    NAME = N'File4', 
    FILENAME = N'C:D:\data\FG4\File4.ndf' , 
    SIZE = 5MB,   
    MAXSIZE = 100MB,   
    FILEGROWTH = 10% 
) TO FILEGROUP MyFG4
;
ALTER PARTITION SCHEME UDF_PS_DateKey NEXT USED MyFG4 --为分区方案指定一个可用的文件组
 
ALTER PARTITION FUNCTION UDF_PF_DateKey() SPLIT RANGE('201803') -- 修改分区函数,添加分区

--执行
select $PARTITION.UDF_PF_DateKey(datekey) partition_name,count(*) amount from dbo.mytesttable group by $PARTITION.UDF_PF_DateKey(datekey);
--结果为
--partition_name	amount
--1	                  1
--2                   2
--3	                  2
--4	                  1

4.交换分区的数据(十分有用也十分危险)

select * into [dbo].[mytest_partition1] from [dbo].[mytesttable] where 1=2;
--create PK and Partition
ALTER TABLE [dbo].[mytest_partition1] ADD  CONSTRAINT [PK_[mytest_partition1] PRIMARY KEY CLUSTERED 
(
    datekey ASC,
    col1 ASC,
    col2 ASC
)ON [UDF_PS_DateKey](datekey); --如果该表之前不是分区表,这次改成分区表,用这句alter table,同时 ON [UDF_PS_DateKey](datekey)在后面,数据会重新分区的,当然做这个之前先把非聚集索引删除,建好这个分区以后再重新建;
--create NONCLUSTERED
--Warn:Must create partition at first,then create NONCLUSTERED,if not,It will be error on switch partition
CREATE NONCLUSTERED INDEX IX_mytest_partition1_col3  ON dbo.mytest_partition1 (col3 ASC);
CREATE NONCLUSTERED INDEX IX_mytest_partition1_col4 ON dbo.mytest_partition1 (col4 ASC);
--以上代码为了建立一张和原来分区表一致的其他表

alter table [dbo].[mytesttable] switch partition 1 to dbo.mytest_partition1 partition 1; --将mytesttable的partition 1的数据分配到dbo.mytest_partition1 partition 上,注意:此操作一旦执行mytesttable的partition 1没有数据了,被移到了dbo.mytest_partition1,且该操作的前提是目标表的partition 1最开始必须为空

--利用下面代码可以验证
SELECT * FROM [mytest].[dbo].[mytest_partition1];

SELECT * FROM [dbo].[mytesttable];