SQL Server 表分區之水平表分區
什么是表分區?
表分區分為水平表分區和垂直表分區,水平表分區就是將一個具有大量數據的表,進行拆分為具有相同表結構的若干個表;而垂直表分區就是把一個擁有多個字段的表,根據需要進行拆分列,然后根據某一個字段進行關聯。
表分區分為以下五個步驟:
1、創建文件組
2、創建數據文件
3、創建分區函數
4、創建分區方案
5、創建分區表
水平表分區
創建文件組:
語法:
-- 創建文件組語法 alter database <數據庫名> add filegroup <文件組名>
alter database Test add filegroup GroupOne alter database Test add filegroup GroupTwo alter database Test add filegroup GroupThree alter database Test add filegroup GroupFour alter database Test add filegroup GroupFive
創建數據文件到指定文件組:
語法:
-- 創建數據文件到指定文件組語法 alter database <數據庫名稱> add file <文件屬性> to filegroup <文件組名稱> <文件屬性> ( name=文件的邏輯名稱, filename=文件的物理名稱, size=文件初始大小, filegrowth=文件自動增長量(數值或百分比), maxsize=文件增長的最大值 )
alter database Test add file ( name=N'OneFile', filename=N'D:\DataDB\OneFile.mdf', size=3MB, filegrowth=10%, maxsize=unlimited -- 無限大小 ) to filegroup GroupOne alter database Test add file ( name=N'TwoFile', filename=N'D:\DataDB\TwoFile.mdf', size=3MB, filegrowth=10%, maxsize=unlimited -- 無限大小 ) to filegroup GroupTwo alter database Test add file ( name=N'ThreeFile', filename=N'D:\DataDB\ThreeFile.mdf', size=3MB, filegrowth=10%, maxsize=unlimited -- 無限大小 ) to filegroup GroupThree alter database Test add file ( name=N'FourFile', filename=N'D:\DataDB\FourFile.mdf', size=3MB, filegrowth=10%, maxsize=unlimited -- 無限大小 ) to filegroup GroupFour alter database Test add file ( name=N'FiveFile', filename=N'D:\DataDB\FiveFile.mdf', size=3MB, filegrowth=10%, maxsize=unlimited -- 無限大小 ) to filegroup GroupFive
創建分區函數:
創建一個分區函數,創建分區函數的目的是告訴 SQL Server 以什么方式對分區表進行分區。
語法:
create partition function -- 創建分區函數 Part_Fun(int) -- 分區函數名(分區列類型) as range [left/right] -- 左置/右置,即邊界值的存儲位置,如果設置為右置,邊界值存到下一個表 for values ('1000','2000','3000','4000','5000') -- 設置每個分區表的邊界值
create partition function Part_Fun(int) as range right for values ('1000','2000','3000','4000','5000')
刪除分區函數:
--刪除分區函數語法 drop partition function <分區函數名> --刪除名為 Part_Fun 的分區函數 drop partition function Part_Fun
PS:只有當分區函數沒有應用到分區方案中時,指定的分區函數才能被刪除。
創建分區方案:
分區方案的作用是將分區函數生成的分區映射到文件組中去。分區函數的作用是告訴SQL Server,如何將數據進行分區,而分區方案的作用則是告訴 SQL Server 將已分區的數據放在哪個文件組中。
語法:
--創建分區方案語法 create partition scheme -- 創建分區方案 <分區方案名稱> -- 分區方案名稱 as partition <分區函數名稱> -- 指定分區函數名稱 to (文件組名稱,,,,) -- 指定分區函數划分出來的數據對應存放的文件組
create partition scheme -- 創建分區方案 Part_Plan -- 分區方案名稱 as partition Part_Fun -- 分區函數名稱 to ('GroupOne','GroupTwo','GroupThree','GroupFour','GroupFive') -- 分區文件組
一執行,結果報錯了。
不對呀,我明明建立的是五個分區文件組,分區函數也是分為五份的啊。其實這里的意思應該就是后續數據的問題了,首先不可能保證這個表永遠就 5000 條數據的,所以他在這里的意思就是后續數據存儲的文件組。這里我把后續數據放在最后一個文件組里面。
create partition scheme -- 創建分區方案 Part_Plan -- 分區方案名稱 as partition Part_Fun -- 分區函數名稱 to ('GroupOne','GroupTwo','GroupThree','GroupFour','GroupFive','GroupFive') -- 分區文件組
刪除分區方案:
--刪除分區方案語法 drop partition scheme<分區方案名稱> --刪除名為 Part_Plan 的分區方案 drop partition scheme Part_Plan
PS:當沒有分區表引用該分區方案時,才能對其進行刪除。
創建分區表:
語法:
--創建分區表語法 create table <表名> -- 表名稱 ( column1 int not null primary key nonclustered, -- 字段名稱、字段類型、是否可空、主鍵約束、非聚集索引 column2 int not null ) on <分區方案名>(分區列名) -- 分區方案的名稱(指定要依據分區的列名)
create table US_Info ( ID int not null primary key identity(1,1), Name nvarchar(32) null, CreateTime nvarchar(32) null )on Part_Plan(ID)
PS:如果在表中創建有主鍵、唯一索引、聚集索引,則分區依據列必須為該列之一。即分區依據列必須建立在主鍵、唯一索引、聚集索引之上。
創建分區索引:
語法:
--創建分區索引語法 create [ unique [ clustered | nonclustered ] ] -- unique 唯一 clustered 聚集 nonclustered 非聚集 index <索引名稱> -- 指定索引名稱 on <表名>(列名) -- 指定表名(指定列名) on <分區方案名>(分區依據列名) -- 分區方案名稱(分區依據列名)
create nonclustered index Part_Non_Name on US_Info(Name) on Part_Plan(ID)
在表 US_Info 中插入5000條數據:
declare @I int set @I=1 while(@I<=5000) begin insert into US_Info(Name,CreateTime) values('名稱'+convert(nvarchar,@I),Convert(nvarchar,GETDATE(),121)) set @I=@I+1 end select * from US_Info
查詢指定值位於數據表哪個分區中:
-- 查詢指定值位於數據表哪個分區中 select $partition.Part_Fun('3050') -- 返回 4,表示位於第四個分區中
查詢分區表中,每個分區存在的數據的行數:
--查看分區表中,每個分區存在的數據的行數 select $partition.Part_Fun(ID) as Part_Num,count(1) as R_Count from US_Info group by $partition.Part_Fun(ID)
查詢指定分區中的數據:
-- 查詢指定分區中的數據 select * from US_Info where $partition.Part_Fun(ID)=3
拆分分區:
在分區函數中新增一個邊界值,即可將 1 個分區拆分為 2 個。
--將第 3 個分區拆分為 2 個分區 alter partition function Part_Fun() split range(N'2500')
一執行,報錯了,拆分不了,因為前面我們已經用分區函數指定了分區和文件組,那就要先添加一個文件組。
為分區方案指定下一個文件組:
-- 添加一個文件組 GroupSix alter database Test add filegroup GroupSix -- 添加一個數據文件 alter database Test add file ( name=N'SixFile', filename=N'D:\DataDB\SixFile.mdf', size=3MB, filegrowth=10%, maxsize=unlimited -- 無限大小 ) to filegroup GroupSix -- 為分區方案指定下一個文件組 alter partition scheme Part_Plan -- 分區方案名稱 next used GroupSix -- 下一個文件組名稱
然后再來對分區進行拆分:
--將第 3 個分區拆分為 2 個分區 alter partition function Part_Fun() -- 分區函數 split range -- 分割界限 (N'2500') -- 分區界限值
合並分區:
與拆分分區相反,去除一個邊界值即可。
-- 將第 3 個分區與第 4 個分區合並 alter partition function Part_Fun() -- 分區函數 merge range -- 合並界限 (N'2500') -- 合並界限值
復制分區表中的數據到普通表:
復制分區表中的數據到普通表需要滿足以下條件:
數據表的結構必須相同,即字段數量、字段類型等,字段與字段之間必須對應。
兩個表必須位於同一文件組,所以創建普通表的時候就需要指定文件組。
create table US_Info_back -- 創建普通表的表名 ( ID int not null primary key identity(1,1), -- 列定義 Name nvarchar(32) null, CreateTime nvarchar(32) null )on GroupThree -- 指定文件組
將分區表中的數據復制到普通表:
-- 將分區表 US_Info 中的第 3 個分區的數據復制到普通表 US_Info_back 中 alter table US_Info switch partition 3 to US_Info_back select * from US_Info_back
將普通表中的數據復制到分區表:
--將普通表 US_Info_back 中的數據復制到分區表 US_Info 中的第 6 個分區 alter table US_Info_back -- 普通表名 switch to US_Info -- 分區表名 partition 6 -- 指定分區
PS:將普通表中的數據復制到分區表時,需要先刪除分區表的索引。
將普通表轉換為分區表:
當數據庫已經存在數據的時候,就不能像上面那樣直接建立分區表了,只能將普通表轉換為分區表,只需在該普通表上創建一個聚集索引,並在該聚集索引中使用分區方案即可。
如果是已經存在的聚集索引,那么需要刪除然后重新建立,並使用分區方案。
現在我有一個現成的表 UserInfo,因為它存在一個主鍵,而建立主鍵時,系統會自動為主鍵列添加聚集索引,因為這個聚集索引沒法刪除,所以我現在要先刪除這個主鍵,然后重新建立一個主鍵,並設置為非聚集索引,然后為主鍵創建一個聚集索引(會覆蓋非聚集索引),並使用分區方案指定分區列即可。
-- 根據 指定表名 查詢 表的約束 exec sp_helpconstraint UserInfo -- UserInfo 表名 -- 根據指定主鍵約束名刪除指定表的主鍵約束 alter table UserInfo drop constraint PK__UserInfo__5A2040BBA6D6767A -- 添加主鍵約束,但設置為非聚集索引 alter table UserInfo add constraint PK__UserInfo__5A2040BBA6D6767A primary key nonclustered (U_Id) -- 添加一個聚集索引,並使用分區方案指定分區的列 create clustered index CLU_StuNo -- 索引名稱 on UserInfo(U_Id) -- 指定添加索引的表(添加索引的列) on Part_Plan(U_Id) -- 分區方案名稱(分區依據的列)
為這個表也插入5000條數據,看看效果:
declare @I int select @I=U_Id from UserInfo order by U_Id desc while(@I<=5000) begin insert into UserInfo(U_No,U_Name,U_Pwd) values('demo'+convert(nvarchar,@I),'demo'+convert(nvarchar,@I),'40D1C69C7B86064EA140C13CE8ED0E15') set @I=@I+1 end select * from UserInfo go
查看分區表中,每個分區存在的數據的行數:
--查看分區表中,每個分區存在的數據的行數 select $partition.Part_Fun(U_Id) as Part_Num,count(1) as R_Count from UserInfo group by $partition.Part_Fun(U_Id) order by Part_Num
查看數據庫分區信息 SQL(復制來的):
SELECT OBJECT_NAME(p.object_id) AS ObjectName, i.name AS IndexName, p.index_id AS IndexID, ds.name AS PartitionScheme, p.partition_number AS PartitionNumber, fg.name AS FileGroupName, prv_left.value AS LowerBoundaryValue, prv_right.value AS UpperBoundaryValue, CASE pf.boundary_value_on_right WHEN 1 THEN 'RIGHT' ELSE 'LEFT' END AS Range, p.rows AS Rows FROM sys.partitions AS p JOIN sys.indexes AS i ON i.object_id = p.object_id AND i.index_id = p.index_id JOIN sys.data_spaces AS ds ON ds.data_space_id = i.data_space_id JOIN sys.partition_schemes AS ps ON ps.data_space_id = ds.data_space_id JOIN sys.partition_functions AS pf ON pf.function_id = ps.function_id JOIN sys.destination_data_spaces AS dds2 ON dds2.partition_scheme_id = ps.data_space_id AND dds2.destination_id = p.partition_number JOIN sys.filegroups AS fg ON fg.data_space_id = dds2.data_space_id LEFT JOIN sys.partition_range_values AS prv_left ON ps.function_id = prv_left.function_id AND prv_left.boundary_id = p.partition_number - 1 LEFT JOIN sys.partition_range_values AS prv_right ON ps.function_id = prv_right.function_id AND prv_right.boundary_id = p.partition_number WHERE OBJECTPROPERTY(p.object_id, 'ISMSShipped') = 0 UNION ALL SELECT OBJECT_NAME(p.object_id) AS ObjectName, i.name AS IndexName, p.index_id AS IndexID, NULL AS PartitionScheme, p.partition_number AS PartitionNumber, fg.name AS FileGroupName, NULL AS LowerBoundaryValue, NULL AS UpperBoundaryValue, NULL AS Boundary, p.rows AS Rows FROM sys.partitions AS p JOIN sys.indexes AS i ON i.object_id = p.object_id AND i.index_id = p.index_id JOIN sys.data_spaces AS ds ON ds.data_space_id = i.data_space_id JOIN sys.filegroups AS fg ON fg.data_space_id = i.data_space_id WHERE OBJECTPROPERTY(p.object_id, 'ISMSShipped') = 0 ORDER BY ObjectName,IndexID,PartitionNumber
參考:
http://www.cnblogs.com/knowledgesea/p/3696912.html
http://blog.csdn.net/lgb934/article/details/8662956