ALTER TABLE… LA COMMUTATION d'une table régulière vers une table partitionnée échoue

9

Le code ci-dessous fait ce qui suit:

  1. Crée une base de données play_partition dans C: \ TEMP
  2. Crée deux tables partitionnées identiques play_table et archive_play_table
  3. Bascule la partition 1 de play_table sur la partition 1 de archive_play_table
  4. Crée une nouvelle table temp_table non partitionnée avec la même structure que play_table sur le même groupe de fichiers que la partition play_table 2
  5. Bascule play_table_partition 2 sur temp_table
  6. Tente de basculer temp_table vers la partition 2 de play_table et échoue avec

    Échec de l'instruction ALTER TABLE SWITCH Msg 4982, niveau 16, état 1, ligne 64. Vérifiez les contraintes de la table source 'play_partition.dbo.temp_table' autorisez les valeurs qui ne sont pas autorisées par la plage définie par la partition 2 sur la table cible 'play_partition.dbo.play_table'.

Pourquoi ça échoue?

J'utilise SQL Server 2014 (version d'essai Enterprise Edition).

Cordialement,

Colin Daley

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;
Colin Daley
la source
+1 sur votre question. Vous avez facilité la reproduction et la réponse, grâce au DDL que vous avez mis ici. Pour cela, merci. J'aimerais pouvoir +10 questions comme celle-ci.
Thomas Stringer
Merci. Cette erreur a besoin d'un meilleur message. Quand il a mentionné des contraintes de vérification sur la table (quand il n'y avait pas de contrainte de vérification), il ne m'est pas venu à l'esprit que l'absence d'une contrainte de vérification était, en fait, le problème.
Colin Daley

Réponses:

11

Lorsque vous travaillez avec le changement de partition, SQL Server devra vérifier que les limites de la table / partition source peuvent tenir dans les limites de la table / partition de destination. En d' autres termes, vous essayez de données de commutation de dbo.temp_tablela dbo.play_tables » partition 2. Pensez-y comme celui - ci, les données de l' c1en dbo.temp_tableest contraint que par le type de données ( int), de sorte que vous pouvez avoir des valeurs allant de -2147483648 à 2147483647 . Mais à l'inverse, votre destination ( dbo.play_tablepartition 2) a une plage de 4 à 2 147 483 647.

Vos données ne violent pas cela, mais ce sont les métadonnées qui ne peuvent pas le permettre. Vous pouvez tout aussi facilement insérer la valeur -10 dans dbo.temp_table. La commutation de partition échouerait de la même manière et aurait plus de sens, car -10 ne correspond pas aux dbo.play_tablelimites de la 2ème partition de.

Si vous vouliez faire fonctionner ce code, vous auriez besoin de dire explicitement à SQL Server qu'il dbo.temp_tablen'aura jamais de données qui ne rentreront pas dans dbo.play_tablela 2ème partition. Vous pouvez le faire avec une contrainte de vérification:

/******************************************************************************
    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
******************************************************************************/

Cet exemple d'ajout ci-dessus à votre code en fait une solution de travail. Maintenant, SQL Server sait que les données dbo.temp_tablepeuvent tenir dans la partition 2 en dbo.play_tableraison de la contrainte de vérification ajoutée à dbo.temp_table.

Thomas Stringer
la source