背景:
在前面兩篇博文《SQL Server 大數據管理——數據歸檔(主文件備份)》、《SQL Server 大數據管理——數據歸檔(段落備份)》中,表分區在其中起到了主要作用,本文將介紹分區的實現及表分區的相關屬性和操作。
一. 創建分區文件組/文件
--創建分區文件組
alter database test add filegroup test2015
alter database test add filegroup test2016
alter database test add filegroup test2017
alter database test add filegroup test2018
--創建分區文件
alter database test
add file(name='test2015'
,filename='D:\DB\testPartion\test2015.ndf'
,size=1mb
,filegrowth=1mb)
to filegroup test2015;
alter database test
add file(name='test2016'
,filename='D:\DB\testPartion\test2016.ndf'
,size=1mb
,filegrowth=1mb)
to filegroup test2016;
alter database test
add file(name=N'test2017'
,filename=N'D:\DB\testPartion\test2017.ndf'
,size=1mb
,filegrowth=1mb)
to filegroup test2017
alter database test
add file(name=N'test2018'
,filename=N'D:\DB\testPartion\test2018.ndf'
,size=1mb
,filegrowth=1mb)
to filegroup test2018
二. 創建分區函數
--創建分區函數
create partition function f_TestDate(datetime)
as range right for values('2016-01-01','2017-01-01','2018-01-01')
注意:
1. F_TestDate 爲分區函數名,分區的字段是datetime類型
2. Right 表示該分區包含右邊界值,上面分區函數會把數據分爲
小於2016.1.1
大於等於2016.1.1 且小於2017.1.1
大於等於2017.1.1 且小於2018.1.1
大於等於2018.1.1
四個分區,若把right換爲left,則分區變爲
小於等於2016.1.1
大於2016.1.1 且小於等於2017.1.1
大於2017.1.1 且小於等於2018.1.1
大於2018.1.1
三. 創建分區方案
--創建分區方案
create partition scheme s_TestDate
as partition f_TestDate to (test2015,test2016,test2017,test2018)
注意:
1. 分區方案是建立在分區函數的基礎上的,所以先建立分區函數,再建立分區方案
2. 分區個數比分區邊界值多1
3. 本分區方案每個分區建在一個文件組上,當然也可以把所有分區建立在一個文件組上
--創建分區方案,所有分區均建立在主文件組上
create partition scheme s_TestDate
as partition f_TestDate all to ([primary])
兩種方案的優劣待續……
四. 創建分區表
4.1 新建分區表
create table tradelog
(
ID int,
productID int,
tradedate datetime
) on s_TestDate(tradedate)注:創建分區表,用的是s_TestDate分區方案名稱
4.2 對已有表分區若表上沒有聚集索引,可以通過創建聚集索引,對錶進行分區
CREATE CLUSTERED INDEX [CLI_tn_TestDate] ON [dbo].[tradelog_noClusterIndex]
(
[tradedate]
) ON [s_TestDate]([tradedate])
--如果不需要聚集索引,刪除聚集索引
DROP INDEX [CLI_tn_TestDate] ON [dbo].[tradelog_noClusterIndex]若表上已有聚集索引,刪除聚集索引,再通過上面腳本重建聚集索引。或者通過WITH(DROP_EXISTING=ON)重建聚集索引,腳本如下:
CREATE CLUSTERED INDEX [CLI_tn_TestDate] ON [dbo].[tradelog_noClusterIndex]
(
[tradedate]
)WITH (DROP_EXISTING = ON) ON [s_TestDate]([tradedate])
五. 增加分區
增加分區的方法是將某個現有的分區“拆分”爲兩個分區並重新定義新分區的邊界。
--向分區表插入1000W行數據
DECLARE @max AS INT, @rc AS INT;
SET @max = 10000000;
SET @rc = 1;
INSERT INTO tradelog(id,productID,tradedate) VALUES(1,1,'2014-01-01');
WHILE @rc * 2 <= @max
BEGIN
INSERT INTO dbo.tradelog(id,productID,tradedate) SELECT id + @rc,id + @rc+1,DATEADD(mi,id,tradedate) FROM dbo.tradelog;
SET @rc = @rc * 2;
END
INSERT INTO dbo.tradelog (id,productID,tradedate)
SELECT id + @rc,id + @rc+1,DATEADD(mi,id,tradedate) FROM dbo.tradelog WHERE id + @rc <= @max;
go
--查看分區表的現狀
;with cte as
(select
object_id
,OBJECT_NAME(i.object_id) tableName
,i.index_id
,dds.partition_scheme_id
,dds.destination_id as partition_number
,fg.groupid
,fg.groupname
,f.fileid
,f.name
,f.filename
--,p.partition_id
--,p.rows
from sys.destination_data_spaces dds,sys.indexes i,sys.sysfilegroups fg,sys.sysfiles f
where dds.partition_scheme_id=i.data_space_id
and dds.data_space_id=fg.groupid
and fg.groupid=f.groupid
)
,cte1 as(
select
ps.data_space_id as partition_scheme_id
,ps.name partiton_schemes_name
,pf.name partition_function_name
,pf.function_id
--,prv.value AS BoundaryValue
from sys.partition_schemes ps ,sys.partition_functions pf
where ps.function_id=pf.function_id
--and pf.function_id=prv.function_id
)
select cte.tableName,cte.groupname,cte.name,cte.filename
,cte.partition_number,cte1.partiton_schemes_name,cte1.partition_function_name,p.rows
,prv.boundary_id,prv.value BoundaryValue
from cte
inner join cte1 on cte.partition_scheme_id=cte1 .partition_scheme_id
left join sys.partition_range_values prv on cte1.function_id=prv.function_id and cte.partition_number=prv.boundary_id
left join sys.partitions p on cte.object_id=p.object_id and cte.index_id=p.index_id and cte.partition_number=p.partition_number
where
cte.object_id=OBJECT_ID('dbo.tradelog','U')
可以看到tradelog表按交易時間列分爲4區,分區邊界值爲16、17、18三年的1月1日,其中
16年以前的數據存在文件test2015上
16年數據存在文件test2016上
17年數據存在文件test2017上
18年及以後的數據存在文件test2018上
現在增加一個分區,將2019以後的數據分開,或者說將原4分區以2019年1月1日爲分區邊界拆分爲兩個分區,具體腳本如下:
--創建新分區文件組
alter database test add filegroup test2019
--創建新分區文件
alter database test
add file(name='test2019'
,filename='D:\DB\testPartion\test2019.ndf'
,size=1mb
,filegrowth=1mb)
to filegroup test2019;
alter partition scheme s_TestDate
next used test2019
alter partition function f_TestDate()
split range('2019-01-01 0:00:00')
重新執行分區狀態查詢腳本,結果如下圖:
可以看到,源第4分區被拆分爲兩個分區,並且2019年以後的數據被移動到新的文件test2019上。
可以有這樣一個結論,新增分區後,新增的邊界值,到下一個分區邊界值之間的數據,將被移動到新的文件上,無論是拆分第1個分區,還是拆分中間的某個分區(如拆分第4個分區),如下圖:
六. 合併分區
減少分區的方法是將兩個分區的邊界“合併”成一個。 減少分區操作將重新填充一個分區而不對另一個分區進行分配。
--分區合併
alter partition function f_TestDate()
merge range('2018-07-01 0:00:00')
分區合併的數據移動方向剛好和增加分區的方向相反,分區合併後,將合併分界點的後一個分區數據移動到前一個分區的文件中。這個結論在數據自動歸檔中將極爲有用,因爲數據歸檔最後一步是將合併後的空文件、文件組回收,這樣就可以確定回收的文件名
七. 分區數據移到普通表
create table tradelog_partition1
(
ID int,
productID int,
tradedate datetime
) on test2015
alter table tradelog switch partition 1 to tradelog_partition1
把分區表的某個分區數據轉移到普通表,要求
1. 普通表必須和對應的分區在同一個文件組下
2. 普通表和分區表結構相同,包括字段、數據類型、數據長度、索引等
分區表上在tradedate上有聚集索引,但普通表tradelog_partition1上沒有建聚集索引,執行上述腳本就會報如下錯誤:
八. 普通表數據移到某一分區
alter table tradelog_partition1 switch to tradelog partition 1
在tradelog_partition1的tradedate上創建聚集索引,重新執行上面的腳本,又報瞭如下錯誤
What happen??這是因爲分區1上有CHECK日期要在2014到2016之間,而tradelog_partition1上沒有這個檢查,所以,在表上加上如下檢查:
ALTER TABLE dbo.tradelog_partition1
ADD CONSTRAINT TradeDate_Switch_CHECK CHECK
(TradeDate >= CONVERT(DATE,'2014-01-01') AND TradeDate < CONVERT(DATE,'2016-01-01')
AND TradeDate IS NOT NULL);
GO
再執行移動數據,數據又重新移回到分區1中