是否可以通过更新分区键在分区之间移动行?


17

我认为这将是一个相当简单的问题,但实际上,我很难找到答案。

问题:您是否可以通过简单地更新分区列使其越过分区边界,将分区表中的数据行从一个分区移至另一个分区?

例如,如果我有一个带有分区键的表:

CREATE TABLE SampleTable
(
    SampleID INT PRIMARY KEY,
    SampleResults VARCHAR(100) NOT NULL,
)

使用映射到主键的分区功能:

CREATE PARTITION FUNCTION MyPartitionFunc (INT) AS
RANGE LEFT FOR VALUES (10000, 20000);

是否可以通过将SampleID从1更改为(例如)500,000,将行从第一分区移动到第三分区?

注意:我将其标记为sql server 2005和2008,因为它们都支持分区。他们有不同的处理方式吗?

Answers:


14

我没有要测试的2005服务器。但是,2008年似乎可以按预期进行处理:

USE [Test]
GO
CREATE TABLE [IDRanges](
    [ID] [int] NOT NULL
)
GO

CREATE PARTITION FUNCTION IDRange1 (int)
AS RANGE LEFT FOR VALUES (10) ;
GO
--Add one record to each partition
INSERT INTO IDRanges ([ID]) VALUES (17)
INSERT INTO IDRanges ([ID]) VALUES (7)
GO
--Verify records in partition
SELECT $PARTITION.IDRange1([ID]) AS Partition, COUNT(*) AS [COUNT] 
FROM IDRanges
GROUP BY $PARTITION.IDRange1([ID]) 
ORDER BY Partition ;
GO
--Move row between partitions
UPDATE IDRanges
SET [ID] = 8 WHERE [ID] = 17
GO
--Verify records in partition
SELECT $PARTITION.IDRange1([ID]) AS Partition, COUNT(*) AS [COUNT] 
FROM IDRanges
GROUP BY $PARTITION.IDRange1([ID]) 
ORDER BY Partition ;

您应该在更新之前在每个分区中看到一个记录,然后在第一个分区中看到两个记录。


1
这是一个很好的答案!
玛丽安

这会执行你在SQL Server 2005中描述以及
奔Brocka

-1这不会测试方案。$PARTITION仅根据输入计算分区号;它实际上并没有测试该行的物理位置。
乔恩·塞格尔

9

为了测试这一点,实验实际上需要对表进行分区。参见http://www.kodyaz.com/articles/how-to-partition-table-non-partitioned-table-sql-server-2008.aspx

查询分区功能只是告诉您分区功能的含义。它没有说数据存储在哪里。您可以设置分区功能并运行它,而无需实际对表进行分区,如此处已演示的那样。

为了对表进行分区,还必须创建文件组和使用分区功能将功能结果分配给文件组的分区方案。然后,您必须在使用该分区方案的表上放置一个集群键。

设置分区

我不是命令行SQL方面的专家。我使用SSMS界面来设置文件组pfg1(带有pf1文件)和pfg2(带有pf2文件)。然后,我声明了分区功能和方案:

CREATE PARTITION FUNCTION IDRange1 (int)
AS RANGE LEFT FOR VALUES (10) ;
GO

CREATE PARTITION SCHEME ps_IDRange1
AS PARTITION IDRange1
TO (pfg1, pfg2)
GO

创建表和聚簇索引

CREATE TABLE [IDRanges](
    [ID] [int] NOT NULL
)
GO

CREATE CLUSTERED INDEX PK_IDRanges
ON dbo.IDRanges(id) ON ps_IDRange1 (ID)
GO

完成此操作后,当您查询sys.partitions(我有2005)时,您会看到该表现在具有两个分区,而不只是该表的一个分区。这表明我们已经为该表完全实现了分区。

select * from sys.partitions where object_id = object_id('IDRanges')
partition_id object_id index_id partition_number hobt_id行
-------------------- ----------- ----------- -------- -------- -------------------- --------------------
72057597780295680 770674389 1 1 72057597780295680 0
72057597780361216 770674389 1 2 72057597780361216 0

现在我们有了两个分区(每个分区都有一个行数),我们可以进行一个实验。

插入行

INSERT INTO IDRanges ([ID]) VALUES (17)
INSERT INTO IDRanges ([ID]) VALUES (7)

检查sys.partitions以查看发生了什么。

select * from sys.partitions where object_id = object_id('IDRanges')
partition_id object_id index_id partition_number hobt_id行
-------------------- ----------- ----------- -------- -------- -------------------- --------------------
72057597780295680 770674389 1 1 72057597780295680 1
72057597780361216 770674389 1 2 72057597780361216 1

是的 每个分区一行。

移动一行。

UPDATE IDRanges
SET [ID] = 8 WHERE [ID] = 17

检查分区

select * from sys.partitions where object_id = object_id('IDRanges')
partition_id object_id index_id partition_number hobt_id行
-------------------- ----------- ----------- -------- -------- -------------------- --------------------
72057597780295680 770674389 1 1 72057597780295680 2
72057597780361216 770674389 1 2 72057597780361216 0

现在,第一个分区具有两行而不是1行,第二个分区具有零行而不是2行。

我认为这可以确认该行是由于修改分区表中的集群键而自动移动的。


1
对该实际测试场景的问题的第一个答案+1。欢迎使用DBA.SE!
乔恩·塞格尔

-1能否请您指出支持您对表进行“完全”分区的MSDN文档?具体来说,是否需要单独的文件组和聚簇索引?
肯尼思

-2

我认为答案不正确。使用值时

 $PARTITION.IDRange1([ID]) AS Partition

您只是在重新计算分区应该是什么,而不是重新计算记录的当前位置。

您应该使用:

select * from sys.partitions where object_id = object_id('IDRanges')

在我对sql 2005的测试中,值更改,但记录保留在同一分区中。这可能会使统计信息和优化器混乱,因为它将以多线程模式运行,并期望分区在特定范围内。当它尝试使用分区消除功能仅查询相关分区时,也将完全错误。我认为您需要删除并重新插入每条记录才能使它们移动。


2
$partition 在此处搜索表明接受的答案是正确的。更新记录后,如何确认记录保留在同一分区中?
Nick Chammas

第一点是正确的,但是关于行不移动的结论是错误的-运行的测试可能存在问题。
乔恩·塞格尔
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.