ITPub博客

首页 > 数据库 > SQL Server > SQL SERVER之分区表

SQL SERVER之分区表

原创 SQL Server 作者:lhrbest 时间:2019-04-17 15:00:15 0 删除 编辑



https://docs.microsoft.com/zh-cn/sql/relational-databases/partitions/modify-a-partition-function?view=sql-server-2014


一.1  确定表是否分区
如果表 PartitionTable 已分区,以下查询将返回一个或多个行。 如果表未分区,则不返回任何行。
SELECT *   
FROM sys.tables AS t   
JOIN sys.indexes AS i   
    ON t.[object_id] = i.[object_id]   
    AND i.[type] IN (0,1)   
JOIN sys.partition_schemes ps   
    ON i.data_space_id = ps.data_space_id   
WHERE t.name = 'PartitionTable';   
或:
SELECT ps.*
  FROM sys.indexes i
  JOIN sys.partition_schemes ps
    ON i.data_space_id = ps.data_space_id
 WHERE i.object_id = object_id('PartitionTable') 
 
一.2  确定已分区表的边界值
以下查询对于 PartitionTable 表中的每个分区返回边界值。
SELECT t.name AS TableName, i.name AS IndexName, p.partition_number, p.partition_id, i.data_space_id, f.function_id, f.type_desc, r.boundary_id, r.value AS BoundaryValue   
FROM sys.tables AS t  
JOIN sys.indexes AS i  
    ON t.object_id = i.object_id  
JOIN sys.partitions AS p  
    ON i.object_id = p.object_id AND i.index_id = p.index_id   
JOIN  sys.partition_schemes AS s   
    ON i.data_space_id = s.data_space_id  
JOIN sys.partition_functions AS f   
    ON s.function_id = f.function_id  
LEFT JOIN sys.partition_range_values AS r   
    ON f.function_id = r.function_id and r.boundary_id = p.partition_number  
WHERE t.name = 'PartitionTable' AND i.type <= 1  
ORDER BY p.partition_number;  
一.3  确定已分区表的分区列
以下查询返回表的分区列的名称。 PartitionTable中创建已分区表或索引。
SELECT   
    t.[object_id] AS ObjectID   
    , t.name AS TableName   
    , ic.column_id AS PartitioningColumnID   
    , c.name AS PartitioningColumnName   
FROM sys.tables AS t   
JOIN sys.indexes AS i   
    ON t.[object_id] = i.[object_id]   
    AND i.[type] <= 1 -- clustered index or a heap   
JOIN sys.partition_schemes AS ps   
    ON ps.data_space_id = i.data_space_id   
JOIN sys.index_columns AS ic   
    ON ic.[object_id] = i.[object_id]   
    AND ic.index_id = i.index_id   
    AND ic.partition_ordinal >= 1 -- because 0 = non-partitioning column   
JOIN sys.columns AS c   
    ON t.[object_id] = c.[object_id]   
    AND ic.column_id = c.column_id   
WHERE t.name = 'PartitionTable' ;   
 
一.4  其他
 
select * from sys.filegroups; -- 文件组
SELECT * FROM sys.partition_functions; -- 分区函数
SELECT * FROM sys.partition_schemes; -- 分区方案
select $partition.Function_DateTime('2012-02-01');
select $partition.Function_DateTime('2011-01-01');
 
一.5  所有分区表
 
 
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;
 
 
一.6  将一个普通表转换成一个分区表
如何将一个普通表转换成一个分区表呢?说到底,只要将该表创建一个聚集索引,并在聚集索引上使用分区方案即可。
 
1、删除普通表中的聚集索引
--删掉主键  
ALTER TABLE testTab DROP constraint PK_testTab  
--创建主键,但不设为聚集索引  
ALTER TABLE testTab ADD CONSTRAINT PK_testTab  PRIMARY KEY NONCLUSTERED  
(  
    [ID] ASC  
) ON [PRIMARY] 
2、创建一个分区函数
CREATE PARTITION FUNCTION part_month_func_range_test(datetime)  
AS RANGE RIGHT FOR VALUES (
'2017-7-1 00:00:00',
'2017-8-1 00:00:00',
'2017-8-1 00:00:00',
'2017-9-1 00:00:00',
'2017-10-1 00:00:00',
'2017-11-1 00:00:00',
);
3、创建一个分区方案
CREATE PARTITION SCHEME partschSale  
AS PARTITION part_month_func_range_test
TO (  
  FC201706,  
  FC201707,  
  FC201708, 
  FC201709,  
  FC201710,
  FC201711,
  FC201712
) ;
4、按分区方案创建聚集索引
 
--创建一个新的聚集索引,在该聚集索引中使用分区方案  
CREATE CLUSTERED INDEX CT_testTab ON Sale([inDate])  
ON part_month_func_range_test([inDate]);  
 
为表创建了一个使用分区方案的聚集索引之后,该普通表就变成了一个分区表了。


1. 分区表简介

  分区表在逻辑上是一个表,而物理上是多个表。从用户角度来看,分区表和普通表是一样的。使用分区表的主要目的是为改善大型表以及具有多个访问模式的表的可伸缩性和可管理性。

  分区表是把数据按设定的标准划分成区域存储在不同的文件组中,使用分区可以快速而有效管理和访问数据子集。

1.1> 适合做分区表的情况

  ◊ 数据库中某个表的数据很多,在查询数据时会明显感觉到速度很慢,这个时候需要考虑分区表;

  ◊ 数据是分段的,如以年份为分隔的数据,对于当年的数据经常进行增删改查操作,而对于往年的数据几乎不做操作或只做查询操作,这种情况可以使用分区表。对数据的操作如果只涉及一部分数据而不是全部数据的情况可以考虑分区表,如果一张表的数据经常使用且不管年份之类的因素经常对其增删改查操作则最好不要分区。

1.2> 分区表的优点

  ◊ 分区表可以从物理上将一个大表分成几个小表,但是从逻辑上来看还是一个大表。

  ◊ 对于具有多个CPU的系统,分区可以对表的操作通过并行的方式进行,可以提升访问性能。


SQL Server   支持表和索引分区。   已分区表和已分区索引的数据划分为分布于一个数据库中多个文件组的单元。   数据是按水平方式分区的,因此多组行映射到单个的分区。   单个索引或表的所有分区都必须位于同一个数据库中。   对数据进行查询或更新时,表或索引将被视为单个逻辑实体。   在  Microsoft SQL Server  的各版本中均不提供已分区的表和索引。   有关的各版本支持的功能列表 SQL Server ,请参阅 SQL Server 2014 各个版本支持的功能

 重要

SQL Server 2014   在默认情况下支持多达 15,000 个分区。   在  SQL Server 2012 之前的版本中,分区数默认限制为 1000。在基于 x86 的系统上,可以创建分区数超过 1000 的表或索引,但不受支持。

分区的优点

通过对大型表或索引进行分区,可以具有以下可管理性和性能优点。

  • 可以快速、高效地传输或访问数据的子集,同时又能维护数据收集的完整性。   例如,将数据从 OLTP 加载到 OLAP 系统之类的操作仅需几秒钟即可完成,而如果不对数据进行分区,执行此操作需要几分钟或几小时。

  • 您可以更快地对一个或多个分区执行维护操作。   这些操作的效率更高,因为它们仅针对这些数据子集,而非整个表。 例如,您可以选择在一个或多个分区中压缩数据,或者重新生成索引的一个或多个分区。

  • 您可以根据经常执行的查询类型和硬件配置,提高查询性能。   例如,在两个或更多的已分区表中的分区列相同时,查询优化器可以更快地处理这些表之间的同等联接查询,因为可以联接这些分区本身。

    当  SQL Server  针对 I/O 操作执行数据排序时,它会首先按分区对数据进行排序。   SQL Server   每次访问一个驱动器,这样可能会降低性能。   为了提高数据排序性能,可以通过设置 RAID 将多个磁盘中的分区数据文件条带化。   这样一来,尽管  SQL Server  仍按分区对数据进行排序,但它可以同时访问每个分区的所有驱动器。

    此外,您可以通过对在分区级别而不是整个表启用锁升级来提高性能。   这可以减少表上的锁争用。

组件和概念

以下术语适用于表和索引分区。

分区函数
一种数据库对象,它定义如何根据某个列(称为分区列)的值将表或索引的行映射到一组分区。   也就是说,分区函数定义表将具有的分区数和分区边界的定义方式。   例如,假定一个包含销售订单数据的表,您可能需要基于  datetime  列(如销售日期)将表划分为 12 个(按月)分区。

分区方案
将分区函数的分区映射到一组文件组的数据库对象。   在各个文件组上放置分区的主要原因是为了确保可以在分区上独立执行备份操作。   这是因为您可以在各个文件组上执行备份。

分区列
分区函数对表或索引进行分区时所使用的表或索引列。   参与分区函数的计算列必须显式标记为 PERSISTED。   用作索引列时有效的所有数据类型都可以用作分区依据列, timestamp  除外。   无法指定  ntext text image xml varchar(max) nvarchar(max)  或  varbinary(max)  数据类型。   此外,无法指定 Microsoft .NET Framework 公共语言运行时 (CLR) 用户定义类型和别名数据类型列。

对齐的索引
与其对应的表建立在同一个分区方案之上的一种索引。   如果表与其索引对齐,SQL Server 则可以快速高效地切换分区,同时又能维护表及其索引的分区结构。   索引要与其基表对齐,并不需要与基表参与相同的命名分区函数。   但是,索引和基表的分区函数在实质上必须相同,即:1) 分区函数的参数具有相同的数据类型;2) 分区函数定义了相同数目的分区;3) 分区函数为分区定义了相同的边界值。

非对齐的索引
独立于其相应的表进行分区的一种索引。   也就是说,索引具有不同的分区方案或者放置于不同于基表的单独文件组中。   在下列情况下,设计非对齐的分区索引可能会很有用:

  • 基表未分区。

  • 索引键是唯一的,不包含表的分区依据列。

  • 您希望基表与使用不同联接列的多个表一起参与并置联接。

分区排除
查询优化器用来仅访问相关分区以便满足查询的筛选条件的过程。

性能准则

这个新的、更高的 15,000 个分区的限制将影响内存、分区的索引操作、DBCC 命令和查询。   本节介绍将分区数目增加到超过 1,000 个的性能影响并根据需要提供解决方法。   由于对分区最大数目的限制已增加到 15,000 个,因此您可以存储更长时间的数据。   不过,您应该仅保留所需时长的数据,并且在性能和分区数目之间保持平衡。

内存使用情况和指导方针

如果正在使用大量分区,我们建议您使用至少 16 GB 的 RAM。   如果系统没有足够的内存,则数据操作语言 (DML) 语句、数据定义语言 (DDL) 语句和其他操作可能会由于内存不足而失败。   如果系统具有 16 GB 的 RAM 并且运行许多大量占用内存的进程,则在运行大量分区的操作时,可能会出现内存不足的情况。   因此,您具有超过 16 GB 的内存越多,您遇到性能和内存问题的可能性就越低。

内存限制可能会影响 SQL Server 生成已分区索引的性能或能力。   如果表中已应用聚集索引,当索引未与其基表或聚集索引对齐时更是如此。

已分区索引操作

内存限制可能会影响 SQL Server 生成已分区索引的性能或能力。   具有非对齐索引的情况尤其是这样。   对超过 1,000 个分区的表创建和重新生成非对齐索引是可能的,但不支持。   这样做可能会导致性能下降,或在执行这些操作的过程中占用过多内存。

随着分区数目的增加,创建和重新生成对齐索引的执行时间可能会更长。   我们建议您不要同时运行多个创建和重新生成索引命令,因为可能会遇到性能和内存问题。

当 SQL Server 执行排序以生成已分区索引时,它首先为每个分区生成一个排序表。   然后在每个分区各自的文件组中生成排序表,或者在  tempdb  中生成排序表(如果指定了 SORT_IN_TEMPDB 索引选项)。   每个排序表都需要一个最小内存量才能生成。   在生成与其基表对齐的已分区索引时,将一次生成一个排序表,因此使用的内存较少。   但是,在生成非对齐的已分区索引时,将同时生成排序表。   因此,必须有足够的内存来处理这些并发的排序。   分区数越多,所需的内存越多。   每个分区的每个排序表的最小大小为 40 页,每页 8 KB。   例如,具有 100 个分区的非对齐已分区索引需要足够的内存才能同时连续地对 4,000 (40 * 100) 页进行排序。   如果有这么多的可用内存,生成操作将成功,但性能可能会降低。   如果没有这么多可用内存,生成操作将失败。   而具有 100 个分区的对齐已分区索引只需要具有对 40 页进行排序的内存就足够了,因为不会同时执行排序。

无论是对齐索引还是非对齐索引,如果 SQL Server 对多处理器计算机上的生成操作应用了并行度,需要的内存可能会更多。   这是因为并行度越高,需要的内存就越多。   例如,如果 SQL Server 将并行度设置为 4,那么具有 100 个分区的非对齐已分区索引将需要使四个处理器同时分别对 4,000 页(即,共 16,000 页)进行排序的足够内存。   如果已分区索引是对齐的,需要的内存将减少,只要够四个处理器分别对 40 页(共 160 页,即 4 * 40)进行排序就行了。   您可以使用 MAXDOP 索引选项手动降低并行度。

DBCC 命令

在具有较多分区的情况下,随着分区数目的增加,DBCC 命令可能需要更长的时间来执行。

查询

与具有大量分区的查询相比,使用分区排除的查询在性能上相当或更高。   随着分区数目的增加,未使用分区排除的查询可能需要更长的时间来执行。

例如,假定一个表具有 1 亿行和列的  A 、  B 和  C   在方案 1 中,该表在列  A 上划分为 1000 个分区。   在方案 2 中,该表在列  A 上划分为 10,000 个分区。   针对该表的一个查询(该查询对列  A  具有 WHERE 子句筛选)将执行分区排除并且扫描一个分区。   同一个查询在方案 2 中的运行速度可能会更快,因为在分区中要扫描的行数更少。   对列 B 具有 WHERE 子句筛选的查询将扫描所有分区。   与在方案 2 中相比,该查询在方案 1 中的运行速度会更快,因为要扫描更少的分区。

在分区列之外的其他列上使用运算符(如 TOP 或 MAX/MIN)的查询可能会遇到分区性能降低的情况,因为所有分区都必须进行评估。

已分区索引操作期间统计信息计算中的行为更改

从  SQL Server 2012 开始,当创建或重新生成已分区索引时,将通过扫描表中的所有行来创建统计信息。   相反,查询优化器使用默认采样算法来生成统计信息。   在升级具有已分区索引的数据库后,您可以在直方图数据中注意到针对这些索引的差异。   此行为更改可能不会影响查询性能。   若要通过扫描表中所有行的方法获得有关已分区索引的统计信息,请使用 CREATE STATISTICS 或 UPDATE STATISTICS 以及 FULLSCAN 子句。


什么是表分区

一般情况下,我们建立数据库表时,表数据都存放在一个文件里。

但是如果是分区表的话,表数据就会按照你指定的规则分放到不同的文件里,把一个大的数据文件拆分为多个小文件,还可以把这些小文件放在不同的磁盘下由多个cpu进行处理。这样文件的大小随着拆分而减小,还得到硬件系统的加强,自然对我们操作数据是大大有利的。

所以大数据量的数据表,对分区的需要还是必要的,因为它可以提高select效率,还可以对历史数据经行区分存档等。但是数据量少的数据就不要凑这个热闹啦,因为表分区会对数据库产生不必要的开销,除啦性能还会增加实现对象的管理费用和复杂性。


确定表是否分区

  1. 如果表  PartitionTable  已分区,以下查询将返回一个或多个行。   如果表未分区,则不返回任何行。


    SELECT *   
    FROM sys.tables AS t   
    JOIN sys.indexes AS i   
        ON t.[object_id] = i.[object_id]   
        AND i.[type] IN (0,1)   
    JOIN sys.partition_schemes ps   
        ON i.data_space_id = ps.data_space_id   
    WHERE t.name = 'PartitionTable';   
    GO
    

确定已分区表的边界值

  1. 以下查询对于  PartitionTable  表中的每个分区返回边界值。


    SELECT t.name AS TableName, i.name AS IndexName, p.partition_number, p.partition_id, i.data_space_id, f.function_id, f.type_desc, r.boundary_id, r.value AS BoundaryValue   
    FROM sys.tables AS t  
    JOIN sys.indexes AS i  
        ON t.object_id = i.object_id  
    JOIN sys.partitions AS p  
        ON i.object_id = p.object_id AND i.index_id = p.index_id   
    JOIN  sys.partition_schemes AS s   
        ON i.data_space_id = s.data_space_id  
    JOIN sys.partition_functions AS f   
        ON s.function_id = f.function_id  
    LEFT JOIN sys.partition_range_values AS r   
        ON f.function_id = r.function_id and r.boundary_id = p.partition_number  
    WHERE t.name = 'PartitionTable' AND i.type <= 1  
    ORDER BY p.partition_number;
    

确定已分区表的分区列

  1. 以下查询返回表的分区列的名称。   PartitionTable 中创建已分区表或索引。


    SELECT   
        t.[object_id] AS ObjectID   
        , t.name AS TableName   
        , ic.column_id AS PartitioningColumnID   
        , c.name AS PartitioningColumnName   
    FROM sys.tables AS t   
    JOIN sys.indexes AS i   
        ON t.[object_id] = i.[object_id]   
        AND i.[type] <= 1 -- clustered index or a heap   
    JOIN sys.partition_schemes AS ps   
        ON ps.data_space_id = i.data_space_id   
    JOIN sys.index_columns AS ic   
        ON ic.[object_id] = i.[object_id]   
        AND ic.index_id = i.index_id   
        AND ic.partition_ordinal >= 1 -- because 0 = non-partitioning column   
    JOIN sys.columns AS c   
        ON t.[object_id] = c.[object_id]   
        AND ic.column_id = c.column_id   
    WHERE t.name = 'PartitionTable' ;   
    GO
    

有关详细信息,请参阅:



2. 创建分区表步骤

  创建分区表的步骤分为5步:

  (1)创建数据库文件组

  (2)创建数据库文件

  (3)创建分区函数

  (4)创建分区方案

  (5)创建分区表

2.1> 创建数据库文件组

  新建示例数据库Northwind,创建数据库文件组和文件,添加文件组。

  

2.2> 创建数据库文件

  创建数据文件,并为数据文件分配文件组。

  

  完成创建后的数据库文件信息

  

  通过SQL Server Profiler可以看到具体的创建数据库的脚本如下:

CREATE DATABASE [Northwind]
 CONTAINMENT = NONE ON  PRIMARY ( NAME = N'Northwind', FILENAME = N'F:\Database\Northwind\Northwind.mdf' , SIZE = 5120KB , FILEGROWTH = 1024KB ), 
 FILEGROUP [SECTION2010] ( NAME = N'Northwind_Data_2010', FILENAME = N'F:\Database\Northwind\Northwind_Data_2010.ndf' , SIZE = 5120KB , FILEGROWTH = 1024KB ), 
 FILEGROUP [SECTION2011] ( NAME = N'Northwind_Data_2011', FILENAME = N'F:\Database\Northwind\Northwind_Data_2011.ndf' , SIZE = 5120KB , FILEGROWTH = 1024KB ), 
 FILEGROUP [SECTION2012] ( NAME = N'Northwind_Data_2012', FILENAME = N'F:\Database\Northwind\Northwind_Data_2012.ndf' , SIZE = 5120KB , FILEGROWTH = 1024KB ), 
 FILEGROUP [SECTION2013] ( NAME = N'Northwind_Data_2013', FILENAME = N'F:\Database\Northwind\Northwind_Data_2013.ndf' , SIZE = 5120KB , FILEGROWTH = 1024KB ), 
 FILEGROUP [SECTION2014] ( NAME = N'Northwind_Data_2014', FILENAME = N'F:\Database\Northwind\Northwind_Data_2014.ndf' , SIZE = 5120KB , FILEGROWTH = 1024KB ) LOG ON ( NAME = N'Northwind_log', FILENAME = N'F:\Database\Northwind\Northwind_log.ldf' , SIZE = 2048KB , FILEGROWTH = 10%)

  查看数据库文件组SQL语句:

2.3> 创建分区函数

  创建分区函数Transact-SQL语法:

CREATE PARTITION FUNCTION partition_function_name ( input_parameter_type )AS RANGE [ LEFT | RIGHT ] FOR VALUES ( [ boundary_value [ ,...n ] ] ) 
[ ; ]

  参数:  

  partition_function_name:分区函数的名称。 分区函数名称在数据库内必须唯一,并且符合标识符的规则。

  input_parameter_type:用于分区的列的数据类型。 当用作分区列时,除 text、ntext、image、xml、timestamp、varchar(max)、nvarchar(max)、varbinary(max)、别名数据类型或 CLR 用户定义数据类型外,所有数据类型均有效。

  boundary_value:为使用 partition_function_name 的已分区表或索引的每个分区指定边界值。 如果 boundary_value 为空,则分区函数使 partition_function_name 将整个表或索引映射到单个分区。 只能使用 CREATE TABLE 或 CREATE INDEX 语句中指定的一个分区列。

  LEFT | RIGHT 指定当间隔值由 数据库引擎 按升序从左到右排序时,boundary_value [ ,...n ] 属于每个边界值间隔的哪一侧(左侧还是右侧)。 如果未指定,则默认值为 LEFT。

  示例:创建将用于Order表的分区函数

CREATE PARTITION FUNCTION Function_DateTime ( DATETIME )AS RANGE RIGHTFOR VALUES('2011-01-01', '2012-01-01', '2013-01-01', '2014-01-01')

  完成创建分区函数之后,可以通过以下SQL语句查看已创建的分区函数情况。

SELECT * FROM sys.partition_functions

2.4> 创建分区方案

  分区方案的作用是将分区函数生成的分区映射到文件组中去,分区方案是让SQL Server将已分区的数据放在哪个文件组中。

  在当前数据库中创建一个将已分区表或已分区索引的分区映射到文件组的方案。 已分区表或已分区索引的分区的个数和域在分区函数中确定。 必须首先在 CREATE PARTITION FUNCTION 语句中创建分区函数,然后才能创建分区方案。

  创建分区方案的Transact-SQL语法:

CREATE PARTITION SCHEME partition_scheme_nameAS PARTITION partition_function_name[ ALL ] TO ( { file_group_name | [ PRIMARY ] } [ ,...n ] )[ ; ]

  参数:

   partition_scheme_name :分区方案的名称。 分区方案名称在数据库中必须是唯一的,并且符合标识符规则。

   partition_function_name :使用分区方案的分区函数的名称。 分区函数所创建的分区将映射到在分区方案中指定的文件组。 partition_function_name 必须已经存在于数据库中。 单个分区不能同时包含 FILESTREAM 和非 FILESTREAM 文件组。

   ALL :指定所有分区都映射到在 file_group_name 中提供的文件组,或映射到主文件组(如果指定了 [PRIMARY]。 如果指定了 ALL,则只能指定一个 file_group_name。

   file_group_name | [ PRIMARY ] [ ,...n] :指定用来持有由 partition_function_name 指定的分区的文件组的名称。 file_group_name 必须已经存在于数据库中。
  如果指定了 [PRIMARY],则分区将存储于主文件组中。 如果指定了 ALL,则只能指定一个 file_group_name。 分区分配到文件组的顺序是从分区 1 开始,按文件组在 [,...n] 中列出的顺序进行分配。 在 [,...n] 中,可以多次指定同一个 file_group_name。 如果 n 不足以拥有在 partition_function_name 中指定的分区数,则 CREATE PARTITION SCHEME 将失败,并返回错误。
  如果 partition_function_name 生成的分区数少于文件组数,则第一个未分配的文件组将标记为 NEXT USED,并且出现显示命名 NEXT USED 文件组的信息。 如果指定了 ALL,则单独的 file_group_name 将为该 partition_function_name 保持它的 NEXT USED 属性。 如果在 ALTER PARTITION FUNCTION 语句中创建了一个分区,则 NEXT USED 文件组将再接收一个分区。 若要再创建一个未分配的文件组来拥有新的分区,请使用 ALTER PARTITION SCHEME。
  在 file_group_name[ 1,...n] 中指定主文件组时,必须像在 [PRIMARY] 中那样分隔 PRIMARY,因为它是关键字。

 

  示例:创建将用于Order表的分区方案

CREATE PARTITION SCHEME Scheme_DateTimeAS PARTITION Function_DateTimeTO ( SECTION2010, SECTION2011, SECTION2012, SECTION2013, SECTION2014 )

  分区函数和分区方案创建之后,可以在数据库的【存储】中查看:

  通过可以通过以下SQL语句查看已创建的分区方案:

SELECT * FROM sys.partition_schemes

2.5> 创建分区表

CREATE TABLE [Order](
    OrderID INT IDENTITY(1,1) NOT NULL,
    UserID INT NOT NULL,
    TotalAmount DECIMAL(18,2) NULL,
    OrderDate DATETIME NOT NULL) ON Scheme_DateTime ( OrderDate )

  这里需要注意分区表不能再创建聚集索引,因为聚集索引可以将记录在物理上顺序存储,而分区表是将数据存储在不同的表中,这两个概念是冲突的,所以在创建分区表时不能再创建聚集索引。

  完成Order表创建之后,查看表的属性,可以看到Order表已经是分区表。

3. 操作分区表

3.1> Insert数据

USE [Northwind]GOINSERT INTO [dbo].[Order] ([UserID],[TotalAmount] ,[OrderDate]) VALUES (1 ,10.00 ,'2009-10-20');INSERT INTO [dbo].[Order] ([UserID],[TotalAmount] ,[OrderDate]) VALUES (1 ,20.50 ,'2009-12-31');INSERT INTO [dbo].[Order] ([UserID],[TotalAmount] ,[OrderDate]) VALUES (2 ,40.00 ,'2010-01-20');INSERT INTO [dbo].[Order] ([UserID],[TotalAmount] ,[OrderDate]) VALUES (3 ,40.00 ,'2010-10-20');INSERT INTO [dbo].[Order] ([UserID],[TotalAmount] ,[OrderDate]) VALUES (4 ,50.00 ,'2011-10-20');INSERT INTO [dbo].[Order] ([UserID],[TotalAmount] ,[OrderDate]) VALUES (5 ,60.00 ,'2012-10-20');INSERT INTO [dbo].[Order] ([UserID],[TotalAmount] ,[OrderDate]) VALUES (5 ,60.00 ,'2012-10-20');INSERT INTO [dbo].[Order] ([UserID],[TotalAmount] ,[OrderDate]) VALUES (6 ,70.00 ,'2013-10-20');INSERT INTO [dbo].[Order] ([UserID],[TotalAmount] ,[OrderDate]) VALUES (10 ,90.00 ,'2014-10-20');INSERT INTO [dbo].[Order] ([UserID],[TotalAmount] ,[OrderDate]) VALUES (9 ,100.00 ,'2015-10-20');GO

3.2> 查询数据所在物理分区表

  在分区表中使用一般的SELECT语句无法知道数据是分别存放在哪几个不同的物理表中,若要知道数据分别存放的物理表,可以使用$PARTITION函数,该函数可以调用分区函数并返回数据所在物理分区的编号。

  $PARTITION的语法:$PARTITION.分区函数名(表达式)

SELECT $PARTITION.Function_DateTime('2010-01-01')

  查询结果分区函数返回为1,说明2010-01-01的数据会存放在第1个物理分区表中。

   使用$PARTITION函数可以具体知道每个物理分区表中存放了哪些记录。

  查看物理分区表中存放的记录:

SELECT * FROM [Order] WHERE $PARTITION.Function_DateTime(OrderDate) = 1

SELECT $PARTITION.Function_DateTime(OrderDate) AS 分区编号, COUNT(1) AS 记录数 
FROM [Order]GROUP BY $PARTITION.Function_DateTime(OrderDate)

3.3> 修改分区表数据

UPDATE dbo.[Order] SET OrderDate='2015-01-01' WHERE OrderID = 3

4. 将普通表转换为分区表

  一般的普通表都是在主键上建聚集索引,记录的物理保存位置由主键决定。

  示例:创建一个Product普通表

CREATE TABLE Product
(
    ProductID INT IDENTITY(1,1) NOT NULL,
    ProductName VARCHAR(100) NOT NULL,
    UnitPrice DECIMAL(18,2) NULL,
    CreateDate DATETIME NOT NULL,    CONSTRAINT PK_Product PRIMARY KEY CLUSTERED (ProductID)
)
USE [Northwind]GOINSERT INTO [dbo].[Product] ([ProductName],[UnitPrice],[CreateDate]) VALUES ('LINQ to SQL' ,10 ,'2012-01-01');INSERT INTO [dbo].[Product] ([ProductName],[UnitPrice],[CreateDate]) VALUES ('LINQ to XML' ,10 ,'2012-12-01');INSERT INTO [dbo].[Product] ([ProductName],[UnitPrice],[CreateDate]) VALUES ('LINQ to Object' ,10 ,'2013-02-01');INSERT INTO [dbo].[Product] ([ProductName],[UnitPrice],[CreateDate]) VALUES ('LINQ to ADO.NET' ,10 ,'2014-01-02');INSERT INTO [dbo].[Product] ([ProductName],[UnitPrice],[CreateDate]) VALUES ('LINQ to Entity' ,10 ,'2015-01-01');GO

  查看表Product的属性:

  查看表Product的索引,可以看到PK_Product为聚集索引。

  将普通表转换为分区表的操作是先在普通表上删除聚集索引,在创建一个新的聚集索引,在该聚集索引中使用分区方案。

  在SQL Server中,主键字段上默认创建聚集索引,删除主键的聚集索引。

ALTER TABLE Product DROP CONSTRAINT PK_Product

  重新创建主键非聚集索引

ALTER TABLE Product ADD CONSTRAINT PK_Product PRIMARY KEY NONCLUSTERED (ProductID ASC)

  重新创建后的主键:

  创建使用分区方案的聚集索引:

CREATE CLUSTERED INDEX IX_CreateDate ON Product ( CreateDate )ON Scheme_DateTime ( CreateDate )

  调整后的Product表属性:

  调整后Product表记录的物理保存情况:

5. 删除(合并)一个分区表

   删除2012-01-01的分区,修改分区函数:

ALTER PARTITION FUNCTION Function_DateTime() MERGE RANGE ('2012-01-01')

  在修改了分区函数之后,与之关联的分区方案也将同时自动调整。在执行了上面合并分区的函数之后,查看分区方案的Create脚本。

CREATE PARTITION SCHEME [Scheme_DateTime] AS PARTITION [Function_DateTime] TO ([SECTION2010], [SECTION2011], [SECTION2013], [SECTION2014])

  合并分区之后,被合并的分区记录也将被重新分配物理保存位置。

6. 添加分区

  分区方案中指定的文件组个数比分区函数中指定的边界数大1,为分区方案指定一个可用的文件组时,该分区方案并没有立刻使用这个文件组,只是将文件组先备用着,等修改了分区函数之后分区方案才会使用这个文件组。如果分区函数没有更改,分区方案中的文件组个数也不会更改。

   添加分区所需要使用到的文件组可以使用之前合并分区之后没有再使用的SECTION2012,也可以新建文件组。

ALTER DATABASE [Northwind] ADD FILEGROUP [SECTION2015]
ALTER DATABASE [Northwind] ADD FILE ( 
    NAME = N'Northwind_Data_2015', 
    FILENAME = N'F:\Database\Northwind\Northwind_Data_2015.ndf' , 
    SIZE = 5120KB , 
    FILEGROWTH = 1024KB 
) TO FILEGROUP [SECTION2015]

  为分区方案指定一个可用的文件组:

ALTER PARTITION SCHEME Scheme_DateTime NEXT USED [SECTION2015]

  修改分区函数,添加分区:

ALTER PARTITION FUNCTION Function_DateTime() SPLIT RANGE('2015-01-01')

  查看添加分区后的数据物理存储:




跟着做,分区如此简单

先跟着做一个分区表(分为11个分区),去除神秘的面纱,然后咱们再逐一击破各个要点要害。

分区是要把一个表数据拆分为若干子集合,也就是把把一个数据文件拆分到多个数据文件中,然而这些文件的存放可以依托一个文件组或这多个文件组,由于多个文件组可以提高数据库的访问并发量,还可以把不同的分区配置到不同的磁盘中提高效率,所以创建时建议分区跟文件组个数相同。

1.创建文件组

可以点击数据库属性在文件组里面添加

T-sql语法:

alter database <数据库名> add filegroup <文件组名>
---创建数据库文件组alter database testSplit add filegroup ByIdGroup1alter database testSplit add filegroup ByIdGroup2alter database testSplit add filegroup ByIdGroup3alter database testSplit add filegroup ByIdGroup4alter database testSplit add filegroup ByIdGroup5alter database testSplit add filegroup ByIdGroup6alter database testSplit add filegroup ByIdGroup7alter database testSplit add filegroup ByIdGroup8alter database testSplit add filegroup ByIdGroup9alter database testSplit add filegroup ByIdGroup10

2.创建数据文件到文件组里面

可以点击数据库属性在文件里面添加

T-sql语法:

alter database <数据库名称> add file <数据标识> to filegroup <文件组名称>--<数据标识> (name:文件名,fliename:物理路径文件名,size:文件初始大小kb/mb/gb/tb,filegrowth:文件自动增量kb/mb/gb/tb/%,maxsize:文件可以增加到的最大大小kb/mb/gb/tb/unlimited)
alter database testSplit add file (name=N'ById1',filename=N'J:\Work\数据库\data\ById1.ndf',size=5Mb,filegrowth=5mb)to filegroup ByIdGroup1alter database testSplit add file (name=N'ById2',filename=N'J:\Work\数据库\data\ById2.ndf',size=5Mb,filegrowth=5mb)to filegroup ByIdGroup2alter database testSplit add file (name=N'ById3',filename=N'J:\Work\数据库\data\ById3.ndf',size=5Mb,filegrowth=5mb)to filegroup ByIdGroup3alter database testSplit add file (name=N'ById4',filename=N'J:\Work\数据库\data\ById4.ndf',size=5Mb,filegrowth=5mb)to filegroup ByIdGroup4alter database testSplit add file (name=N'ById5',filename=N'J:\Work\数据库\data\ById5.ndf',size=5Mb,filegrowth=5mb)to filegroup ByIdGroup5alter database testSplit add file (name=N'ById6',filename=N'J:\Work\数据库\data\ById6.ndf',size=5Mb,filegrowth=5mb)to filegroup ByIdGroup6alter database testSplit add file (name=N'ById7',filename=N'J:\Work\数据库\data\ById7.ndf',size=5Mb,filegrowth=5mb)to filegroup ByIdGroup7alter database testSplit add file (name=N'ById8',filename=N'J:\Work\数据库\data\ById8.ndf',size=5Mb,filegrowth=5mb)to filegroup ByIdGroup8alter database testSplit add file (name=N'ById9',filename=N'J:\Work\数据库\data\ById9.ndf',size=5Mb,filegrowth=5mb)to filegroup ByIdGroup9alter database testSplit add file (name=N'ById10',filename=N'J:\Work\数据库\data\ById10.ndf',size=5Mb,filegrowth=5mb)to filegroup ByIdGroup10

执行完成后,右键数据库看文件组跟文件里面是不是多出来啦这些文件组跟文件。

3.使用向导创建分区表

右键到要分区的表--- >> 存储 --- >> 创建分区 --- >>显示向导视图 --- >> 下一步 --- >> 下一步。。

这里举例说下选择列的意思:

假如你选择的是int类型的列:那么你的分区可以指定为1--100W是一个分区,100W--200W是一个分区....

假如你选择的是datatime类型:那么你的分区可以指定为:2014-01-01--2014-01-31一个分区,2014-02-01--2014-02-28一个分区...

根据这样的列数据规则划分,那么在那个区间的数据,在插入数据库时就被指向那个分区存储下来。

 

我这里选用orderid int类型 --- >> 下一步 --- >>

左边界右边界:就是把临界值划分给上一个分区还是下一个分区。一个小于号,一个小于等于号。

然后下一步下一步最后你会得到分区函数和分区方案。

USE [testSplit]GOBEGIN TRANSACTION--创建分区函数
CREATE PARTITION FUNCTION [bgPartitionFun](int) AS RANGE LEFT FOR VALUES (N'1000000', N'2000000', N'3000000', N'4000000', N'5000000', N'6000000', N'7000000', N'8000000', N'9000000', N'10000000')--创建分区方案CREATE PARTITION SCHEME [bgPartitionSchema] AS PARTITION [bgPartitionFun] TO ([PRIMARY], [ByIdGroup1], [ByIdGroup2], [ByIdGroup3], [ByIdGroup4], [ByIdGroup5], [ByIdGroup6], [ByIdGroup7], [ByIdGroup8], [ByIdGroup9], [ByIdGroup10])--创建分区索引
CREATE CLUSTERED INDEX [ClusteredIndex_on_bgPartitionSchema_635342971076448165] ON [dbo].[BigOrder] (    [OrderId])WITH (SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF) ON [bgPartitionSchema]([OrderId])
--删除分区索引
DROP INDEX [ClusteredIndex_on_bgPartitionSchema_635342971076448165] ON [dbo].[BigOrder] WITH ( ONLINE = OFF )COMMIT TRANSACTION

执行上面向导生成的语句。分区完成。。

4.秀一下速度。

首先我在表中插入啦1千万行数据。给表分啦11个分区。前十个分区里面一个是100W条数据。。

说两句:

可见反常现象,扫描次数跟逻辑读取次数都是无分区表的2倍之多,但查询速度却是快啦不少啊。这就是分区的神奇之处啊,所以要相信这世界一切皆有可能。

分区函数,分区方案,分区表,分区索引

1.分区函数

指定分依据区列(依据列唯一),分区数据范围规则,分区数量,然后将数据映射到一组分区上。

创建语法:  

create partition function 分区函数名(<分区列类型>) as range [left/right] for values (每个分区的边界值,....)
--创建分区函数CREATE PARTITION FUNCTION [bgPartitionFun](int) AS RANGE LEFT FOR VALUES (N'1000000', N'2000000', N'3000000', N'4000000', N'5000000', N'6000000', N'7000000', N'8000000', N'9000000', N'10000000')

然而,分区函数只定义了分区的方法,此方法具体用在哪个表的那一列上,则需要在创建表或索引是指定。  

删除语法:

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

需要注意的是,只有没有应用到分区方案中的分区函数才能被删除。

2.分区方案

指定分区对应的文件组。

创建语法:  

--创建分区方案语法create partition scheme <分区方案名称> as partition <分区函数名称> [all]to (文件组名称,....)
--创建分区方案,所有分区在一个组里面CREATE PARTITION SCHEME [bgPartitionSchema] AS PARTITION [bgPartitionFun] TO ([ByIdGroup1], [ByIdGroup1], [ByIdGroup1], [ByIdGroup1], [ByIdGroup1], [ByIdGroup1], [ByIdGroup1], [ByIdGroup1], [ByIdGroup1], [ByIdGroup1], [ByIdGroup1])

分区函数必须关联分区方案才能有效,然而分区方案指定的文件组数量必须与分区数量一致,哪怕多个分区存放在一个文件组中。

删除语法:

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

只有没有分区表,或索引使用该分区方案是,才能对其删除。

3.分区表

创建语法:

--创建分区表语法create table <表名> (  <列定义>)on<分区方案名>(分区列名)
--创建分区表create table BigOrder (
   OrderId              int                  identity,
   orderNum             varchar(30)          not null,
   OrderStatus          int                  not null default 0,
   OrderPayStatus       int                  not null default 0,
   UserId               varchar(40)          not null,
   CreateDate           datetime             null default getdate(),
   Mark                 nvarchar(300)        null)on bgPartitionSchema(OrderId)

如果在表中创建主键或唯一索引,则分区依据列必须为该列。

4.分区索引

创建语法:  

--创建分区索引语法create <索引分类> index <索引名称> on <表名>(列名)on <分区方案名>(分区依据列名)
--创建分区索引CREATE CLUSTERED INDEX [ClusteredIndex_on_bgPartitionSchema_635342971076448165] ON [dbo].[BigOrder] (    [OrderId])WITH (SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF) ON [bgPartitionSchema]([OrderId])

使用分区索引查询,可以避免多个cpu操作多个磁盘时产生的冲突。

分区表明细信息

这里的语法,我就不写啦,自己看语句分析吧。简单的很。。

1.查看分区依据列的指定值所在的分区  

--查询分区依据列为10000014的数据在哪个分区上select $partition.bgPartitionFun(2000000)  --返回值是2,表示此值存在第2个分区

2.查看分区表中,每个非空分区存在的行数

--查看分区表中,每个非空分区存在的行数select $partition.bgPartitionFun(orderid) as partitionNum,count(*) as recordCountfrom bigordergroup by  $partition.bgPartitionFun(orderid)

3.查看指定分区中的数据记录  

---查看指定分区中的数据记录select * from bigorder where $partition.bgPartitionFun(orderid)=2

结果:数据从1000001开始到200W结束

分区的拆分与合并以及数据移动

 1.拆分分区

在分区函数中新增一个边界值,即可将一个分区变为2个。

--分区拆分alter partition function bgPartitionFun()
split range(N'1500000')  --将第二个分区拆为2个分区

注意:如果分区函数已经指定了分区方案,则分区数需要和分区方案中指定的文件组个数保持对应一致。

 2.合并分区

 与拆分分区相反,去除一个边界值即可。

--合并分区alter partition function bgPartitionFun()
merge range(N'1500000')  --将第二第三分区合并

3.分区中的数据移动

 你或许会遇到这样的需求,将普通表数据复制到分区表中,或者将分区表中的数据复制到普通表中。

 那么移动数据这两个表,则必须满足下面的要求。

  • 字段数量相同,对应位置的字段相同
  • 相同位置的字段要有相同的属性,相同的类型。
  • 两个表在一个文件组中

1.创建表时指定文件组

--创建表create table <表名> (  <列定义>)on <文件组名>

2.从分区表中复制数据到普通表

--将bigorder分区表中的第一分区数据复制到普通表中alter table bigorder switch partition 1 to <普通表名>

3.从普通标中复制数据到分区表中

这里要注意的是要先将分区表中的索引删除,即便普通表中存在跟分区表中相同的索引。

--将普通表中的数据复制到bigorder分区表中的第一分区alter table <普通表名> switch to bigorder partition 1

分区视图

分区视图是先建立带有字段约束的相同表,而约束不同,例如,第一个表的id约束为0--100W,第二表为101万到200万.....依次类推。

创建完一系列的表之后,用union all 连接起来创建一个视图,这个视图就形成啦分区视同。

很简单的,这里我主要是说分区表,就不说分区视图啦。。

 查看数据库分区信息

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 RowsFROM sys.partitions                  AS pJOIN sys.indexes                     AS i      ON i.object_id = p.object_id
      AND i.index_id = p.index_idJOIN sys.data_spaces                 AS ds      ON ds.data_space_id = i.data_space_idJOIN sys.partition_schemes           AS ps      ON ps.data_space_id = ds.data_space_idJOIN sys.partition_functions         AS pf      ON pf.function_id = ps.function_idJOIN sys.destination_data_spaces     AS dds2      ON dds2.partition_scheme_id = ps.data_space_id 
      AND dds2.destination_id = p.partition_numberJOIN sys.filegroups                  AS fg      ON fg.data_space_id = dds2.data_space_idLEFT JOIN sys.partition_range_values AS prv_left      ON ps.function_id = prv_left.function_id      AND prv_left.boundary_id = p.partition_number - 1LEFT 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') = 0UNION ALLSELECT
      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 RowsFROM sys.partitions     AS pJOIN sys.indexes        AS i      ON i.object_id = p.object_id
      AND i.index_id = p.index_idJOIN sys.data_spaces    AS ds      ON ds.data_space_id = i.data_space_idJOIN sys.filegroups           AS fg      ON fg.data_space_id = i.data_space_idWHERE
      OBJECTPROPERTY(p.object_id, 'ISMSShipped') = 0ORDER BY
      ObjectName,
      IndexID,
      PartitionNumber

 


SQL Server 2008将普通表转换成分区表

  (2017-07-26 10:35:48)
标签:  

it

 

sqlserver

 

分区表

分类:   学习资料
最近项目中,某个表数据量爆发时增长,单表已700w+,读写性能急剧下降,所以考虑加入分区表以解燃眉之急,后续还是要分表分库,当然这是后话。下面简要说一下将普通表转为分区表的步骤。
一、创建文件组
打开SQL Server Management Studio,在相关数据库项右键属性,进入数据库属性页,选择文件组选项 SQL <111111111111111wbr>Server <111111111111111wbr>2008将普通表转换成分区表 ,添加所要的文件组  SQL <111111111111111wbr>Server <111111111111111wbr>2008将普通表转换成分区表
二、创建文件
数据库属性页,选择文件选项 SQL <111111111111111wbr>Server <111111111111111wbr>2008将普通表转换成分区表 ,添加文件  。
添加文件时,需要选择上一步添加的对应文件组
三、删除普通表中的聚集索引,因为分区表 是以某个字段为分区条件,所以,除了这个字段以外不能再存在其他聚集索引的。要想将普通表转换成分区表,就必须要先删除原表中聚集索引,然后再创建一个新的聚集索引,以此聚集索引中创建分区方案
  1. --删掉主键  
  2. ALTER TABLE testTab DROP constraint PK_testTab  
  3. --创建主键,但不设为聚集索引  
  4. ALTER TABLE  testTab  ADD CONSTRAINT  PK_testTab   PRIMARY KEY NONCLUSTERED  
  5. (  
  6.     [ID] ASC  
  7. ) ON [PRIMARY]  
四、创建一个分区函数
  1. --创建一个分区函数
  2. CREATE PARTITION FUNCTION part_month_func_range_test(datetime)  
  3. AS RANGE RIGHT FOR VALUES (
  4. '2017-7-1 00:00:00' ,
  5. ' 2017-8-1 00:00:00 ' ,
  6. ' 2017-8-1 00:00:00 ' ,
  7. ' 2017-9-1 00:00:00 ',
  8. ' 2017-10-1 00:00:00 ',
  9. ' 2017-11-1 00:00:00 ',
  10. )  

五、创建一个分区方案

  1. CREATE PARTITION SCHEME partschSale  
  2. AS PARTITION part_month_func_range_test
  3. TO (  
  4.   FC201706,  
  5.   FC201707,  
  6.   FC201708, 
  7.   FC201709,  
  8.   FC201710,
  9.   FC201711,
  10.   FC201712
  11. )  
注意: 方案中文件组比函数中要多一个

六、按分区方案创建聚集索引
  1. --创建一个新的聚集索引,在该聚集索引中使用分区方案  
  2. CREATE CLUSTERED INDEX CT_ testTab  ON Sale([inDate])  
  3. ON  part_month_func_range_test ([ inDate ])  
为表创建了一个使用分区方案的聚集索引之后,该表就变成了一个分区表了。



sql server 分区表之查看分区表的相关数据


在向分区表中插入数据方法和在普遍表中插入数据的方法是完全相同的,对于程序员而言,

不需要去理会这13条记录研究放在哪个数据表中。当然,在查询数据时,也可以不用理会数

据到底是存放在哪个物理上的数据表中。如使用以下SQL语句进行查询:



select * from Sale  

 

    查询的结果如下图所示:



    从上面两个步骤中,根本就感觉不到数据是分别存放在几个不同的物理表中,因为在逻辑上,这

些数据都属于同一个数据表。如果你非想知道哪条记录是放在哪个物理上的分区表中,那么就必须

使用到$PARTITION函数,这个函数的可以调用分区函数,并返回数据所在物理分区的编号。


    说起来有点难懂,不过用起来很简单。$PARTITION的语法是:


    $PARTITION.分区函数名(表达式)


    假设,你想知道2010年10月1日的数据会放在哪个物理分区表中,你就可以使用以下语句来查看。



select $PARTITION.partfunSale ('2010-10-1')  

 

    在以上语句中,partfunSale()为分区函数名,括号中的表达式必须是日期型的数据或可以隐式转换成

日期型的数据,如果要问我为什么, 那么就回想一个怎么定义分区函数的吧

(CREATE PARTITION FUNCTION partfunSale (datetime))。

在定义partfunSale()函数时,指定了参数为日期型,所以括号中的表达式必须是日期型或可

以隐式转换成日期型的数据。以上代码的运行结果如下图所示:



    在该图中可以看出,分区函数返回的结果为2,也就是说,2010年10月1日的数据会放在第2个物理分区表中。


 


    再进一步考虑,如果想具体知道每个物理分区表中存放了哪些记录,也可以使用$PARTITION函数。

因为$PARTITION函数可以得到物理分区表的编号,那么只要将$PARTITION.partfunSale(SaleTime)做

为where的条件使用即可,如以下代码 所示:



select * from Sale where $PARTITION.partfunSale(SaleTime)=1  

select * from Sale where $PARTITION.partfunSale(SaleTime)=2  

select * from Sale where $PARTITION.partfunSale(SaleTime)=3  

select * from Sale where $PARTITION.partfunSale(SaleTime)=4  

select * from Sale where $PARTITION.partfunSale(SaleTime)=5  

 

    以上代码的运行结果如下图所示:



    从上图中我们可以看到每个分区表中的数据记录情况——和我们插入时设置的情况完全一致。同理可得

,如果要统计每个物理分区表中的记录数,可以使用如下代码:


select $PARTITION.partfunSale(SaleTime) as 分区编号,count(id) as

 记录数 from Sale group by $PARTITION.partfunSale(SaleTime)  

 

    以上代码的运行结果如下图所示:



 


    除了在插入数据时程序员不需要去考虑分区表的物理情况之外,就是连修改数据也不需要考虑。

SQL Server会自动将记录从一个分区表移到另一个分区表中,如以下代码所示:



--统计所有分区表中的记录总数  

select $PARTITION.partfunSale(SaleTime) as 分区编号,count(id) as 记录数 from

 Sale group by $PARTITION.partfunSale(SaleTime)  

--修改编号为1的记录,将时间改为2019年1月1日  

update Sale set SaleTime='2019-1-1' where id=1  

--重新统计所有分区表中的记录总数  

select $PARTITION.partfunSale(SaleTime) as 分区编号,count(id) as 记录数 

from Sale group by $PARTITION.partfunSale(SaleTime)  

 

    在以上代码中,程序员将其中一条数据的时间改变了,从分区函数中可以得知,

这条记录应该从第一个分区表移到第五个分区表中,如下图所示。而整个操作过程,

程序员是完全不需要干预的。





About Me

........................................................................................................................

● 本文作者:小麦苗,部分内容整理自网络,若有侵权请联系小麦苗删除

● 本文在itpub( http://blog.itpub.net/26736162 )、博客园( http://www.cnblogs.com/lhrbest )和个人weixin公众号( xiaomaimiaolhr )上有同步更新

● 本文itpub地址: http://blog.itpub.net/26736162

● 本文博客园地址: http://www.cnblogs.com/lhrbest

● 本文pdf版、个人简介及小麦苗云盘地址: http://blog.itpub.net/26736162/viewspace-1624453/

● 数据库笔试面试题库及解答: http://blog.itpub.net/26736162/viewspace-2134706/

● DBA宝典今日头条号地址: http://www.toutiao.com/c/user/6401772890/#mid=1564638659405826

........................................................................................................................

● QQ群号: 230161599 (满) 、618766405

● weixin群:可加我weixin,我拉大家进群,非诚勿扰

● 联系我请加QQ好友 646634621 ,注明添加缘由

● 于 2019-04-01 06:00 ~ 2019-04-30 24:00 在魔都完成

● 最新修改时间:2019-04-01 06:00 ~ 2019-04-30 24:00

● 文章内容来源于小麦苗的学习笔记,部分整理自网络,若有侵权或不当之处还请谅解

● 版权所有,欢迎分享本文,转载请保留出处

........................................................................................................................

小麦苗的微店 https://weidian.com/s/793741433?wfr=c&ifr=shopdetail

小麦苗出版的数据库类丛书 http://blog.itpub.net/26736162/viewspace-2142121/

小麦苗OCP、OCM、高可用网络班 http://blog.itpub.net/26736162/viewspace-2148098/

小麦苗腾讯课堂主页 https://lhr.ke.qq.com/

........................................................................................................................

使用 weixin客户端 扫描下面的二维码来关注小麦苗的weixin公众号( xiaomaimiaolhr )及QQ群(DBA宝典)、添加小麦苗weixin, 学习最实用的数据库技术。

........................................................................................................................

欢迎与我联系

 

 



来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/26736162/viewspace-2641649/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
QQ:646634621| 网名:小麦苗| 微信公众号:xiaomaimiaolhr| 11g OCM| QQ群:618766405 微信群:私聊| 《数据库笔试面试宝典》作者| OCP、OCM、高可用(RAC+DG+OGG)网络班开讲啦,有需要的小伙伴可以私聊我。

注册时间:2012-09-23

  • 博文量
    1235
  • 访问量
    7363491