ALTER TABLE ... 从常规表切换到分区表失败

Col*_*ley 9 sql-server partitioning sql-server-2014

下面的代码执行以下操作:

  1. 在 C:\TEMP 中创建数据库 play_partition
  2. 创建两个相同的分区表 play_table 和 archive_play_table
  3. 将 play_table 分区 1 切换到 archive_play_table 分区 1
  4. 在与 play_table 分区 2 相同的文件组上创建一个与 play_table 具有相同结构的新未分区表 temp_table
  5. 将 play_table_partition 2 切换到 temp_table
  6. 尝试将 temp_table 切换回 play_table 分区 2 并失败

    消息 4982,级别 16,状态 1,第 64 行 ALTER TABLE SWITCH 语句失败。检查源表“play_partition.dbo.temp_table”的约束允许目标表“play_partition.dbo.play_table”上的分区 2 定义的范围不允许的值。

为什么会失败?

我使用的是 SQL Server 2014(企业版试用版)。

问候,

科林戴利

http://www.colindaley.com/translator

/* Playing with partitioned tables */

USE master;
GO

DROP DATABASE play_partition;
GO

CREATE DATABASE play_partition
    ON PRIMARY(
        NAME = play_partition
        , FILENAME = 'C:\TEMP\play_partition.mdf')
    ,FILEGROUP play_fg1(
        NAME = play_fg1
        ,FILENAME = 'C:\TEMP\play_fg1f1.ndf')
    ,FILEGROUP play_fg2(
        NAME = play_fg2f1
        ,FILENAME = 'C:\TEMP\play_fg2f1.ndf');
GO

USE play_partition;


CREATE PARTITION FUNCTION play_range(INT)
    AS RANGE LEFT FOR VALUES(3);

-- Partition scheme
CREATE PARTITION SCHEME play_scheme 
    AS PARTITION play_range TO (play_fg1, play_fg2);

-- Partitioned tables
CREATE TABLE dbo.play_table(
    c1 INT NOT NULL CONSTRAINT PK_play_table_c1 PRIMARY KEY CLUSTERED
)
    ON play_scheme(c1);

CREATE TABLE dbo.archive_play_table(
c1 INT NOT NULL CONSTRAINT PK_archive_play_table_c1 PRIMARY KEY CLUSTERED
)
    ON play_scheme(c1);

-- partition 1 = {1, 2, 3}, partiion 2 = {4, 5, 6}
INSERT INTO dbo.play_table(c1) VALUES (1), (2),  (3), (4), (5), (6);

-- move partition 1 from play_table to archive play_table
ALTER TABLE dbo.play_table
    SWITCH PARTITION 1 to dbo.archive_play_table PARTITION 1;

-- create empty table with same structure as dbo.play_table
SELECT * INTO dbo.temp_table FROM dbo.play_table WHERE 1 = 0;

-- move temp_table to filegroup play_fg2
ALTER TABLE dbo.temp_table
    ADD CONSTRAINT PK_temp_table_c1 PRIMARY KEY CLUSTERED(c1) ON play_fg2;

-- move contents of play_table to temp_table, which is not partitioned
-- but is in the same filegroup
ALTER TABLE dbo.play_table
    SWITCH PARTITION 2 TO temp_table;
PRINT 'Switched from partitioned table to non-partitioned table';

-- move data back to partitioned play_table from unpartitioned temp_table
-- FAIL
ALTER TABLE dbo.temp_table
    SWITCH TO play_table partition 2;
PRINT 'Switched from non-partitioned table to partitioned table';


SELECT 'archive_play_table' as table_name, t1.c1
    FROM dbo.archive_play_table AS t1
    UNION ALL
    SELECT 'temp_table' AS table_name, t1.c1
        FROM dbo.temp_table as t1
    ORDER BY 1, 2;
Run Code Online (Sandbox Code Playgroud)

Tho*_*ger 11

当您使用分区切换时,SQL Server 需要验证源表/分区边界是否适合目标表/分区边界。换句话说,您正在尝试将数据从dbo.temp_tabletodbo.play_table的分区 2切换。可以这样想,c1indbo.temp_table的数据仅受数据类型 ( int) 的约束,因此您可以拥有从 -2,147,483,648 到 2,147,483,647 的值. 但相反,您的目的地(dbo.play_table分区 2)的范围从 4 到 2,147,483,647。

您的数据没有违反这一点,但元数据不允许这样做。您可以轻松地将值 -10 插入到dbo.temp_table. 分区切换会以同样的方式失败并且更有意义,因为 -10 不适合dbo.play_table的第二个分区边界。

如果你想让这段代码工作,你需要明确地告诉 SQL Serverdbo.temp_table永远不会有任何不适合dbo.play_table的第二个分区的数据。您可以使用检查约束来做到这一点:

/******************************************************************************
    your code omitted for brevity
******************************************************************************/

-- move contents of play_table to temp_table, which is not partitioned
-- but is in the same filegroup
ALTER TABLE dbo.play_table
    SWITCH PARTITION 2 TO temp_table;
PRINT 'Switched from partitioned table to non-partitioned table';

/******************************************************************************
    added check constraint so that data can fit in the destination partition
******************************************************************************/
alter table dbo.temp_table
add constraint CK_TempTable_C1 check (c1 >= 4);
go
/******************************************************************************
    end of added code
******************************************************************************/

-- move data back to partitioned play_table from unpartitioned temp_table
-- this will no longer FAIL
ALTER TABLE dbo.temp_table
    SWITCH TO play_table partition 2;
PRINT 'Switched from non-partitioned table to partitioned table';

/******************************************************************************
    your code omitted for brevity
******************************************************************************/
Run Code Online (Sandbox Code Playgroud)

上述示例添加到您的代码使其成为一个有效的解决方案。现在的SQL Server知道,在数据dbo.temp_table能够适应分区2的dbo.play_table,因为增加的检查约束的dbo.temp_table